From 8ef68a9422d6a55779af3d9d98f0367c736a4c1a Mon Sep 17 00:00:00 2001 From: Maubry94 Date: Thu, 2 Apr 2026 13:42:40 +0200 Subject: [PATCH 01/44] feat: start form design --- dev/vue/TheApp.vue | 18 +++--- dev/vue/components/TheTextInput.vue | 51 ++++++++++++++++- scripts/vue/designSystem/RepeatAddButton.vue | 53 ++++++++++++++++++ .../vue/designSystem/RepeatRemoveButton.vue | 55 ++++++++++++++++++- .../vue/designSystem/RepeatResetButton.vue | 54 +++++++++++++++++- scripts/vue/designSystem/UnionSelectKind.vue | 47 ++++++++++++++++ scripts/vue/designSystem/index.ts | 2 + scripts/vue/designSystem/styles.scss | 25 +++++++++ .../vue/templates/grid/GridRepeatTemplate.vue | 28 ++++++---- .../vue/templates/grid/GridUnionTemplate.vue | 14 +++-- scripts/vue/templates/grid/styles.scss | 1 + .../templates/grid/GridRepeatTemplate.test.ts | 4 +- vitest.config.js | 11 +++- 13 files changed, 327 insertions(+), 36 deletions(-) create mode 100644 scripts/vue/designSystem/styles.scss diff --git a/dev/vue/TheApp.vue b/dev/vue/TheApp.vue index 53bd224..ab1e060 100644 --- a/dev/vue/TheApp.vue +++ b/dev/vue/TheApp.vue @@ -8,8 +8,8 @@ import { DPE } from "@duplojs/utils"; const useInput = createInput( TheTextInput, { - defaultValue: () => "test", - props: { test: "" }, + defaultValue: () => "Default value", + props: { text: "" }, }, ); @@ -35,18 +35,18 @@ const { component: Form, currentValue, check } = useForm( "one", useMultiLayout({ name: useRepeatLayout( - useInput({ defaultValue: "tt" }), + useInput({ defaultValue: "Default value" }), { max: 10, min: 2, }, ), age: useCheckLayout( - useInput({ defaultValue: "198" }), + useInput({ defaultValue: "26" }), { dataParser: DPE.coerce.number(), template: useGridCheckTemplate({ - label: "test", + label: "Check age", columns: 2, hideEmptyMessageError: true, }), @@ -56,7 +56,7 @@ const { component: Form, currentValue, check } = useForm( ], [ "two", - useInput({ defaultValue: "ooo" }), + useInput({ defaultValue: "Another default value" }), ], ], { defaultKind: "one" }, @@ -73,10 +73,8 @@ const { component: Form, currentValue, check } = useForm( diff --git a/scripts/vue/templates/grid/GridUnionTemplate.vue b/scripts/vue/templates/grid/GridUnionTemplate.vue index 6a40088..fe06630 100644 --- a/scripts/vue/templates/grid/GridUnionTemplate.vue +++ b/scripts/vue/templates/grid/GridUnionTemplate.vue @@ -68,12 +68,14 @@ const selfStyles = computed(() => ({ class="DFV-grid-element" :style="selfStyles" > - +
+ +
{ expect(rootContainer.attributes("style")).toContain("--gap: 14px"); expect(repeatTemplate.findAll(".DFV-grid-repeat-element")).toHaveLength(1); expect(repeatTemplate.findAll("button[type=\"button\"]")).toHaveLength(3); - expect(repeatTemplate.text()).toContain("reset"); - expect(repeatTemplate.text()).toContain("x"); + expect(repeatTemplate.text()).toContain("Reset"); + expect(repeatTemplate.text()).toContain("×"); expect(repeatTemplate.text()).toContain("+"); await repeatTemplate.findAll("button[type=\"button\"]").at(-1)!.trigger("click"); diff --git a/vitest.config.js b/vitest.config.js index cb75451..bd86b61 100644 --- a/vitest.config.js +++ b/vitest.config.js @@ -1,6 +1,6 @@ import { defineConfig } from "vitest/config"; -import tsconfigPaths from 'vite-tsconfig-paths' import vue from "@vitejs/plugin-vue"; +import tsconfigPaths from 'vite-tsconfig-paths' import { Path } from "@duplojs/utils"; export default defineConfig({ @@ -21,7 +21,14 @@ export default defineConfig({ provider: "istanbul", reporter: ["text", "json", "html", "json-summary"], reportsDirectory: "coverage", - include: ["scripts"], + include: [ + "scripts/vue/form.ts", + "scripts/vue/formField.ts", + "scripts/vue/input.ts", + "scripts/vue/kind.ts", + "scripts/vue/template.ts", + "scripts/vue/layouts/**/*.ts", + ], exclude: [ "**/*.test.ts", "bin", From 4cc8318cb7cef30608f0ea93d0df47ca04a94fd3 Mon Sep 17 00:00:00 2001 From: Maubry94 Date: Tue, 7 Apr 2026 16:06:52 +0200 Subject: [PATCH 02/44] feat: add style to step form --- docs/examples/v0/vue/index.ts | 4 +- scripts/vue/designSystem/RepeatAddButton.vue | 3 +- .../vue/designSystem/RepeatRemoveButton.vue | 3 +- .../vue/designSystem/RepeatResetButton.vue | 3 +- scripts/vue/designSystem/StepNextButton.vue | 52 +++++++++++- .../vue/designSystem/StepPreviousButton.vue | 53 +++++++++++- scripts/vue/designSystem/StepResetButton.vue | 53 +++++++++++- scripts/vue/designSystem/index.ts | 3 + .../vue/templates/grid/GridRepeatTemplate.vue | 4 +- .../templates/grid/GridStepByStepTemplate.vue | 66 +++++++++++---- scripts/vue/templates/grid/styles.scss | 83 +++++++++++++++++++ .../grid/GridStepByStepTemplate.test.ts | 15 ++-- 12 files changed, 305 insertions(+), 37 deletions(-) diff --git a/docs/examples/v0/vue/index.ts b/docs/examples/v0/vue/index.ts index 0202063..2c93abe 100644 --- a/docs/examples/v0/vue/index.ts +++ b/docs/examples/v0/vue/index.ts @@ -4,13 +4,13 @@ import "@duplojs/form/vue/grid.css"; const useForm = createForm(defaultGridTemplates); -function useMyCustomForm() { +function useMyCustomForm(formField: Parameters[0]) { const { component, check, currentValue, reset, - } = useForm(); + } = useForm(formField); return { TheForm: component, diff --git a/scripts/vue/designSystem/RepeatAddButton.vue b/scripts/vue/designSystem/RepeatAddButton.vue index ebc270b..9910420 100644 --- a/scripts/vue/designSystem/RepeatAddButton.vue +++ b/scripts/vue/designSystem/RepeatAddButton.vue @@ -11,7 +11,7 @@ defineProps(); @@ -38,7 +38,6 @@ defineProps(); color: var(--button-fg); font-size: 1rem; font-weight: 600; - line-height: 1; cursor: pointer; transition: background-color var(--duplo-transition-fast), diff --git a/scripts/vue/designSystem/RepeatRemoveButton.vue b/scripts/vue/designSystem/RepeatRemoveButton.vue index 459ed33..8ed3218 100644 --- a/scripts/vue/designSystem/RepeatRemoveButton.vue +++ b/scripts/vue/designSystem/RepeatRemoveButton.vue @@ -11,7 +11,7 @@ defineProps(); @@ -38,7 +38,6 @@ defineProps(); color: var(--button-fg); font-size: 1rem; font-weight: 600; - line-height: 1; cursor: pointer; transition: background-color var(--duplo-transition-fast), diff --git a/scripts/vue/designSystem/RepeatResetButton.vue b/scripts/vue/designSystem/RepeatResetButton.vue index b57fba1..bde4a82 100644 --- a/scripts/vue/designSystem/RepeatResetButton.vue +++ b/scripts/vue/designSystem/RepeatResetButton.vue @@ -11,7 +11,7 @@ defineProps(); @@ -37,7 +37,6 @@ defineProps(); color: var(--button-fg); font-size: 0.875rem; font-weight: 600; - line-height: 1; cursor: pointer; transition: background-color var(--duplo-transition-fast), diff --git a/scripts/vue/designSystem/StepNextButton.vue b/scripts/vue/designSystem/StepNextButton.vue index 0342609..050b0fd 100644 --- a/scripts/vue/designSystem/StepNextButton.vue +++ b/scripts/vue/designSystem/StepNextButton.vue @@ -9,8 +9,58 @@ defineProps(); + + diff --git a/scripts/vue/designSystem/StepPreviousButton.vue b/scripts/vue/designSystem/StepPreviousButton.vue index 1d05020..24fcdf7 100644 --- a/scripts/vue/designSystem/StepPreviousButton.vue +++ b/scripts/vue/designSystem/StepPreviousButton.vue @@ -9,8 +9,59 @@ defineProps(); + + diff --git a/scripts/vue/designSystem/StepResetButton.vue b/scripts/vue/designSystem/StepResetButton.vue index c64b548..9e3e14f 100644 --- a/scripts/vue/designSystem/StepResetButton.vue +++ b/scripts/vue/designSystem/StepResetButton.vue @@ -9,8 +9,59 @@ defineProps(); + + diff --git a/scripts/vue/designSystem/index.ts b/scripts/vue/designSystem/index.ts index 5c9473f..4059f88 100644 --- a/scripts/vue/designSystem/index.ts +++ b/scripts/vue/designSystem/index.ts @@ -3,4 +3,7 @@ import "./styles.scss"; export { default as RepeatAddButton } from "./RepeatAddButton.vue"; export { default as RepeatRemoveButton } from "./RepeatRemoveButton.vue"; export { default as RepeatResetButton } from "./RepeatResetButton.vue"; +export { default as StepNextButton } from "./StepNextButton.vue"; +export { default as StepPreviousButton } from "./StepPreviousButton.vue"; +export { default as StepResetButton } from "./StepResetButton.vue"; export { default as UnionSelectKind } from "./UnionSelectKind.vue"; diff --git a/scripts/vue/templates/grid/GridRepeatTemplate.vue b/scripts/vue/templates/grid/GridRepeatTemplate.vue index d8ccc92..e0987c3 100644 --- a/scripts/vue/templates/grid/GridRepeatTemplate.vue +++ b/scripts/vue/templates/grid/GridRepeatTemplate.vue @@ -63,7 +63,7 @@ const repeatElementContainerStyles = computed(() => ({ :key="index" :style="repeatElementStyles" > -
+
({
-
+
(); defineSlots(); + +const currentStep = computed(() => props.getCurrentStep() + 1); + +const progressPercent = computed(() => { + if (props.stepQuantity <= 1) { + return 100; + } + + return Math.round((currentStep.value / props.stepQuantity) * 100); +}); diff --git a/scripts/vue/templates/grid/styles.scss b/scripts/vue/templates/grid/styles.scss index f0ff78c..9310b47 100644 --- a/scripts/vue/templates/grid/styles.scss +++ b/scripts/vue/templates/grid/styles.scss @@ -25,11 +25,15 @@ &-element { grid-column: span var(--columns) / span var(--columns); + display: flex; + flex-direction: column; + gap: 0.5rem; } &-form { display: flex; flex-direction: column; + gap: 0.75rem; } &-input { @@ -37,6 +41,11 @@ flex-direction: column; gap: 0.35rem; } + + &-repeat-actions { + display: flex; + gap: 0.35rem; + } } .DFV-template_check.DFV-grid-element { @@ -52,4 +61,78 @@ min-height: 1.2em; } } +} + +.DFV-step { + &-root { + display: flex; + flex-direction: column; + gap: 0.65rem; + padding: 0.75rem; + border: 1px solid #e2e8f0; + border-radius: 0.625rem; + background: #ffffff; + } + + &-head { + display: flex; + justify-content: flex-end; + } + + &-indicator { + display: flex; + flex-direction: column; + gap: 0.35rem; + min-width: 9rem; + + &-meta { + display: flex; + justify-content: flex-end; + align-items: baseline; + gap: 0.16rem; + font-size: 0.75rem; + line-height: 1; + color: #64748b; + + strong { + font-size: 0.8rem; + font-weight: 700; + color: #334155; + } + } + + &-track { + position: relative; + overflow: hidden; + height: 0.38rem; + border-radius: 999px; + background: #e2e8f0; + } + + &-fill { + display: block; + height: 100%; + border-radius: 999px; + background: linear-gradient(90deg, #d4ac24 0%, #e1bc3f 100%); + transition: width 180ms ease; + } + } + + &-content { + align-items: start; + } + + &-error { + display: inline-block; + font-size: 0.75rem; + line-height: 1.2; + color: #b42318; + } + + &-actions { + display: flex; + justify-content: flex-end; + gap: 0.5rem; + flex-wrap: wrap; + } } \ No newline at end of file diff --git a/tests/vue/templates/grid/GridStepByStepTemplate.test.ts b/tests/vue/templates/grid/GridStepByStepTemplate.test.ts index a3b3e88..54de7e6 100644 --- a/tests/vue/templates/grid/GridStepByStepTemplate.test.ts +++ b/tests/vue/templates/grid/GridStepByStepTemplate.test.ts @@ -1,6 +1,5 @@ import { createForm, createInput, createTemplate, useStepLayout } from "@V"; -import { E } from "@duplojs/utils"; -import { sleep } from "@duplojs/utils"; +import { E, sleep } from "@duplojs/utils"; import { mount } from "@vue/test-utils"; import TextInput from "@test-utils/TextInput.vue"; import { testTemplates } from "@test-utils/templates"; @@ -31,12 +30,13 @@ describe("GridStepByStepTemplate", () => { "DFV-grid-element", ]), ); - expect(stepTemplate.get("label").text()).toBe("1/2"); + expect(stepTemplate.get(".DFV-step-indicator-meta").text()).toContain("Step 1"); + expect(stepTemplate.get(".DFV-step-indicator-meta").text()).toContain("on 2"); expect(stepTemplate.get(".DFV-grid-container").element.className).toContain("DFV-grid-container"); expect(stepTemplate.findAll("button[type=\"button\"]")).toHaveLength(3); - expect(stepTemplate.text()).toContain("previous"); - expect(stepTemplate.text()).toContain("reset"); - expect(stepTemplate.text()).toContain("next"); + expect(stepTemplate.text()).toContain("Previous"); + expect(stepTemplate.text()).toContain("Reset"); + expect(stepTemplate.text()).toContain("Next"); expect(E.isLeft(check())).toBe(true); await sleep(); @@ -45,7 +45,8 @@ describe("GridStepByStepTemplate", () => { await stepTemplate.findAll("button[type=\"button\"]")[2]!.trigger("click"); await sleep(); expect(currentValue.value.currentStep).toBe(1); - expect(stepTemplate.get("label").text()).toBe("2/2"); + expect(stepTemplate.get(".DFV-step-indicator-meta").text()).toContain("Step 2"); + expect(stepTemplate.get(".DFV-step-indicator-meta").text()).toContain("on 2"); }); it("wires previous and reset actions through the template buttons", async() => { From d40bf33625cec01b8644466bd78ae8ce7edc8ccd Mon Sep 17 00:00:00 2001 From: Maubry94 Date: Tue, 7 Apr 2026 17:52:22 +0200 Subject: [PATCH 03/44] fix: rename style vars --- scripts/vue/designSystem/RepeatAddButton.vue | 27 +++++++------ .../vue/designSystem/RepeatRemoveButton.vue | 26 ++++++------- .../vue/designSystem/RepeatResetButton.vue | 28 +++++++------- scripts/vue/designSystem/UnionSelectKind.vue | 26 ++++++------- scripts/vue/designSystem/styles.scss | 38 +++++++++---------- 5 files changed, 72 insertions(+), 73 deletions(-) diff --git a/scripts/vue/designSystem/RepeatAddButton.vue b/scripts/vue/designSystem/RepeatAddButton.vue index 9910420..250239b 100644 --- a/scripts/vue/designSystem/RepeatAddButton.vue +++ b/scripts/vue/designSystem/RepeatAddButton.vue @@ -4,12 +4,11 @@ export interface Props { } defineProps(); - diff --git a/scripts/vue/designSystem/TheCheckboxPolicy.vue b/scripts/vue/designSystem/TheCheckboxPolicy.vue new file mode 100644 index 0000000..b5c499b --- /dev/null +++ b/scripts/vue/designSystem/TheCheckboxPolicy.vue @@ -0,0 +1,78 @@ + + + + + diff --git a/scripts/vue/designSystem/TheNumberInput.vue b/scripts/vue/designSystem/TheNumberInput.vue new file mode 100644 index 0000000..6cc9349 --- /dev/null +++ b/scripts/vue/designSystem/TheNumberInput.vue @@ -0,0 +1,68 @@ + + + + + diff --git a/scripts/vue/designSystem/TheTextArea.vue b/scripts/vue/designSystem/TheTextArea.vue new file mode 100644 index 0000000..801b98d --- /dev/null +++ b/scripts/vue/designSystem/TheTextArea.vue @@ -0,0 +1,63 @@ + + +