@cahyo-dimas/freeday 1.53.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +657 -535
- package/COMPONENTS.md +153 -135
- package/README.id.md +46 -45
- package/README.md +46 -43
- package/USAGE.md +39 -35
- package/adapters/blazor/FdyAppShell.razor +50 -0
- package/adapters/blazor/FdyAppShell.razor.cs +114 -0
- package/adapters/blazor/FdyCfl.razor.cs +1 -1
- package/adapters/blazor/FdyChart.razor +1 -1
- package/adapters/blazor/FdyChart.razor.cs +3 -3
- package/adapters/blazor/FdyChartSeries.cs +1 -1
- package/adapters/blazor/FdyCombo.razor.cs +2 -2
- package/adapters/blazor/FdyModal.razor +1 -1
- package/adapters/blazor/FdyTable.razor.cs +7 -7
- package/adapters/blazor/FdyTableFilter.razor +1 -1
- package/adapters/blazor/FdyTableFooter.razor +1 -1
- package/adapters/blazor/FdyTableFooter.razor.cs +4 -4
- package/adapters/blazor/Freeday.Blazor.csproj +1 -1
- package/adapters/blazor/FreedayComponentBase.cs +1 -1
- package/adapters/blazor/TableModel.cs +3 -3
- package/adapters/blazor/TableTypes.cs +3 -3
- package/adapters/blazor/freeday-blazor.js +5 -5
- package/adapters/core/app-shell.d.ts +30 -0
- package/adapters/core/app-shell.js +134 -0
- package/adapters/core/table-model.d.ts +2 -2
- package/adapters/core/table-model.js +8 -8
- package/adapters/react/components/FdyAppShell.tsx +159 -0
- package/adapters/react/components/FdyAutocomplete.tsx +1 -1
- package/adapters/react/components/FdyCascade.tsx +1 -1
- package/adapters/react/components/FdyCfl.tsx +11 -11
- package/adapters/react/components/FdyDateRange.tsx +2 -2
- package/adapters/react/components/FdyDatepicker.tsx +5 -5
- package/adapters/react/components/FdyDrawer.tsx +1 -1
- package/adapters/react/components/FdyModal.tsx +2 -2
- package/adapters/react/components/FdyTable.tsx +7 -7
- package/adapters/react/components/FdyTableFilter.tsx +1 -1
- package/adapters/react/components/FdyTableFooter.tsx +5 -5
- package/adapters/react/index.d.ts +2 -1
- package/adapters/react/index.js +1 -0
- package/adapters/react/useFreeday.js +3 -3
- package/adapters/vue/components/FdyAppShell.vue +160 -0
- package/adapters/vue/components/FdyAutocomplete.vue +1 -1
- package/adapters/vue/components/FdyCascade.vue +1 -1
- package/adapters/vue/components/FdyCfl.vue +8 -8
- package/adapters/vue/components/FdyChart.vue +2 -2
- package/adapters/vue/components/FdyCombo.vue +1 -1
- package/adapters/vue/components/FdyDateRange.vue +2 -2
- package/adapters/vue/components/FdyDatepicker.vue +5 -5
- package/adapters/vue/components/FdyDrawer.vue +3 -3
- package/adapters/vue/components/FdyModal.vue +3 -3
- package/adapters/vue/components/FdyTable.vue +7 -7
- package/adapters/vue/components/FdyTableFilter.vue +1 -1
- package/adapters/vue/components/FdyTableFooter.vue +5 -5
- package/adapters/vue/index.d.ts +3 -2
- package/adapters/vue/index.js +1 -0
- package/adapters/vue/useFreeday.js +3 -3
- package/dist/asset.d.ts +3 -3
- package/dist/freeday-app-shell.js +49 -9
- package/dist/freeday-autocomplete.js +2 -2
- package/dist/freeday-breakpoint.js +1 -1
- package/dist/freeday-carousel.js +5 -5
- package/dist/freeday-cascade.js +7 -7
- package/dist/freeday-cfl.js +9 -9
- package/dist/freeday-chart.js +11 -11
- package/dist/freeday-chip.js +3 -3
- package/dist/freeday-datepicker.js +11 -7
- package/dist/freeday-datetime.js +5 -5
- package/dist/freeday-drawer.js +1 -1
- package/dist/freeday-form.js +14 -14
- package/dist/freeday-mask.js +6 -6
- package/dist/freeday-menu.js +1 -1
- package/dist/freeday-number.js +3 -3
- package/dist/freeday-popover.js +3 -3
- package/dist/freeday-rating.js +2 -2
- package/dist/freeday-select.js +5 -5
- package/dist/freeday-slider.js +2 -2
- package/dist/freeday-stepper.js +7 -7
- package/dist/freeday-table.js +15 -15
- package/dist/freeday-tabs.js +1 -1
- package/dist/freeday-timepicker.js +2 -2
- package/dist/freeday-toast.js +8 -8
- package/dist/freeday-tree.js +3 -3
- package/dist/freeday-upload.js +25 -25
- package/dist/freeday.bundle.css +176 -176
- package/dist/freeday.css +170 -170
- package/dist/freeday.d.ts +2 -2
- package/dist/freeday.js +202 -158
- package/dist/freeday.tokens.css +5 -5
- package/docs/agent-onboarding.md +42 -42
- package/docs/getting-started.md +35 -35
- package/docs/integrations.md +18 -18
- package/docs/reference-screen.html +14 -14
- package/package.json +5 -4
- package/src/base.css +6 -6
- package/src/components/accordion.css +4 -4
- package/src/components/alert.css +1 -1
- package/src/components/app-shell.css +10 -10
- package/src/components/appbar.css +3 -3
- package/src/components/autocomplete.css +1 -1
- package/src/components/avatar.css +3 -3
- package/src/components/badge.css +5 -5
- package/src/components/breadcrumb.css +1 -1
- package/src/components/breakpoints.css +4 -4
- package/src/components/button.css +13 -13
- package/src/components/card.css +8 -8
- package/src/components/carousel.css +2 -2
- package/src/components/cascade.css +2 -2
- package/src/components/cfl.css +4 -4
- package/src/components/chart.css +3 -3
- package/src/components/chip.css +2 -2
- package/src/components/combo.css +3 -3
- package/src/components/composition.css +7 -7
- package/src/components/datepicker.css +6 -6
- package/src/components/datetimepicker.css +1 -1
- package/src/components/description-list.css +1 -1
- package/src/components/divider.css +1 -1
- package/src/components/drawer.css +1 -1
- package/src/components/file-upload.css +5 -5
- package/src/components/filterbar.css +7 -7
- package/src/components/form-grid.css +2 -2
- package/src/components/input-group.css +6 -6
- package/src/components/input.css +6 -6
- package/src/components/kbd.css +1 -1
- package/src/components/list.css +6 -6
- package/src/components/menu.css +3 -3
- package/src/components/modal.css +1 -1
- package/src/components/pagination.css +1 -1
- package/src/components/progress.css +1 -1
- package/src/components/rating.css +2 -2
- package/src/components/selection.css +4 -4
- package/src/components/skeleton.css +1 -1
- package/src/components/slider.css +1 -1
- package/src/components/spinner.css +1 -1
- package/src/components/states.css +1 -1
- package/src/components/stepper.css +2 -2
- package/src/components/table.css +15 -15
- package/src/components/tabs.css +3 -3
- package/src/components/timeline.css +1 -1
- package/src/components/timepicker.css +1 -1
- package/src/components/toast.css +3 -3
- package/src/components/tooltip.css +1 -1
- package/src/components/tree.css +2 -2
- package/tokens/breakpoints.d.ts +2 -2
- package/tokens/breakpoints.mjs +3 -3
- package/tokens/tokens.json +1 -1
package/dist/freeday.tokens.css
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/* Freeday tokens
|
|
1
|
+
/* Freeday tokens. GENERATED by tokens/build.mjs. Do not edit by hand. */
|
|
2
2
|
:root {
|
|
3
3
|
--azure-50: #eff4ff;
|
|
4
4
|
--azure-100: #dbe6fe;
|
|
@@ -178,7 +178,7 @@
|
|
|
178
178
|
--focus-ring-width: 2px;
|
|
179
179
|
}
|
|
180
180
|
/* The SYSTEM default stays root-scoped: it is a statement about the document, and dropping :root
|
|
181
|
-
* here would match every element that is not itself [data-theme="light"]
|
|
181
|
+
* here would match every element that is not itself [data-theme="light"], which would re-darken
|
|
182
182
|
* the children of a light panel, since they carry no attribute of their own. */
|
|
183
183
|
@media (prefers-color-scheme: dark) {
|
|
184
184
|
:root:not([data-theme="light"]) {
|
|
@@ -240,7 +240,7 @@
|
|
|
240
240
|
}
|
|
241
241
|
/* The two EXPLICIT opt-ins are deliberately NOT scoped to :root, for the same reason as density
|
|
242
242
|
* below: these are inheriting custom properties, so data-theme on any ancestor re-themes just that
|
|
243
|
-
* subtree
|
|
243
|
+
* subtree, a dark brand panel beside a light form is an ordinary layout, and it should not require
|
|
244
244
|
* re-colouring each component by hand. The root still matches, so data-theme on <html> is unchanged.
|
|
245
245
|
* Both keep the same specificity (0,1,0) as :root and come after it, so they still win there; and
|
|
246
246
|
* --light after --dark means a light island inside a dark region wins in turn. */
|
|
@@ -356,7 +356,7 @@
|
|
|
356
356
|
--chart-tick: var(--slate-400);
|
|
357
357
|
--focus-ring: var(--azure-600);
|
|
358
358
|
}
|
|
359
|
-
/* Density is deliberately NOT scoped to :root. These are custom properties, so they inherit
|
|
359
|
+
/* Density is deliberately NOT scoped to :root. These are custom properties, so they inherit,
|
|
360
360
|
* putting the attribute on any ancestor (a route wrapper, one section) densifies just that
|
|
361
361
|
* subtree, which is how density is actually decided: per screen, not per app. The root still
|
|
362
362
|
* matches, so setting data-density on the html element keeps working exactly as before. */
|
|
@@ -368,7 +368,7 @@
|
|
|
368
368
|
--control-h: 2rem;
|
|
369
369
|
}
|
|
370
370
|
/* ...and the way back out. Inheritance only runs downhill: once the root is compact every subtree is
|
|
371
|
-
* compact, and before this rule existed a data-density="comfortable" wrapper matched nothing
|
|
371
|
+
* compact, and before this rule existed a data-density="comfortable" wrapper matched nothing, so the
|
|
372
372
|
* comment above was true in one direction only. An app that is dense overall and carries one region
|
|
373
373
|
* of shared chrome (a header, a toolbar, a footer shared with a sibling product) had to restate the
|
|
374
374
|
* defaults by hand, which is exactly the local copy that goes stale when the kit retunes them. */
|
package/docs/agent-onboarding.md
CHANGED
|
@@ -5,7 +5,7 @@ Copilot…). No model has Freeday in its training data, so an agent that is mere
|
|
|
5
5
|
will invent class names or silently fall back to Bootstrap/Tailwind conventions. This file is the
|
|
6
6
|
fix: paste the block below into the consuming project's agent instruction file, once.
|
|
7
7
|
|
|
8
|
-
> Working on **the kit itself**, not a consuming app? That's [`../CLAUDE.md`](../CLAUDE.md)
|
|
8
|
+
> Working on **the kit itself**, not a consuming app? That's [`../CLAUDE.md`](../CLAUDE.md). This
|
|
9
9
|
> file is about *using* the published package.
|
|
10
10
|
|
|
11
11
|
---
|
|
@@ -23,7 +23,7 @@ markup) with **typed components for Vue, React and Blazor** layered on top. Most
|
|
|
23
23
|
markup + classes; ten interactive components also ship a typed wrapper, and in those three stacks
|
|
24
24
|
the wrapper is the correct way to use them.
|
|
25
25
|
|
|
26
|
-
**0. First decide which entry point this project uses. This is not an optimisation
|
|
26
|
+
**0. First decide which entry point this project uses. This is not an optimisation. Get it wrong
|
|
27
27
|
and the code looks correct and fails later.**
|
|
28
28
|
|
|
29
29
|
| This project's stack | Import the ten components from | Binding |
|
|
@@ -31,38 +31,38 @@ and the code looks correct and fails later.**
|
|
|
31
31
|
| Vue 3 | `@cahyo-dimas/freeday/vue` | `v-model` |
|
|
32
32
|
| React 18/19 | `@cahyo-dimas/freeday/react` | `value` + `onChange` |
|
|
33
33
|
| Blazor (net8.0) | `@using Freeday.Blazor` (RCL) | `@bind-Value` |
|
|
34
|
-
| Static HTML, Svelte, server-rendered templates… | no wrapper
|
|
34
|
+
| Static HTML, Svelte, server-rendered templates… | no wrapper: raw markup + the enhancer script | `fdy-*` DOM events |
|
|
35
35
|
|
|
36
36
|
The ten: **FdyCombo · FdyDatepicker · FdyDateRange · FdyAutocomplete · FdyCascade · FdyCfl ·
|
|
37
37
|
FdyChart · FdyTable · FdyModal · FdyDrawer**. In Vue/React/Blazor, **never hand-write the raw
|
|
38
|
-
markup + enhancer for these
|
|
39
|
-
once on `DOMContentLoaded` and the first render is correct
|
|
38
|
+
markup + enhancer for these eleven.** The raw path *appears* to work: the enhancer auto-initialises
|
|
39
|
+
once on `DOMContentLoaded` and the first render is correct. Then it fails quietly: DOM your framework
|
|
40
40
|
renders later is never hydrated, and the widget's state lives in the DOM instead of in your
|
|
41
41
|
framework's state.
|
|
42
42
|
|
|
43
43
|
Everything else is the same in every stack: plain `fdy-*` markup (button, card, badge, alert,
|
|
44
44
|
table markup, layout…). For the *other* interactive components (chips, stepper, input mask, file
|
|
45
45
|
upload, tree, tabs, menu, rating, slider, form validation, carousel, timepicker) there is no
|
|
46
|
-
wrapper
|
|
46
|
+
wrapper, use the raw markup and hydrate it:
|
|
47
47
|
|
|
48
|
-
- **Vue / React
|
|
49
|
-
once at app entry to register the enhancers. (The
|
|
48
|
+
- **Vue / React:** `useFreeday(rootRef)` from the same import path, plus `import '@cahyo-dimas/freeday'`
|
|
49
|
+
once at app entry to register the enhancers. (The eleven typed components do **not** need this: they
|
|
50
50
|
are native Vue/React implementations of the same markup, not wrappers over the enhancer.)
|
|
51
|
-
- **Blazor
|
|
51
|
+
- **Blazor:** `FreedayBlazor.initAll` interop. (Here the typed components *are* thin wrappers over
|
|
52
52
|
the enhancers, so the enhancer script is always required.)
|
|
53
53
|
|
|
54
54
|
**Before writing or editing any markup/CSS, read these (they ship inside the package):**
|
|
55
|
-
- `node_modules/@cahyo-dimas/freeday/COMPONENTS.md`
|
|
55
|
+
- `node_modules/@cahyo-dimas/freeday/COMPONENTS.md` lists every class that exists, with minimal markup
|
|
56
56
|
skeletons, enhancer hooks and the a11y contract per component. **The class list is closed:
|
|
57
|
-
if a class is not in that file, it does not exist
|
|
58
|
-
- `node_modules/@cahyo-dimas/freeday/USAGE.md`
|
|
59
|
-
- `node_modules/@cahyo-dimas/freeday/docs/reference-screen.html`
|
|
57
|
+
if a class is not in that file, it does not exist. Do not invent one.**
|
|
58
|
+
- `node_modules/@cahyo-dimas/freeday/USAGE.md` is the doctrine: which token/role/shadow to use when.
|
|
59
|
+
- `node_modules/@cahyo-dimas/freeday/docs/reference-screen.html` is one complete screen, assembled
|
|
60
60
|
the intended way. Copy this structure for a new screen.
|
|
61
61
|
|
|
62
62
|
**Non-negotiables:**
|
|
63
63
|
1. No raw hex or px in app CSS. Use tokens: `var(--color-primary)`, `var(--space-4)` (4px scale),
|
|
64
64
|
`var(--radius-md)`, `var(--shadow-1)`, `var(--dur-2)`.
|
|
65
|
-
2. Components only touch semantic tokens (`--color-*`)
|
|
65
|
+
2. Components only touch semantic tokens (`--color-*`), never the primitive ramp (`--azure-600`).
|
|
66
66
|
3. `.fdy-btn` is already the primary action (there is no `--primary`). One per screen; everything
|
|
67
67
|
else is `--ghost` or `--text`.
|
|
68
68
|
4. Three title roles only: `.fdy-title-page` (one `<h1>`) / `.fdy-title-section` / `.fdy-title-card`.
|
|
@@ -72,40 +72,40 @@ wrapper — use the raw markup and hydrate it:
|
|
|
72
72
|
Icon-only buttons need `aria-label`. Status is never colour-only.
|
|
73
73
|
7. Interactive components need their enhancer script loaded (see the table in COMPONENTS.md); in an
|
|
74
74
|
SPA, re-hydrate dynamic DOM with `useFreeday` / `FreedayBlazor.initAll`. This applies to the
|
|
75
|
-
components **without** a typed wrapper
|
|
75
|
+
components **without** a typed wrapper. For the eleven in step 0, use the wrapper instead.
|
|
76
76
|
8. Freeday owns components + tokens, **not layout**. Grids/stacks/one-off gaps come from our own
|
|
77
|
-
layout layer
|
|
77
|
+
layout layer. Build its theme on `var(--space-N)` so both systems stay in step.
|
|
78
78
|
```
|
|
79
79
|
|
|
80
80
|
Adjust the paths if the package lives somewhere else (a workspace, a vendored copy, `wwwroot/` for
|
|
81
|
-
Blazor). Then verify the agent can actually read those files
|
|
81
|
+
Blazor). Then verify the agent can actually read those files. An agent that can't open
|
|
82
82
|
`node_modules` will keep guessing.
|
|
83
83
|
|
|
84
84
|
## 2. What ships in the package
|
|
85
85
|
|
|
86
86
|
| File | What it answers |
|
|
87
87
|
|---|---|
|
|
88
|
-
| `COMPONENTS.md` | The complete class surface
|
|
89
|
-
| `USAGE.md` | The doctrine
|
|
88
|
+
| `COMPONENTS.md` | The complete class surface: what exists, its modifiers, minimal markup, a11y. |
|
|
89
|
+
| `USAGE.md` | The doctrine: which token/role/shadow/emphasis to use when. |
|
|
90
90
|
| `docs/getting-started.md` | Install + import + theme, per stack (Static HTML · Vue · React · Blazor). |
|
|
91
91
|
| `docs/integrations.md` | How to bridge third-party libraries (validation, charts, dates, i18n…). |
|
|
92
92
|
| `docs/reference-screen.html` | A full screen assembled from the shell down. Open it in a browser. |
|
|
93
93
|
| `docs/agent-onboarding.md` | This file. |
|
|
94
|
-
| `CHANGELOG.md` | **What changed between the version this project had and the one it has now
|
|
95
|
-
| `dist/` | Built CSS + enhancers. **`freeday.bundle.css` = tokens + components** (what `@cahyo-dimas/freeday/css` resolves to); `freeday.css` is components **only**, `freeday.tokens.css` tokens only
|
|
94
|
+
| `CHANGELOG.md` | **What changed between the version this project had and the one it has now.** Read it after every upgrade; each entry says what broke, what is new, and why. |
|
|
95
|
+
| `dist/` | Built CSS + enhancers. **`freeday.bundle.css` = tokens + components** (what `@cahyo-dimas/freeday/css` resolves to); `freeday.css` is components **only**, `freeday.tokens.css` tokens only, so linking `freeday.css` alone leaves every `var(--…)` unresolved. Plus `freeday-*.js` and the `.d.ts` files. |
|
|
96
96
|
| `src/components/*.css` | The authoritative source for every class, when a doc is ambiguous. |
|
|
97
|
-
| `tokens/tokens.json` | Every token in W3C DTCG format
|
|
97
|
+
| `tokens/tokens.json` | Every token in W3C DTCG format, machine-readable. |
|
|
98
98
|
| `adapters/vue` · `adapters/react` · `adapters/blazor` | Typed wrappers, 10 components each. |
|
|
99
99
|
|
|
100
100
|
The live docs (with an interactive playground) are at
|
|
101
|
-
<https://cahyo-dimas.github.io/freeday-ui-kit/>, and the repo
|
|
102
|
-
apps under `examples/` (Vue, React, Blazor) that are **not** in the npm tarball
|
|
101
|
+
<https://cahyo-dimas.github.io/freeday-ui-kit/>, and the repo (including three complete example
|
|
102
|
+
apps under `examples/` (Vue, React, Blazor) that are **not** in the npm tarball) is at
|
|
103
103
|
<https://github.com/cahyo-dimas/freeday-ui-kit>.
|
|
104
104
|
|
|
105
105
|
### After an upgrade, read the changelog first
|
|
106
106
|
|
|
107
107
|
`npm i @cahyo-dimas/freeday@latest` does not tell you what you gained. Read
|
|
108
|
-
`node_modules/@cahyo-dimas/freeday/CHANGELOG.md` down to the version this project was on before
|
|
108
|
+
`node_modules/@cahyo-dimas/freeday/CHANGELOG.md` down to the version this project was on before,
|
|
109
109
|
it is written for exactly this moment, and it is the difference between adopting a new affordance
|
|
110
110
|
and re-implementing it locally.
|
|
111
111
|
|
|
@@ -130,31 +130,31 @@ Recent additions most likely to replace something an app hand-rolled (all detail
|
|
|
130
130
|
|
|
131
131
|
The order matters; skipping to components is what produces flat, identical-card screens.
|
|
132
132
|
|
|
133
|
-
0. **Pick the screen shape first.** Which archetype is this
|
|
133
|
+
0. **Pick the screen shape first.** Which archetype is this: dashboard, master-detail, kanban,
|
|
134
134
|
wizard, POS…? The repo's
|
|
135
135
|
[`reference/README.md`](https://github.com/cahyo-dimas/freeday-ui-kit/blob/main/reference/README.md)
|
|
136
136
|
maps 15 archetypes to the exact primitives that compose each one, and says plainly which shapes
|
|
137
137
|
the kit has **no** component for (kanban columns, calendar month grid, chat bubbles, canvas) so
|
|
138
|
-
you build the frame instead of inventing a class. Not in the npm package
|
|
139
|
-
1. **Shell
|
|
138
|
+
you build the frame instead of inventing a class. Not in the npm package, so read it on GitHub.
|
|
139
|
+
1. **Shell:** is `.fdy-app` already in place (usually once, in the app layout)? If not, copy it
|
|
140
140
|
from `docs/getting-started.md` §The app shell.
|
|
141
|
-
2. **Theme
|
|
141
|
+
2. **Theme:** `data-theme="light|dark"` + `data-density="comfortable|compact"`, normally on
|
|
142
142
|
`<html>`, set once at the root. Use `compact` for table-heavy back-office screens. Both attributes
|
|
143
143
|
also work on **any ancestor**: `<section data-theme="dark">` inverts that region and every
|
|
144
144
|
component inside it follows, so never hand-colour an inverted panel.
|
|
145
|
-
3. **Fonts
|
|
145
|
+
3. **Fonts:** the package ships **no** `@font-face`. Load Sora / IBM Plex Sans / JetBrains Mono
|
|
146
146
|
yourself, or override `--font-display`/`--font-body`/`--font-mono`. Skipping this reads as
|
|
147
147
|
"unfinished design", not "missing dependency".
|
|
148
|
-
4. **Page frame
|
|
148
|
+
4. **Page frame:** `.fdy-page` + `.fdy-page__header` (eyebrow + `.fdy-title-page` + desc on the
|
|
149
149
|
left, **one** primary action on the right).
|
|
150
|
-
5. **Sections
|
|
151
|
-
6. **Components
|
|
152
|
-
7. **Verify
|
|
150
|
+
5. **Sections:** one `.fdy-page-section` per region, each with a `.fdy-title-section`.
|
|
151
|
+
6. **Components:** from `COMPONENTS.md`, inside the sections.
|
|
152
|
+
7. **Verify:** the checklist in §5.
|
|
153
153
|
|
|
154
154
|
## 4. Migrating an existing UI to Freeday
|
|
155
155
|
|
|
156
156
|
Migration is a **class-and-structure swap**, not a rewrite. Keep the app's DOM semantics; replace
|
|
157
|
-
the styling layer. Rough equivalents
|
|
157
|
+
the styling layer. Rough equivalents, but always confirm the target class in `COMPONENTS.md`, and note
|
|
158
158
|
that Freeday deliberately has **no** layout/spacing utilities, so grid/flex/margin classes stay with
|
|
159
159
|
your own layout layer:
|
|
160
160
|
|
|
@@ -174,19 +174,19 @@ your own layout layer:
|
|
|
174
174
|
| `table table-striped` / `MudTable` | `fdy-table` in `fdy-table-wrap`; interactive → `fdy-datatable` |
|
|
175
175
|
| `badge bg-success` / `MudChip` (status) | `fdy-badge fdy-badge--success` |
|
|
176
176
|
| `alert alert-danger` | `fdy-alert fdy-alert--danger` + `role="alert"` |
|
|
177
|
-
| `modal` / `MudDialog` | `<dialog class="fdy-modal">` (native
|
|
177
|
+
| `modal` / `MudDialog` | `<dialog class="fdy-modal">` (native, so drop the JS backdrop plumbing) |
|
|
178
178
|
| `offcanvas` / `MudDrawer` | `<dialog class="fdy-drawer">` + `data-fdy-drawer` |
|
|
179
179
|
| `nav nav-tabs` | `fdy-tabs` + `data-fdy-tabs` |
|
|
180
180
|
| `breadcrumb` / `pagination` | `fdy-breadcrumb` / `fdy-pagination` (same `<nav><ol>` structure) |
|
|
181
181
|
| `spinner-border` / `progress` | `fdy-spinner` / `fdy-progress` + `__bar` |
|
|
182
|
-
| `toast` container + JS | `Freeday.toast({…})
|
|
182
|
+
| `toast` container + JS | `Freeday.toast({…})`, imperative, no markup to author |
|
|
183
183
|
| `text-muted` | `fdy-text-muted` |
|
|
184
184
|
| `d-none` / `visually-hidden` | `fdy-hidden` / `fdy-visually-hidden` |
|
|
185
|
-
| `container` / `row` / `col-*` / `mb-3` / `gap-2` | **stays yours
|
|
185
|
+
| `container` / `row` / `col-*` / `mb-3` / `gap-2` | **stays yours**: Freeday ships no layout utilities |
|
|
186
186
|
|
|
187
187
|
Order of work that avoids a half-migrated mess:
|
|
188
188
|
|
|
189
|
-
1. Load Freeday's CSS and **turn off the old framework's reset/preflight
|
|
189
|
+
1. Load Freeday's CSS and **turn off the old framework's reset/preflight**, because `base.css` is the
|
|
190
190
|
reset now. Two resets fighting is the usual source of "everything looks slightly off".
|
|
191
191
|
2. Shell + theme attributes first, so tokens resolve everywhere.
|
|
192
192
|
3. Then screen by screen: page frame → sections → controls. Convert a whole screen at a time;
|
|
@@ -194,15 +194,15 @@ Order of work that avoids a half-migrated mess:
|
|
|
194
194
|
4. Delete the old framework's CSS only when no screen references it, then grep for leftover class
|
|
195
195
|
prefixes.
|
|
196
196
|
5. Replace hand-rolled modal/drawer/dropdown JS with the native-`<dialog>` components and the
|
|
197
|
-
enhancers
|
|
197
|
+
enhancers, which is usually where the most code disappears.
|
|
198
198
|
|
|
199
199
|
## 5. Verification checklist (before claiming a screen is done)
|
|
200
200
|
|
|
201
201
|
- Every `fdy-*` class used appears in `COMPONENTS.md`. Grep the diff for `fdy-` and check.
|
|
202
202
|
- No raw hex/rgb/px in the diff's CSS. Grep for `#` and `px`.
|
|
203
203
|
- Exactly one `.fdy-btn` without a variant modifier on the screen; one `.fdy-title-page`.
|
|
204
|
-
- Toggle `data-theme="dark"` on `<html
|
|
205
|
-
- Toggle `data-density="compact"
|
|
204
|
+
- Toggle `data-theme="dark"` on `<html>`: nothing becomes unreadable, no hard-coded white/black.
|
|
205
|
+
- Toggle `data-density="compact"`: the layout still holds.
|
|
206
206
|
- Keyboard: Tab reaches every control, focus is always visible, Esc closes overlays.
|
|
207
207
|
- Form errors carry `aria-invalid` + a linked message; icon-only buttons have `aria-label`.
|
|
208
208
|
- The interactive components on the screen have their enhancer loaded, and SPA-rendered DOM is
|
package/docs/getting-started.md
CHANGED
|
@@ -15,43 +15,43 @@ A step-by-step guide to adopting Freeday in **your new project**. Pick your stac
|
|
|
15
15
|
Freeday = **CSS** (semantic tokens + `fdy-*` classes) + **zero-dependency JS enhancers** (optional).
|
|
16
16
|
|
|
17
17
|
1. **Static vs interactive.** Static components (button, card, badge, plain input, layout) need
|
|
18
|
-
only the **`fdy-*` classes
|
|
18
|
+
only the **`fdy-*` classes**, no JS. Interactive components (select/combo, cascade, date/time
|
|
19
19
|
picker, table, dropzone, form validation, input mask, chip) need the **JS enhancers**.
|
|
20
|
-
2. **The enhancer is the source of truth
|
|
20
|
+
2. **The enhancer is the source of truth, *on the raw path*.** You don't re-implement components;
|
|
21
21
|
the enhancer owns the widget's DOM. You **listen for `fdy-*` events** (all bubbling
|
|
22
22
|
`CustomEvent`s, data in `event.detail`) → store them in your framework state. Event/API contract
|
|
23
23
|
table: [`integrations.md` §Event & API contract](integrations.md).
|
|
24
|
-
**On Vue, React or Blazor this is not the path to take for
|
|
24
|
+
**On Vue, React or Blazor this is not the path to take for eleven of the components.** `FdyCombo`,
|
|
25
25
|
`FdyDatepicker`, `FdyDateRange`, `FdyAutocomplete`, `FdyCascade`, `FdyCfl`, `FdyChart`,
|
|
26
26
|
`FdyTable`, `FdyModal`, `FdyDrawer` ship typed wrappers that own the state properly (Vue and
|
|
27
27
|
React re-implement the interaction natively; Blazor wraps the enhancer over interop). Use them;
|
|
28
28
|
the raw path is for the components without a wrapper, and for stacks without an adapter.
|
|
29
29
|
3. **Hydrate dynamic DOM.** Enhancers auto-init once on `DOMContentLoaded`. DOM an SPA renders
|
|
30
30
|
**after** that must be re-hydrated: `window.Freeday<X>.initAll(el)` (idempotent, safe to repeat).
|
|
31
|
-
Each framework's adapter wraps this
|
|
31
|
+
Each framework's adapter wraps this, so you don't call it manually.
|
|
32
32
|
4. **Theme via `data-*`.** `data-theme="light|dark"` (all semantic tokens switch) +
|
|
33
33
|
`data-density="comfortable|compact"` (control height, for data-dense screens). Normally on
|
|
34
34
|
`<html>`; change at runtime with `document.documentElement.dataset.theme = 'dark'`. Both also
|
|
35
|
-
work on **any ancestor
|
|
35
|
+
work on **any ancestor**, because these are inheriting custom properties, so `<section data-theme="dark">`
|
|
36
36
|
inverts just that region and every component inside it follows. See [`USAGE.md`](../USAGE.md) §5b.
|
|
37
37
|
5. **3-tier token rule.** Components only touch **Tier 2/3** (`var(--color-primary)`,
|
|
38
38
|
`var(--space-4)`, `var(--radius-md)`…). **Never** write raw hex/px.
|
|
39
39
|
6. **Scope: components + tokens, deliberately *not* layout.** Freeday ships components and tokens;
|
|
40
40
|
the only layout helpers are `.fdy-hidden` / `.fdy-visually-hidden`. Stacks, grids, gaps and sizing
|
|
41
|
-
come from **your** layout layer
|
|
41
|
+
come from **your** layout layer. Pair Freeday with a utility framework (Tailwind, UnoCSS…) run
|
|
42
42
|
**utilities-only, preflight OFF** (Freeday's `base.css` is your reset). Two consequences worth
|
|
43
43
|
knowing up front:
|
|
44
|
-
- **base.css is a *light* reset
|
|
44
|
+
- **base.css is a *light* reset.** It does not strip `ul`/`ol`/`p` margins. With preflight off, a
|
|
45
45
|
semantic `<ul>` keeps native bullets + a 40px indent; add **`.fdy-list-reset`** (or use a Freeday
|
|
46
46
|
list component) on such lists.
|
|
47
47
|
- **The spacing scale is public.** `--space-0`…`--space-24`, `--radius-*`, `--dur-*` etc. are real
|
|
48
|
-
custom properties in `dist/freeday.tokens.css
|
|
48
|
+
custom properties in `dist/freeday.tokens.css`, so **define your utility theme in terms of them**
|
|
49
49
|
(`spacing: { 4: 'var(--space-4)' }`) so both systems stay in step. `data-density="compact"` steps
|
|
50
50
|
`--control-h` **and** the mid-range spacing scale (`--space-3`…`--space-6`), so Freeday components
|
|
51
|
-
densify
|
|
52
|
-
7. **Load the fonts
|
|
51
|
+
densify, and if your utility theme is built on `var(--space-N)`, density reaches your utilities too.
|
|
52
|
+
7. **Load the fonts yourself. The package does not.** The type tokens *name* **Sora** (display), **IBM Plex
|
|
53
53
|
Sans** (body) and **JetBrains Mono** (data), but Freeday bundles no `@font-face` and no font files.
|
|
54
|
-
Load them yourself, or the kit renders in the system fallback
|
|
54
|
+
Load them yourself, or the kit renders in the system fallback, which reads as "unfinished design",
|
|
55
55
|
not "missing dependency". One line with [Fontsource](https://fontsource.org):
|
|
56
56
|
```css
|
|
57
57
|
@import '@fontsource/sora/600.css'; @import '@fontsource/sora/700.css';
|
|
@@ -59,23 +59,23 @@ Freeday = **CSS** (semantic tokens + `fdy-*` classes) + **zero-dependency JS enh
|
|
|
59
59
|
```
|
|
60
60
|
(Or a `<link>` to your own self-hosted copies, or override `--font-display`/`--font-body`/`--font-mono`
|
|
61
61
|
to faces you already ship. If you keep a system-sans fallback, consider softening
|
|
62
|
-
`--tracking-tighter` on headings
|
|
62
|
+
`--tracking-tighter` on headings, since it's tuned for Sora's proportions.)
|
|
63
63
|
8. **Start from the shell, then compose.** Every application goes inside **`.fdy-app`** (see below);
|
|
64
|
-
inside it, assemble screens from the composition primitives
|
|
65
|
-
`.fdy-page-section`, `.fdy-toolbar`, `.fdy-stats`/`.fdy-stat`
|
|
64
|
+
inside it, assemble screens from the composition primitives (`.fdy-page`, `.fdy-page__header`,
|
|
65
|
+
`.fdy-page-section`, `.fdy-toolbar`, `.fdy-stats`/`.fdy-stat`) and the type roles (`.fdy-title-page`
|
|
66
66
|
/ `-section` / `-card`), not by re-using `.fdy-card__title` for everything. **Which token/role/shadow
|
|
67
|
-
to use when lives in [`USAGE.md`](../USAGE.md)
|
|
67
|
+
to use when lives in [`USAGE.md`](../USAGE.md)**. Read it once; it's what makes screens cohere.
|
|
68
68
|
|
|
69
69
|
---
|
|
70
70
|
|
|
71
71
|
## The app shell (start here)
|
|
72
72
|
|
|
73
|
-
Every Freeday application goes inside **`.fdy-app
|
|
73
|
+
Every Freeday application goes inside **`.fdy-app`**, the frame that holds a top bar, a sidebar, and
|
|
74
74
|
the scrolling content. Don't hand-roll one from flexbox; the responsive sidebar + backdrop are built in.
|
|
75
75
|
The nav toggle is one line: toggle `.fdy-app--nav-open` (mobile drawer) / `.fdy-app--nav-collapsed`
|
|
76
76
|
(desktop) on the `.fdy-app` element from the `__navtoggle` button's click.
|
|
77
77
|
|
|
78
|
-
The nesting is not free-form
|
|
78
|
+
The nesting is not free-form: `.fdy-app` is a flex **row** of `[sidebar | content]`, and `__content`
|
|
79
79
|
is the column that holds the topbar and the main area (it gives the sticky topbar a tall containing
|
|
80
80
|
block to travel in). The brand belongs in the **sidebar**, sized to match the topbar's height:
|
|
81
81
|
|
|
@@ -109,11 +109,11 @@ block to travel in). The brand belongs in the **sidebar**, sized to match the to
|
|
|
109
109
|
</div>
|
|
110
110
|
```
|
|
111
111
|
|
|
112
|
-
`.fdy-app__main` already carries the page padding (`--space-8`, `--space-5` on mobile)
|
|
112
|
+
`.fdy-app__main` already carries the page padding (`--space-8`, `--space-5` on mobile), so don't wrap
|
|
113
113
|
your screen in another padded box. The toggle's two states split at **720px**: above it,
|
|
114
114
|
`.fdy-app--nav-collapsed` collapses the sidebar to zero width; at or below it,
|
|
115
115
|
`.fdy-app--nav-open` slides the sidebar in as an off-canvas drawer over the backdrop. A complete,
|
|
116
|
-
working version of all of this
|
|
116
|
+
working version of all of this, including the toggle script, is
|
|
117
117
|
[`reference-screen.html`](reference-screen.html).
|
|
118
118
|
|
|
119
119
|
Then compose the screen inside `__main` with `.fdy-page` / `.fdy-page__header` / `.fdy-page-section`
|
|
@@ -125,10 +125,10 @@ token to use where.
|
|
|
125
125
|
|
|
126
126
|
## Static HTML (no build)
|
|
127
127
|
|
|
128
|
-
Good for plain `.html` pages / templates
|
|
128
|
+
Good for plain `.html` pages / templates, with no bundler and no npm.
|
|
129
129
|
|
|
130
130
|
### 1. Get the dist files into your project
|
|
131
|
-
`dist/` is committed, so there's no build step. Easiest way
|
|
131
|
+
`dist/` is committed, so there's no build step. Easiest way: use npm once just to download, then
|
|
132
132
|
copy the files (vendor them):
|
|
133
133
|
```bash
|
|
134
134
|
npm i @cahyo-dimas/freeday
|
|
@@ -184,7 +184,7 @@ live docs also have a copy button per component.
|
|
|
184
184
|
```bash
|
|
185
185
|
npm i @cahyo-dimas/freeday
|
|
186
186
|
```
|
|
187
|
-
Lands in `package.json` as `"@cahyo-dimas/freeday": "^
|
|
187
|
+
Lands in `package.json` as `"@cahyo-dimas/freeday": "^2.0.0"` (public npm package). `dist/` is
|
|
188
188
|
committed and published → no build step; `npm ci` runs without auth.
|
|
189
189
|
|
|
190
190
|
### 2. Import the CSS + enhancers **once** in your entry (`src/main.ts`)
|
|
@@ -229,7 +229,7 @@ const onDate = (e: Event) => { form.dueDate = (e as CustomEvent<FdyDatepicke
|
|
|
229
229
|
```
|
|
230
230
|
|
|
231
231
|
**Gotcha:** if TypeScript complains about `import '@cahyo-dimas/freeday/css'`, make sure `env.d.ts`
|
|
232
|
-
has `/// <reference types="vite/client" />`. For **Nuxt/SSR**, enhancers are client-only
|
|
232
|
+
has `/// <reference types="vite/client" />`. For **Nuxt/SSR**, enhancers are client-only, so wrap them
|
|
233
233
|
in `onMounted`/`<ClientOnly>`.
|
|
234
234
|
|
|
235
235
|
Full working example: [`examples/vue-faktur/`](../examples/vue-faktur/).
|
|
@@ -296,13 +296,13 @@ export function Panel() {
|
|
|
296
296
|
}
|
|
297
297
|
```
|
|
298
298
|
|
|
299
|
-
**Gotcha:** because the enhancer owns the widget DOM, don't double-control it from React
|
|
299
|
+
**Gotcha:** because the enhancer owns the widget DOM, don't double-control it from React. Store the
|
|
300
300
|
value from `event.detail` in state/ref; don't set the DOM `value` back. `StrictMode` mounts twice in
|
|
301
301
|
dev; `useFreeday` is idempotent, so it's safe.
|
|
302
302
|
|
|
303
303
|
### 5. Alternative: typed controlled components (`FdyCombo` · `FdyDatepicker` · `FdyDateRange` · `FdyAutocomplete` · `FdyCascade` · `FdyCfl` · `FdyChart`)
|
|
304
304
|
For fields you'd normally write as a native `<select>`/`<input type="date">`,
|
|
305
|
-
`@cahyo-dimas/freeday/react` also exports typed **controlled** components
|
|
305
|
+
`@cahyo-dimas/freeday/react` also exports typed **controlled** components with plain `value`/`onChange`,
|
|
306
306
|
no manual event bubbling (parity with the Vue `v-model` components above):
|
|
307
307
|
```tsx
|
|
308
308
|
import { FdyCombo } from '@cahyo-dimas/freeday/react';
|
|
@@ -320,7 +320,7 @@ function StatusField({ value, onChange }: { value: Status; onChange: (v: Status)
|
|
|
320
320
|
}
|
|
321
321
|
```
|
|
322
322
|
`FdyDatepicker`, `FdyCfl` (async choose-from-list), and `FdyChart` share the same shape (typed
|
|
323
|
-
`value`/`onChange`, or `series`/`values` for `FdyChart`)
|
|
323
|
+
`value`/`onChange`, or `series`/`values` for `FdyChart`). See [`integrations.md`](integrations.md)
|
|
324
324
|
and `examples/react-faktur/src/App.tsx` for the full patterns. **Vite works with no extra config**
|
|
325
325
|
(esbuild transpiles the `.tsx` source directly); **Next.js** consumers may need
|
|
326
326
|
`transpilePackages: ['@cahyo-dimas/freeday']` in `next.config.js`.
|
|
@@ -331,9 +331,9 @@ Full working example: [`examples/react-faktur/`](../examples/react-faktur/).
|
|
|
331
331
|
|
|
332
332
|
## Blazor (WASM)
|
|
333
333
|
|
|
334
|
-
Blazor doesn't use npm
|
|
334
|
+
Blazor doesn't use npm. Freeday is served as **static files** in `wwwroot/`.
|
|
335
335
|
|
|
336
|
-
> **Prefer the native components?** Jump to [§4
|
|
336
|
+
> **Prefer the native components?** Jump to [§4, the `Freeday.Blazor` RCL](#4-recommended-native-typed-components-freedayblazor-rcl):
|
|
337
337
|
> typed `<FdyX>` with `@bind`, no manual JS interop. Steps 1–2 (assets + scripts) still apply; step 3
|
|
338
338
|
> below (the raw enhancer + event bridge) is the underlying mechanism and the fallback for markup the
|
|
339
339
|
> RCL doesn't cover.
|
|
@@ -365,7 +365,7 @@ Load `freeday.js` then `freeday-blazor.js` **before** `blazor.webassembly.js`:
|
|
|
365
365
|
<script src="_framework/blazor.webassembly.js"></script>
|
|
366
366
|
</body>
|
|
367
367
|
```
|
|
368
|
-
> Use the global IIFE (`window.FreedayBlazor`), **not** an ES module
|
|
368
|
+
> Use the global IIFE (`window.FreedayBlazor`), **not** an ES module, so it passes strict-MIME on static hosts.
|
|
369
369
|
|
|
370
370
|
### 3. Hydrate + bridge events in code-behind (`.razor.cs`)
|
|
371
371
|
In `OnAfterRenderAsync(firstRender)`: `initAll`, then `on(...)` per event → `[JSInvokable]` methods.
|
|
@@ -402,7 +402,7 @@ public partial class Panel : ComponentBase, IAsyncDisposable
|
|
|
402
402
|
}
|
|
403
403
|
```
|
|
404
404
|
```razor
|
|
405
|
-
@* Panel.razor
|
|
405
|
+
@* Panel.razor: @ref on the subtree container, fdy-* classes + data-fdy-* hooks in the markup *@
|
|
406
406
|
<div @ref="_root">
|
|
407
407
|
<button class="fdy-btn fdy-btn--primary" type="button">Save</button>
|
|
408
408
|
<div data-fdy-cascade></div>
|
|
@@ -415,7 +415,7 @@ to flip the theme. Event DTOs are deserialized case-insensitively by Blazor.
|
|
|
415
415
|
### 4. Recommended: native typed components (`Freeday.Blazor` RCL)
|
|
416
416
|
|
|
417
417
|
Instead of hand-writing `fdy-*` markup + the interop above, reference the **Razor Class Library** and
|
|
418
|
-
use typed `<FdyX>` components with `@bind
|
|
418
|
+
use typed `<FdyX>` components with `@bind`, the Blazor equivalent of the Vue `v-model` / React
|
|
419
419
|
`value`/`onChange` adapters. Place the Freeday repo near your solution and add a project reference:
|
|
420
420
|
```xml
|
|
421
421
|
<!-- YourApp.csproj -->
|
|
@@ -428,7 +428,7 @@ use typed `<FdyX>` components with `@bind` — the Blazor equivalent of the Vue
|
|
|
428
428
|
Load `freeday.js` + `freeday-blazor.js` exactly as in step 2 (the components still hydrate over the
|
|
429
429
|
kit's CSS/enhancers), then bind:
|
|
430
430
|
```razor
|
|
431
|
-
@* Invoice.razor
|
|
431
|
+
@* Invoice.razor: no @ref, no manual JS interop, no [JSInvokable] *@
|
|
432
432
|
<FdyCombo TValue="string" @bind-Value="_status" Options="_statusOptions" AriaLabelledby="lbl-status" />
|
|
433
433
|
<FdyDatepicker @bind-Value="_dueDate" Label="Due date" />
|
|
434
434
|
<FdyTable TRow="Invoice" Columns="_cols" Rows="_rows" RowKey="@(i => i.Code)"
|
|
@@ -444,7 +444,7 @@ or controlled `Sort`/`Filters`/`Page` for a server-paged table; `RowActivatable`
|
|
|
444
444
|
`select`-type control also takes `Disabled`/`Readonly`/`Invalid`. The RCL targets **net8.0** and is
|
|
445
445
|
consumed as source (`<ProjectReference>`); `.NET bin/obj` never ships in the npm tarball.
|
|
446
446
|
|
|
447
|
-
Full working example
|
|
447
|
+
Full working example: [`examples/blazor-faktur/`](../examples/blazor-faktur/),
|
|
448
448
|
`Pages/ComponentsDemo.razor`.
|
|
449
449
|
|
|
450
450
|
---
|
|
@@ -452,8 +452,8 @@ Full working example (all ten): [`examples/blazor-faktur/`](../examples/blazor-f
|
|
|
452
452
|
## Verify (every stack)
|
|
453
453
|
|
|
454
454
|
Run the project → check two things:
|
|
455
|
-
1. **CSS connected
|
|
456
|
-
2. **Enhancers connected
|
|
455
|
+
1. **CSS connected:** buttons/cards are styled (not plain HTML).
|
|
456
|
+
2. **Enhancers connected:** interactive components come alive (e.g. datepicker/combo open on
|
|
457
457
|
click), and `event.detail` reaches your state.
|
|
458
458
|
|
|
459
459
|
If the visuals are plain → the CSS didn't load. If visuals are fine but widgets are dead → the
|
package/docs/integrations.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Freeday is a **dependency-free foundation**: tokens + accessible markup + vanilla enhancers. This
|
|
4
4
|
document maps each area to the ecosystem library you'd normally install in a real project, **when**
|
|
5
5
|
Freeday's built-ins are enough, and **how to bridge**. The goal: when you start a new project, open
|
|
6
|
-
this one file
|
|
6
|
+
this one file, so there's no hunting around.
|
|
7
7
|
|
|
8
8
|
> Golden rule: **Freeday owns the look (tokens + markup + a11y); the library owns the engine
|
|
9
9
|
> (heavy logic).** Don't duplicate. Connect through the 3 mechanisms below.
|
|
@@ -14,11 +14,11 @@ this one file — no more hunting around.
|
|
|
14
14
|
|
|
15
15
|
Every integration comes down to one of these:
|
|
16
16
|
|
|
17
|
-
1. **`fdy-*` events
|
|
17
|
+
1. **`fdy-*` events:** listen to enhancer output, forward it to framework/library state.
|
|
18
18
|
All events are bubbling `CustomEvent`s; the data is in `event.detail`.
|
|
19
|
-
2. **Init hook `window.Freeday<X>.initAll(el)
|
|
19
|
+
2. **Init hook `window.Freeday<X>.initAll(el)`:** call it again after DOM is rendered
|
|
20
20
|
dynamically (Vue `onMounted`, React `useEffect`, Blazor `OnAfterRenderAsync`). Idempotent.
|
|
21
|
-
3. **Color tokens
|
|
21
|
+
3. **Color tokens:** match a library's colors to the active theme:
|
|
22
22
|
```js
|
|
23
23
|
// Read a live semantic token so a 3rd-party lib matches the current theme
|
|
24
24
|
const token = (name) =>
|
|
@@ -79,7 +79,7 @@ library only.
|
|
|
79
79
|
| Virtual scroll | 🔌 | — | **TanStack Virtual** · Vue: **vue-virtual-scroller** | — |
|
|
80
80
|
|
|
81
81
|
> **Monospace data cells:** add `.fdy-mono` to any identifier / code / IP / timestamp cell (or an
|
|
82
|
-
> inline `<span>`) to render it in the data font with tabular figures. It is alignment-neutral
|
|
82
|
+
> inline `<span>`) to render it in the data font with tabular figures. It is alignment-neutral,
|
|
83
83
|
> unlike `.fdy-table__num`, which is also right-aligned. `FdyTable` applies it automatically to any
|
|
84
84
|
> column with `mono: true`.
|
|
85
85
|
|
|
@@ -115,7 +115,7 @@ library only.
|
|
|
115
115
|
|
|
116
116
|
| Area | Freeday enough? | When the built-in is enough | If you need more | Bridge |
|
|
117
117
|
|---|---|---|---|---|
|
|
118
|
-
| Icons | ➕ (inline SVG) | A small built-in set | A full set: **Lucide** (best fit
|
|
118
|
+
| Icons | ➕ (inline SVG) | A small built-in set | A full set: **Lucide** (best fit, since Freeday uses this stroke style), **Heroicons**, **Tabler**, **Phosphor** | Drop the SVG into a slot: `[data-fdy-icon]`, `.fdy-input-group__addon--icon`, `.fdy-combo__icon`. |
|
|
119
119
|
| Drag & drop / sortable | 🔌 | — | **SortableJS** (vanilla) · React: **dnd-kit** · Vue: **vuedraggable** | Lay it out with Freeday markup, DnD from the library. |
|
|
120
120
|
| Command palette | 🔌 | — | **cmdk** (React) · **kbar** | Style with tokens + `.fdy-kbd`. |
|
|
121
121
|
| Routing | 🔌 | — | Vue Router · React Router · Blazor Router | After a route change → `window.FreedayTable.initAll()`, etc. |
|
|
@@ -124,8 +124,8 @@ library only.
|
|
|
124
124
|
|
|
125
125
|
## Per-framework binding
|
|
126
126
|
|
|
127
|
-
> **Recommended path:** use the ready-made adapters
|
|
128
|
-
> `useFreeday` hook (React), or `window.FreedayBlazor` (Blazor)
|
|
127
|
+
> **Recommended path:** use the ready-made adapters: the `useFreeday` composable (Vue), the
|
|
128
|
+
> `useFreeday` hook (React), or `window.FreedayBlazor` (Blazor). They wrap hydration + event
|
|
129
129
|
> bridging. Full steps per stack are in **[`getting-started.md`](getting-started.md)**. The raw
|
|
130
130
|
> `initAll()` pattern below is the underlying mechanism (and a fallback if you don't use an adapter).
|
|
131
131
|
|
|
@@ -154,7 +154,7 @@ function Panel(): JSX.Element {
|
|
|
154
154
|
```
|
|
155
155
|
> **Controlled alternative (parity with Vue's `v-model`):** `@cahyo-dimas/freeday/react` also exports
|
|
156
156
|
> the typed components `FdyCombo` / `FdyDatepicker` / `FdyDateRange` / `FdyAutocomplete` /
|
|
157
|
-
> `FdyCascade` / `FdyCfl` / `FdyChart` / `FdyTable` / `FdyModal` / `FdyDrawer
|
|
157
|
+
> `FdyCascade` / `FdyCfl` / `FdyChart` / `FdyTable` / `FdyModal` / `FdyDrawer`, with plain
|
|
158
158
|
> `value`/`onChange` (props in, events out), no `data-fdy-*` + manual event listener:
|
|
159
159
|
> ```tsx
|
|
160
160
|
> import { FdyCombo } from '@cahyo-dimas/freeday/react';
|
|
@@ -168,22 +168,22 @@ function Panel(): JSX.Element {
|
|
|
168
168
|
> `row-detail`/`renderRowDetail` + controlled `expandedKeys`); `FdyModal`/`FdyDrawer` take `open` +
|
|
169
169
|
> `onClose`.
|
|
170
170
|
>
|
|
171
|
-
> **Responsive list screens
|
|
171
|
+
> **Responsive list screens: drive the page from outside the table.** `FdyTable`'s pager renders
|
|
172
172
|
> inside `.fdy-datatable`, so a screen that hides the table below `md` and shows a card list loses it.
|
|
173
173
|
> Pass **`pageIndex`** (+ `update:pageIndex` / `onPageIndexChange` / `PageIndexChanged`) to own the
|
|
174
174
|
> client-side index while the table still does filter/sort/paginate: render **one** pager outside for
|
|
175
175
|
> both breakpoints, feed the card list from the **`process`** event (`{ rows, total }`), and build the
|
|
176
176
|
> pager's page window with `pageWindow` from `@cahyo-dimas/freeday/table-model` so it is literally the
|
|
177
|
-
> same computation the table uses. One page index can then span several tables
|
|
177
|
+
> same computation the table uses. One page index can then span several tables, which is what a
|
|
178
178
|
> grouped list needs. The same set is available
|
|
179
|
-
> in `@cahyo-dimas/freeday/vue` via `v-model` / props
|
|
179
|
+
> in `@cahyo-dimas/freeday/vue` via `v-model` / props, and both adapters are fully symmetric. See
|
|
180
180
|
> [`getting-started.md` §React](getting-started.md#react-vite) and
|
|
181
181
|
> `examples/react-faktur/src/App.tsx`. **Vite** transpiles the `.tsx` source with no extra config;
|
|
182
182
|
> **Next.js** may need `transpilePackages: ['@cahyo-dimas/freeday']`.
|
|
183
183
|
|
|
184
184
|
**Blazor**
|
|
185
185
|
```csharp
|
|
186
|
-
// Component.razor.cs
|
|
186
|
+
// Component.razor.cs: call the enhancer after Blazor renders the markup.
|
|
187
187
|
protected override async Task OnAfterRenderAsync(bool firstRender)
|
|
188
188
|
{
|
|
189
189
|
if (firstRender)
|
|
@@ -200,7 +200,7 @@ document.addEventListener('fdy-form-invalid', (e) =>
|
|
|
200
200
|
> **Native components (parity with Vue's `v-model` / React's `value`/`onChange`):** the
|
|
201
201
|
> **`Freeday.Blazor`** RCL (net8.0, `adapters/blazor/`) ships the typed components `FdyCombo<TValue>` /
|
|
202
202
|
> `FdyDatepicker` / `FdyDateRange` / `FdyAutocomplete` / `FdyCascade` / `FdyCfl<TRow>` / `FdyChart` /
|
|
203
|
-
> `FdyTable<TRow>` / `FdyModal` / `FdyDrawer`
|
|
203
|
+
> `FdyTable<TRow>` / `FdyModal` / `FdyDrawer` with `@bind`, no `@ref` / manual `initAll` / `[JSInvokable]`:
|
|
204
204
|
> ```razor
|
|
205
205
|
> @using Freeday.Blazor
|
|
206
206
|
> <FdyCombo TValue="string" @bind-Value="_status" Options="_statusOptions" AriaLabelledby="lbl-status" />
|
|
@@ -297,7 +297,7 @@ computePosition(trigger, tip, { placement: 'top', middleware: [offset(8), flip()
|
|
|
297
297
|
```
|
|
298
298
|
|
|
299
299
|
### 6. Large async select → `freeday-cfl`
|
|
300
|
-
For thousands of server-side rows, don't force a `<select
|
|
300
|
+
For thousands of server-side rows, don't force a `<select>`. Use choose-from-list:
|
|
301
301
|
```js
|
|
302
302
|
window.FreedayCfl.init(el, {
|
|
303
303
|
// Server owns paging/search; Freeday owns the accessible dialog UI.
|
|
@@ -311,13 +311,13 @@ el.addEventListener('fdy-cfl-select', (e) => store.setCustomer(e.detail.row));
|
|
|
311
311
|
## SAP B1 note
|
|
312
312
|
|
|
313
313
|
Freeday is purely for the **web** (companion apps, .NET web add-ons, portals). A **UI API
|
|
314
|
-
(SAPBouiCOM)** add-on is WinForms/COM
|
|
314
|
+
(SAPBouiCOM)** add-on is WinForms/COM, a different world where Freeday doesn't apply. For
|
|
315
315
|
web-based add-ons (Service Layer + .NET), Freeday + enhancers over JS interop work normally.
|
|
316
316
|
|
|
317
317
|
---
|
|
318
318
|
|
|
319
319
|
*Library recommendations reflect what's common and stable as of 2026; choose based on your
|
|
320
|
-
project's license and bundle-size budget. Freeday locks you into none of them
|
|
320
|
+
project's license and bundle-size budget. Freeday locks you into none of them. Everything is
|
|
321
321
|
optional and replaceable.*
|
|
322
322
|
|
|
323
323
|
## Building a control the kit does not ship
|
|
@@ -329,7 +329,7 @@ import { usePopover } from '@cahyo-dimas/freeday/vue'; // or '/react'
|
|
|
329
329
|
usePopover(panelRef, triggerRef, open); // panel needs popover="manual"
|
|
330
330
|
```
|
|
331
331
|
|
|
332
|
-
This is what every kit dropdown uses to escape an ancestor's overflow clip
|
|
332
|
+
This is what every kit dropdown uses to escape an ancestor's overflow clip, because `.fdy-card` is
|
|
333
333
|
`overflow:hidden`, so a panel positioned inside one is otherwise cut at the card's edge. It is
|
|
334
334
|
exported so a control you build behaves like one the kit ships, and improves when the kit's
|
|
335
335
|
positioning does.
|