@ziyarago/ui 0.13.0 → 0.15.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.
Files changed (75) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +5 -5
  3. package/dist/llms-full.txt +3 -3
  4. package/dist/llms.txt +2 -2
  5. package/dist/manifest.json +316 -2
  6. package/docs/INTEGRATION.md +14 -0
  7. package/package.json +10 -25
  8. package/src/core/components.js +0 -11
  9. package/src/core/components2.js +1 -71
  10. package/src/core/components3.js +2 -44
  11. package/src/core/components4.js +0 -38
  12. package/src/core/components6.js +0 -50
  13. package/src/core/components7.js +0 -34
  14. package/src/core/index.js +6 -0
  15. package/src/core/pages.js +2 -36
  16. package/src/i18n/components/index.js +0 -72
  17. package/src/internal.js +10 -0
  18. package/src/meta/index.js +15 -0
  19. package/src/meta/pages.js +1 -1
  20. package/src/nuxt/module.mjs +9 -1
  21. package/src/react/index.js +8 -107
  22. package/src/resolver.js +17 -4
  23. package/src/vue/index.js +9 -2
  24. package/types/core.d.ts +3 -0
  25. package/types/internal.d.ts +30 -0
  26. package/types/meta.d.ts +8 -0
  27. package/types/nuxt.d.ts +2 -0
  28. package/types/react.d.ts +138 -4143
  29. package/types/resolver.d.ts +2 -0
  30. package/types/vue-global.d.ts +0 -106
  31. package/types/vue.d.ts +147 -3598
  32. package/docs/MIGRATION.md +0 -455
  33. package/src/compat/index.js +0 -469
  34. package/src/compat/map.js +0 -282
  35. package/src/compat/resolver.js +0 -22
  36. package/src/compat/vite.js +0 -29
  37. package/src/i18n/components/ActionCard.js +0 -5
  38. package/src/i18n/components/Actionsheet.js +0 -5
  39. package/src/i18n/components/CallCard.js +0 -5
  40. package/src/i18n/components/CallRow.js +0 -5
  41. package/src/i18n/components/Carousel.js +0 -5
  42. package/src/i18n/components/ChartCard.js +0 -5
  43. package/src/i18n/components/ChatAi.js +0 -5
  44. package/src/i18n/components/ChipInput.js +0 -5
  45. package/src/i18n/components/ContactPanel.js +0 -5
  46. package/src/i18n/components/DataGroup.js +0 -5
  47. package/src/i18n/components/DealCard.js +0 -5
  48. package/src/i18n/components/FullscreenDialog.js +0 -5
  49. package/src/i18n/components/Gallery.js +0 -5
  50. package/src/i18n/components/GuestCard.js +0 -5
  51. package/src/i18n/components/ImageUpload.js +0 -5
  52. package/src/i18n/components/ImageViewer.js +0 -5
  53. package/src/i18n/components/IncomingCall.js +0 -5
  54. package/src/i18n/components/InlineEdit.js +0 -5
  55. package/src/i18n/components/IntegrationCard.js +0 -5
  56. package/src/i18n/components/Map.js +0 -5
  57. package/src/i18n/components/NotificationBell.js +0 -5
  58. package/src/i18n/components/PricingCard.js +0 -5
  59. package/src/i18n/components/QiblaCompass.js +0 -5
  60. package/src/i18n/components/RamadanTracker.js +0 -5
  61. package/src/i18n/components/RangeCalendar.js +0 -5
  62. package/src/i18n/components/RangeSlider.js +0 -5
  63. package/src/i18n/components/RichEditor.js +0 -5
  64. package/src/i18n/components/Screensaver.js +0 -5
  65. package/src/i18n/components/Sortable.js +0 -5
  66. package/src/i18n/components/StepDialog.js +0 -5
  67. package/src/i18n/components/StepForm.js +0 -5
  68. package/src/i18n/components/TabTiles.js +0 -5
  69. package/src/i18n/components/Tour.js +0 -5
  70. package/src/i18n/components/TourCard.js +0 -5
  71. package/src/i18n/components/TourPopover.js +0 -5
  72. package/src/i18n/components/TreeSelect.js +0 -5
  73. package/types/compat-resolver.d.ts +0 -12
  74. package/types/compat-vite.d.ts +0 -11
  75. package/types/compat.d.ts +0 -176
package/docs/MIGRATION.md DELETED
@@ -1,455 +0,0 @@
1
- # Migrating from the local glass-ui-kit copies to @ziyarago/ui
2
-
3
- For the two projects that vendor `@ziyarago/glass-ui-kit` 1.32.0 as `src/components/ui/Ui*.vue` (qrbon-saas, tartib-saas): Vite 8, Vue 3.5, Tailwind 4, unplugin-vue-components, vue-i18n 10, lucide-vue-next 1.
4
-
5
- The plan has two phases so that nothing has to be rewritten on day one:
6
-
7
- 1. **Switch imports** to `@ziyarago/ui/compat`. It exports the OLD component names (`UiInput`, `UiTable`, `UiSidebar`, ...) as thin Vue wrappers over the new components, with the old props, slots and events. Delete `src/components/ui` afterwards.
8
- 2. **Refactor to the new names** (`UiTextField`, `UiDataTable`, `UiSideNav`, ...) component by component, and drop the compat wrappers you no longer use.
9
-
10
- General Tailwind/vue-i18n/SSR recipes live in [INTEGRATION.md](./INTEGRATION.md); this file only covers what is specific to the move.
11
-
12
- ## 0. What was inventoried
13
-
14
- | | qrbon-saas | tartib-saas |
15
- |---|---|---|
16
- | local UI SFCs | 42 in 31 folders | 44 (adds `UiConfirmModal`, `MultilangInput`) |
17
- | composables / helpers | `useToast`, `useCommandPalette`, `useTheme`, `dateUtils`, `lib/tones`, `lib/cn` | same |
18
- | pages using `<Ui*>` | 144 | 158 |
19
-
20
- Differences between the two copies: tartib uses i18n keys (`common.*`) for every built-in string, a cobalt (bronze-gold) primary instead of ink, a `variant="ink"` button, `anchorClass` and a body-teleported `UiTooltip`, a text time input in `UiDateTimePicker`, `ConfirmModal`, `MultilangInput`. qrbon has English literals and an ink (black) primary.
21
-
22
- Real usage (tags in templates, both projects together): `UiButton` 976, `UiCard` 644, `UiInput` 541, `UiBadge` 291, `UiAlert` 275, `UiSkeleton` 224, `UiSelect` 173, `UiModal` 172, `UiMenuItem` 112, `UiSwitch` 97, `UiTab` 79, `UiAvatar` 72, `UiDatePicker` 47, `UiTable` 47, `UiDropdown` 45, `UiTextarea` 45, `UiMenu` 42, `UiPagination` 35, `UiCheckbox` 34, `UiProgress` 31, `UiStatsCard` 30, `UiConfirmModal` 24, the rest under 25. Slots: `#icon-left` 645, `#prefix` 180, `#footer` 175, `#header` 123, `#subtitle` 116, `#actions` 84, `#cell-*` ~300, `#trigger` 45, `#empty` 34.
23
-
24
- ## 1. Mapping summary
25
-
26
- 44 local SFCs:
27
-
28
- | bucket | count | components |
29
- |---|---|---|
30
- | **1:1**, same name, same props after a mechanical translation | 14 | Alert, Avatar, Badge, Breadcrumbs, Button, Calendar, Checkbox, Combobox, DatePicker (single date), Progress, Select, Skeleton, Switch, Textarea |
31
- | **Renamed or props reshaped**, the new component renders it | 7 | Card (padding to size), ConfirmModal (to ConfirmDialog), Input (to TextField), Modal (sizes, v-model:open), Pagination (current/total to page/pages), SegmentedControl (options to items), StatsCard (to StatsCard or KpiTile) |
32
- | **Different model** (compound or slot API vs data-driven), re-implemented in compat on top of the kit's CSS and components | 20 | Tabs, TabsList, Tab, TabPanel, Radio, RadioGroup, Dropdown, Menu, MenuItem, MenuSeparator, Table (to DataTable), Sidebar, SidebarItem, SidebarSection, Navbar (to TopNav), SubNav, ToastContainer with `useToast`, CommandPalette with `useCommandPalette`, FileUpload (stateless in the new kit), Tooltip (teleported, rich content) |
33
- | **Missing in the new kit** (exist only in compat) | 3 | AvatarGroup, DateTimePicker (ISO string, date + time), MultilangInput (tartib only) |
34
-
35
- Also: `useTheme` becomes a compat shim over `@ziyarago/ui/theme`; `useToast` and `useCommandPalette` are re-implemented with the old signatures; `lib/tones.ts` and `lib/cn.ts` are Tailwind helpers and stay.
36
-
37
- The published kit (`@ziyarago/glass-ui-kit`, 143 catalog entries): 98 have a same-name component in the new kit (props may differ, check the tables in section 6); 45 do not, see Appendix A.
38
-
39
- ## 2. Install
40
-
41
- ```bash
42
- npm i @ziyarago/ui
43
- # peers you already have: vue >= 3.5. Optional, only when you use them: vue-i18n 10 (the compat layer reads it if present), vue-router 4 (UiSubNav).
44
- ```
45
-
46
- You can later remove `@floating-ui/vue`, `class-variance-authority` and (when no page imports it) `tailwind-merge` from the UI layer; the pages themselves still use `clsx`, `lucide-vue-next` and Tailwind.
47
-
48
- **CSP warning:** icons are fetched at runtime. Under `connect-src 'self'` they disappear unless you self-host them as shown below (`setIconBase`). Do this before shipping to a server with a strict CSP.
49
-
50
- Keep `lucide-vue-next`. The new kit takes icons by NAME (`icon="trash-2"`) and fetches Lucide SVGs from `unpkg.com/lucide-static@0.460.0/icons/` at runtime; the compat layer converts your Lucide components to names for you (`iconName()`), including the 9 icons that lucide-vue-next 1.x renamed (`funnel` to `filter`, `circle-question-mark` to `circle-help`, `text-align-start` to `align-left`, ...). To avoid the CDN (offline, CSP, flash on first paint) self-host once:
51
-
52
- ```bash
53
- npm i -D lucide-static@0.460.0
54
- cp -R node_modules/lucide-static/icons public/lucide # or copy in your build
55
- ```
56
- ```ts
57
- import { setIconBase } from "@ziyarago/ui/vue";
58
- setIconBase("/lucide/");
59
- ```
60
-
61
- ## 3. CSS
62
-
63
- In `src/styles/main.css`, directly after Tailwind, import the kit INTO the `components` layer (so Tailwind utilities such as `w-44`, `p-0`, `h-20`, `bg-red-500` on kit components still win, and the bundled `@font-face` rules stay valid inside the layer; since 0.7 there is no remote `@import` in `style.css`):
64
-
65
- ```css
66
- @import "tailwindcss";
67
- @import "@ziyarago/ui/style.css" layer(components);
68
-
69
- /* class-based dark mode stays as it is */
70
- @custom-variant dark (&:where(.dark, .dark *));
71
- ```
72
-
73
- Verified with Vite 6, Tailwind 4.3 in dev and in a production build. Do not add `class="zg-root"` yet: it sets font, text color and background on the element and would replace your body styles. Add it later if you want the kit's typography.
74
-
75
- ### Making the kit look like your app (optional, 5 lines)
76
-
77
- The kit paints with `--zg-*` variables; your Tailwind theme (`--color-cobalt-*`, `--font-sans`) is untouched. To keep your font and brand:
78
-
79
- ```css
80
- :root {
81
- --zg-font: var(--font-sans); /* Geist instead of Manrope */
82
- --zg-brand: var(--color-cobalt-600); /* tartib: bronze-gold. qrbon: leave the default gold, or see "primary" below */
83
- --zg-brand-hover: var(--color-cobalt-500);
84
- }
85
- ```
86
-
87
- qrbon's primary button is a black pill. The new `primary` is the brand color. Either keep black: `configureCompat({ primary: "inverse" })` (section 5), or accept the gold CTA.
88
-
89
- ## 4. Theme
90
-
91
- | | old (`composables/useTheme.ts`) | new (`@ziyarago/ui/theme`) |
92
- |---|---|---|
93
- | API | `useTheme()` returns `{ theme, setTheme, toggleTheme }` (`theme` is a readonly ref) | `useUiTheme()` returns `{ theme, palette, dir, motion, effects, bg, setTheme, toggleTheme, setPalette, setDir, setLocale, setMotion, setEffects, setBackground, subscribe, snapshot }`, plain getters (not refs) |
94
- | dark mode | class `.dark` on `<html>` + `color-scheme` | attribute `data-theme="dark"|"light"` on `<html>` |
95
- | storage | `localStorage["glass-ui-theme"]` = `"light"|"dark"` | `localStorage["zg-ui"]` = JSON (theme, palette, dir, motion, effects, bg) |
96
- | default | system preference | `dark` |
97
- | palettes | one (the project's `@theme` colors) | `gold` (default, brand `oklch(0.676 0.084 67.4)`) and `tamam` (green) via `data-palette` |
98
- | RTL | `applyBootLocale` sets `dir` itself | `setDir("rtl")` / `setLocale("ar")` sets `dir` |
99
- | flash prevention | none | `themeInitScript` (inline in `<head>`) |
100
-
101
- Compat shim: `useTheme` from `@ziyarago/ui/compat` has the OLD signature, calls `useUiTheme` underneath, sets BOTH `data-theme` and the `.dark` class (so Tailwind `dark:` keeps working), mirrors the value into the old storage key, and on first run adopts `glass-ui-theme` if the new key is empty. Replace the body of the existing file and nothing else changes:
102
-
103
- ```ts
104
- // src/composables/useTheme.ts
105
- export { useTheme } from "@ziyarago/ui/compat";
106
- export type { Theme } from "@ziyarago/ui/compat";
107
- ```
108
-
109
- Pre-paint (optional): `@ziyarago/ui/theme` also exports `themeInitScript`, a string of JavaScript that sets the kit's attributes on `<html>` before the first frame. Inline it in `<head>` (paste it, or inject it with a small Vite `transformIndexHtml` plugin). It only reads `localStorage["zg-ui"]`; your own `.dark` class logic keeps running.
110
-
111
- New API when you are off the shim: `const ui = useUiTheme(); ui.setTheme("light"); ui.setPalette("tamam")`. Subscribe with `ui.subscribe(s => ...)` if you need reactivity; wrap it in a `ref` yourself.
112
-
113
- ## 5. Replace the local components with the compat layer
114
-
115
- `vite.config.ts` (order matters: the redirect plugin runs before `vue()`):
116
-
117
- ```ts
118
- import Components from "unplugin-vue-components/vite";
119
- import { ZiyaragoCompatResolver } from "@ziyarago/ui/compat/resolver";
120
- import { ziyaragoCompat } from "@ziyarago/ui/compat/vite";
121
-
122
- export default defineConfig({
123
- plugins: [
124
- ziyaragoCompat(), // rewrites imports of ".../components/ui/..." to "@ziyarago/ui/compat"
125
- vue(),
126
- tailwindcss(),
127
- Components({
128
- dirs: ["src/components"], // keeps PageHeader, KpiTile and the other app components
129
- dts: "src/components.d.ts",
130
- directoryAsNamespace: false,
131
- extensions: ["vue"],
132
- resolvers: [ZiyaragoCompatResolver()], // <UiInput>, <UiTable>, ... come from compat
133
- }),
134
- ],
135
- });
136
- ```
137
-
138
- - The resolver wins over local files in `dirs` (verified), so you can leave `src/components/ui` in place while you test, then delete it.
139
- - `ziyaragoCompat()` fixes the script imports. In the two projects there are ~290 of them: `import { useToast } from "../../../components/ui/Toast"` (157), `import type { SelectOption } from ".../ui/Select"`, `TableColumn`, `SegmentedOption`, `DateRange`, `useCommandPalette`, `UiDatePicker`/`UiDateTimePicker` from `.../ui/DatePicker`, `import UiInput from ".../ui/Input/UiInput.vue"`. Matching is `components/ui/<Folder>[/<File>]`; `MultilangInput` is excluded (see below). If you prefer to touch the files, replace the paths with `@ziyarago/ui/compat` yourself (all those names are exported).
140
- - Types come with `@ziyarago/ui/compat` (`types/compat.d.ts`): `SelectOption`, `TableColumn<T>`, `TableVariant`, `TableDensity`, `SubNavItem`, `SegmentedOption`, `BreadcrumbItem`, `DateRange`, `Command`, `Toast*`, `MultilangValue`.
141
- - `MultilangInput` (tartib, not part of the published kit) is also exported by compat (a 40-line version: language switcher + `UiTextField`). Keep your local file if you rely on its details: `ziyaragoCompat()` leaves it alone by default.
142
-
143
- `main.ts`: nothing for components (auto-import). Keep `useToast` and `<UiToastContainer />` and `<UiCommandPalette />` in `App.vue` as they are. Optional configuration (before `app.mount`):
144
-
145
- ```ts
146
- import { configureCompat } from "@ziyarago/ui/compat";
147
-
148
- configureCompat({
149
- sizes: "height", // default. Keeps old pixel heights: old md (48px) renders as new lg (48px), old lg (56px) as new xl. "name" maps md to md.
150
- primary: "inverse", // qrbon only: keep the black primary button
151
- warn: true, // dev warnings for props the new kit cannot honour (once per prop)
152
- // t: (key, fallback) => ... // i18n bridge, see below
153
- });
154
- ```
155
-
156
- i18n: with vue-i18n installed (`globalInjection` is on by default) the wrappers translate their own default texts through your keys when the key exists, otherwise they fall back to English. Keys read (the tartib ones, already in its messages): `common.areYouSure`, `common.irreversible`, `common.delete`, `common.cancel` (ConfirmModal), `common.select` (Select placeholder), `common.noMatches` (Combobox), `common.noData` (Table, new key), `common.dropFilesOrBrowse` (FileUpload), `common.prevPage`, `common.nextPage` (Pagination), `common.datePlaceholder`, `common.selectDates` (new key, range picker). Date pickers also take their locale from `$i18n.locale` when no `locale` prop is given. Not covered because the new components render the text themselves: aria-labels of the Modal/Alert close button ("Close"), calendar arrows, FileUpload reject texts, Spinner ("Loading").
157
-
158
- ### After the build is green
159
-
160
- Delete `src/components/ui/*` (keep `MultilangInput/` in tartib if you use it) and the `@ziyarago/ui/compat/vite` plugin once every import is rewritten.
161
-
162
- ## 6. Prop-level mapping
163
-
164
- "compat" = what `@ziyarago/ui/compat` does for you automatically. "new" = what to write when you use the new component directly (phase 2).
165
-
166
- Size tables, used everywhere below:
167
-
168
- | old control size (`UiButton`, `UiInput`, `UiSelect`, `UiCombobox`, `UiDatePicker`) | px | compat, `sizes: "height"` | compat, `sizes: "name"` | new component size for the same height |
169
- |---|---|---|---|---|
170
- | sm | 36 | md (40) | sm | md |
171
- | md | 48 | lg (48) | md | lg |
172
- | lg (old default) | 56 | xl (56) | lg | xl |
173
- | xl | 64 | xl (56) | xl | xl |
174
-
175
- New scale: xs 28, sm 32, md 40, lg 48, xl 56 px. Components that are not height-based keep the same names (`Switch`, `Checkbox`, `Radio`, `Textarea`, `Avatar`, `Skeleton`, `Progress`), except `Badge` and `SegmentedControl` (old sm/md/lg become new md/lg/xl in height mode).
176
-
177
- ### UiButton
178
-
179
- | old | compat and new | note |
180
- |---|---|---|
181
- | `variant` primary / glass / ghost / danger | primary / secondary / ghost / danger | `configureCompat({ primary: "inverse" })` renders primary as `inverse` |
182
- | `variant="gradient"` | `variant="tonal" tone="brand"` | |
183
- | `variant="ink"` (tartib) | `variant="inverse"` | |
184
- | `size` md / lg (default) / xl | see size table | |
185
- | `iconOnly`, `block`, `loading`, `disabled` | same (`iconOnly` is a new core prop: square, width = height) | |
186
- | `type` button / submit / reset | `type` (new core prop; the new Button used to be always `type="button"`) | |
187
- | `title` | `title` (new core prop) | |
188
- | slot `icon-left` / `icon-right` | compat renders them around the label inside the default slot, hidden while `loading`. New: `icon="trash-2"` / `iconRight="arrow-right"` (string names) | |
189
- | slot `loading` | not supported (new shows a spinner in place of the icon) | |
190
- | event `click` | same, not fired while disabled or loading | |
191
-
192
- ### UiInput to UiTextField
193
-
194
- | old | compat and new | note |
195
- |---|---|---|
196
- | `modelValue`, `label`, `hint`, `placeholder`, `disabled`, `readonly`, `type` | same | |
197
- | `error` string \| boolean | `error` string. `true` shows the red state without a message | |
198
- | `size` sm md lg xl (default lg) | see size table | |
199
- | `required` | label asterisk + native `required` | |
200
- | `autocomplete`, `name`, `maxlength`, `minlength`, `min`, `max`, `step`, `inputmode`, `pattern`, `spellcheck`, `autofocus`, `autocapitalize` | forwarded to the inner `<input>` (new core feature) | |
201
- | `id` | lands on the root `<label>`, not on the input | |
202
- | `help` | `hint` (it was silently ignored by the old kit: tartib `NotificationConfigPage` passes it, so the text appears now) | |
203
- | `clearable` | unsupported (the old kit ignored it too) | |
204
- | slot `prefix` / `suffix` | same (new core slots; new also has `icon="search"` and the `suffix` prop) | |
205
- | `@update:model-value` | `v-model` works; `@change` now receives the string, not a DOM event | |
206
- | `@input`, `@blur`, `@focus`, `@keydown.enter` | forwarded to the input element. `@input` receives the native `Event` | |
207
-
208
- ### UiTextarea
209
-
210
- `modelValue`, `label`, `hint`, `error`, `rows`, `placeholder`, `disabled`, `required`, `readonly`, `maxlength`, `name` same. `size` md/lg/xl same names. `autoResize` same (CSS `field-sizing: content`, Chromium and Safari). `showCount` same (shows `n / maxlength`). `resize`: old default false, new default true; compat passes `resize: false` unless you set it. `@blur`, `@keydown` forwarded to the `<textarea>`.
211
-
212
- ### UiSelect, UiCombobox
213
-
214
- | old | compat and new | note |
215
- |---|---|---|
216
- | `modelValue`, `options` (`{label, value, disabled}`), `label`, `disabled` | same | option `value: null` works (shows as a normal option) |
217
- | `hint`, `error`, `required`, `name` | same (new core props; `name` renders a hidden input) | |
218
- | `size` md lg xl (default lg) | see size table | |
219
- | `placeholder` (default "Select an option") | default from `common.select` or English | |
220
- | Combobox `clearable`, `filter(option, query)`, `emptyText` | same (new core props; `emptyText` default from `common.noMatches`) | |
221
- | `id` | root element | |
222
- | events | `update:modelValue` and `change(value)` | |
223
-
224
- ### UiCheckbox, UiSwitch, UiRadio, UiRadioGroup
225
-
226
- | old | compat and new |
227
- |---|---|
228
- | Checkbox `modelValue`, `label`, `disabled`, `size`, `name`, `value` | same (`indeterminate`, `hint`, `error` are new core props) |
229
- | Switch `modelValue`, `label`, `hint`, `error`, `disabled`, `size`, `labelLeft` | same (`hint`, `error`, `labelLeft` are new core props) |
230
- | `<UiRadioGroup v-model>` + `<UiRadio :value label hint disabled>` children | compat reads the children and renders the new `RadioGroup`. New: `<UiRadioGroup v-model="x" :options="[{ value, label, hint, disabled }]" />` |
231
- | RadioGroup `label`, `hint`, `error`, `size`, `disabled`, `orientation` | same (`hint`, `error`, `orientation` are new core props) |
232
- | standalone `<UiRadio v-model :value>` (no group) | one-option group each |
233
-
234
- ### UiBadge, UiAlert, UiAvatar, UiAvatarGroup, UiProgress, UiSkeleton
235
-
236
- | old | compat result |
237
- |---|---|
238
- | Badge `tone` sunshine (default) / cobalt | `brand` |
239
- | Badge `tone` petrol | `info` |
240
- | Badge `tone` ink | `inverse` (new tone) |
241
- | Badge `tone` glass / neutral | neutral (no tone) |
242
- | Badge `tone` success warning danger info | same |
243
- | Badge `size` sm md lg | md lg xl in height mode (24/28/32 px), sm md lg in name mode |
244
- | Badge `shape="square"` | `shape="square"` (new core prop); `dot` same; slots `icon-left` / `icon-right` rendered around the label |
245
- | Alert `tone` success warning danger info | same; `glass` and `neutral` render as `info` |
246
- | Alert `title`, `hideIcon`, slots `title` / `icon` / `actions` | same (new core props and slots) |
247
- | Alert `closable` | close button, event `close` (compat attaches the handler only when `closable`). New: handle `@close` yourself (the button exists only when a handler is set). `dismissible` / `@dismiss` were never implemented in the old kit and are ignored |
248
- | Avatar `src`, `size` xs..xl | same (px differ: new 24/32/40/56/72) |
249
- | Avatar `initials` | `initials` (new core prop) |
250
- | Avatar `alt` | `name` |
251
- | Avatar `tone` cobalt / sunshine / petrol / pink / ink | brand / warning / sky / danger / neutral (new core prop `tone`) |
252
- | Avatar `shape="squircle"` | `shape="rounded"`; `ring`, `status` online/offline/away/busy are new core props |
253
- | AvatarGroup `spacing`, `reverse` | compat only (`.zg-avgroup`) |
254
- | Progress `value`, `max`, `label`, `showValue`, `size`, `tone` | value normalised to 0-100 using `max`; `showValue` becomes `meta="NN%"`; tone success warning danger same, sunshine = warning, ink cobalt petrol = brand. Indeterminate (no `value`) shows 0% |
255
- | Skeleton `variant` text rect circle line, `size`, `lines` | same; `line` = 1 px rect; `lastLineWidth` dropped (last line is 60%); a Tailwind `h-*` class on a rect makes it fill the wrapper |
256
-
257
- ### UiCard, UiModal, UiConfirmModal
258
-
259
- | old | compat and new |
260
- |---|---|
261
- | Card `variant` glass / solid | default glass card |
262
- | Card `variant` outline | `variant="outline"` (new core) |
263
- | Card `variant` gradient | `variant="gradient"` |
264
- | Card `variant="block"` + `tone` cobalt / sunshine / petrol / ink | `variant="gradient" intensity="vivid"` + tone brand / warning / sky / neutral |
265
- | Card `padding` none / sm / md / lg (default) / xl | `padding="none"` (new core) or `size`: sm to md, md to lg, lg to lg, xl to xl (name mode: same names) |
266
- | Card `interactive`, `@click` | same (new core: pointer, hover lift, Enter key) |
267
- | Card slots `header` / `footer` | same (new core slots) |
268
- | Card `as` | unsupported |
269
- | Modal `open` + `v-model:open`, `title`, slots `title` / `subtitle` / `default` / `footer` | same (`subtitle`, `title` slot are new core). New: `:open` + `@close` (there is no `update:open` event) |
270
- | Modal `size` sm md lg xl 2xl 3xl 4xl 5xl | sm md lg xl xl; 3xl 4xl 5xl add `width` 768 / 896 / 1024 px (new core prop `width`) |
271
- | Modal `closable`, `closeOnBackdrop`, `closeOnEsc` | same (new core props) |
272
- | Modal events `close` and `update:open` | both emitted on close button, backdrop, Escape |
273
- | ConfirmModal `open`, `title`, `message`, `confirmLabel`, `confirmVariant` danger / primary / glass, `loading`, events `confirm` / `cancel`, default slot | `ConfirmDialog` `open`, `title`, `description`, `confirmLabel`, `tone` danger / default, `loading`, `@confirm`, `@cancel`; the body slot is accepted (new core). Defaults from `common.*` keys |
274
-
275
- ### UiTooltip, UiDropdown, UiMenu, UiMenuItem, UiMenuSeparator
276
-
277
- | old | compat result |
278
- |---|---|
279
- | Tooltip `text`, `placement`, `tone` ink/glass, `delay`, `disabled`, `anchorClass`, slot `content` | all supported by the compat tooltip (teleported to body, so `overflow:hidden` parents never clip it). The NEW `UiTooltip` is a CSS bubble (`placement` added, no rich content, no delay) |
280
- | `<UiDropdown placement offset v-model:open>` + `#trigger="{ isOpen, toggle }"` + default slot with `<UiMenu>` | compat: trigger wrapper, body-teleported panel, flips and clamps to the viewport, outside click and Escape close. New: `<UiDropdown label icon :items="[{ label, value, icon, tone: 'danger', onClick, separator }]" @select>` |
281
- | `UiMenu` `minWidth`, `autofocus`; `UiMenuItem` `tone` danger, `disabled`, `closeOnClick`, slots `icon-left` / `icon-right` / `shortcut`; `UiMenuSeparator` `label` | compat components (arrow keys, Home, End). `loading` on a menu item is ignored |
282
-
283
- ### UiTable to UiDataTable
284
-
285
- | old | compat | new |
286
- |---|---|---|
287
- | `rows`, `columns` `{ key, label, accessor?, sortable?, align?, width? }`, `rowKey(row, i)` | same | `rows` need an `id` field (compat generates it from `rowKey` or the index); columns `{ key, label, sortable, num, align, width, render(value, row, h) }` |
288
- | client-side sorting on click | yes (compat sorts) | controlled: handle `@sort(key, dir)` and pass `sortKey`/`sortDir` |
289
- | slots `cell-<key>` `{ row, value, index }`, `header-<key>` `{ column }`, `empty` | same | `render` per column; `empty` slot (new core) or `empty` text |
290
- | `loading` + `skeletonRows` | skeleton rows | not in the new component |
291
- | `@row-click(row, index)` | same | `@rowClick(row, index)` (new core event) |
292
- | `density` comfortable / compact | `size` md / sm | `size` |
293
- | `variant` default / striped / bordered, `hoverable` | ignored | rows always hover |
294
- | `width` Tailwind class (`w-32`, `w-[120px]`, `w-1/2`, `w-full`) | converted to CSS width | CSS width string |
295
-
296
- ### UiTabs family, UiPagination, UiBreadcrumbs, UiSegmentedControl, UiSubNav
297
-
298
- | old | compat result | new |
299
- |---|---|---|
300
- | `UiTabs v-model variant` + `UiTabsList` + `UiTab value disabled` + `UiTabPanel value` | compound components kept (pill = kit pill tabs, underline = kit tabs) | `<UiTabs v-model :items="[{ value, label }]">` renders only the strip; show the panel yourself with `v-if` |
301
- | `UiPagination current total siblings hideArrows`, `v-model:current` | `page`, `pages`, `siblings`, `hideArrows` (new core), event `update:current` | `:page="p" :pages="n" @change="p = $event"`; aria labels via `labels` (new core) |
302
- | `UiBreadcrumbs items separator` chevron / slash / dot | same (`separator` is new core) | same |
303
- | `UiSegmentedControl v-model options size block iconOnly ariaLabel` | `options` to `items`; Lucide icons to names; `block`, `iconOnly`, `ariaLabel`, per-item `disabled` / `icon` (new core) | `items` |
304
- | `UiSubNav items { path, label, icon, badge } title titleIcon matchExact` | compat: route-aware (`$route`, `$router.push`) | new `UiSubNav` is value based: `:items="[{ value, label, icon, badge }]" :value @change` |
305
-
306
- ### UiSidebar, UiNavbar
307
-
308
- | old | compat | new |
309
- |---|---|---|
310
- | `UiSidebar v-model:collapsed v-model:open defaultCollapsed storageKey`, slots `header` / `footer` with `{ collapsed }`, `UiSidebarSection label`, `UiSidebarItem icon label active badge href disabled` | same API on the kit's sidebar look, always dark like the old one (`--sidebar-bg` is honoured), mobile drawer below 1024 px, `widthExpanded` / `widthCollapsed` Tailwind classes ignored | `UiSideNav :brand :items="[{ group }, { value, label, icon, count }]" :value :collapsed @change @toggle` plus the `footer` slot, or `UiAppShell` |
311
- | `UiNavbar variant size sticky`, slots `left` / `center` / `right` | same slots on `.zg-topnav` (variant ignored, sticky off by default like the old one) | `UiTopNav :brand :items :value @change` plus the `actions` slot |
312
-
313
- ### UiDatePicker, UiCalendar, UiDateTimePicker, UiFileUpload, UiStatsCard, UiCommandPalette, toasts
314
-
315
- | old | compat result |
316
- |---|---|
317
- | DatePicker `modelValue` Date, `label`, `placeholder`, `hint`, `error`, `disabled`, `required`, `size`, `locale`, `weekStartsOn` | same; `locale` defaults to the host `$i18n.locale` |
318
- | DatePicker `clearable` (old default true) | `clearable` (new core); compat default true |
319
- | DatePicker `min`, `max`, `disabledDate` | same (new core props on Calendar and DatePicker) |
320
- | DatePicker `range` + `DateRange { start, end }` model | compat range picker (new `RangeCalendar` in a popup); not in the new `DatePicker` |
321
- | Calendar `range`, `initialMonth` | unsupported |
322
- | DateTimePicker `modelValue` ISO string (UTC), `defaultTime`, `label`, `hint`, `error`, `disabled` | compat: date picker + text time field (`HH:MM`, auto colon), emits `update:modelValue` with an ISO string or `null` |
323
- | FileUpload `modelValue` File[], `multiple`, `accept`, `maxSize`, `label`, `hint`, `error`, `@reject({ file, cause })`, slot `title` | compat keeps the file list (chips with remove) and the old `reject` shape; the new `FileUpload` is stateless and calls `onReject(file, reason)` |
324
- | StatsCard `label value icon delta { value, trend, sublabel }` | `StatsCard` (`delta` string + `trend` up/down; `flat` renders as up). `variant="block"` renders `KpiTile` (brand, or inverse for `tone="ink"`; the icon is dropped) |
325
- | CommandPalette + `useCommandPalette()` `register` `unregister` `clear` `open` `close` `toggle` | same API; ⌘/Ctrl+K handled by the compat component; `group`, `description` are folded into the search text (no group headings, no description line) |
326
- | `useToast()` `push` `success` `info` `warning` `danger` `dismiss` `clear` + `<UiToastContainer position>` | same API and positions; `tone: "default"` renders as info |
327
-
328
- ## 7. Behaviour differences
329
-
330
- Look and feel (by design of the new kit):
331
-
332
- 1. Inputs, buttons and selects are pills; surfaces are glass; the default font is Manrope (override `--zg-font`). Sizes follow the new scale, see section 6.
333
- 2. Primary buttons use `--zg-brand`; tartib's cobalt maps to the gold brand, qrbon's black primary needs `primary: "inverse"`.
334
- 3. Pill tabs are rectangular brand-filled tabs, not a black capsule. Active nav items are inverse (white on dark) pills.
335
- 4. Avatars default to the brand color; the 5 old tones map to brand, warning, sky, danger, neutral.
336
-
337
- Mechanics:
338
-
339
- 5. Icons are strings loaded from a CDN, so an icon can appear a moment after first paint; self-host with `setIconBase` (section 2).
340
- 6. Vue "bare boolean" attributes (`<UiButton block loading>`) used to work only because the old components declared real props. The new adapter now turns `''` into `true` for every documented boolean prop, and kebab-case attributes (`icon-only`) are camelCased. `modelValue=""` and other text props are left alone.
341
- 7. `class`, `style`, `id`, `aria-*` and `data-*` now land on the root element of every new component (needed for `class="w-44"` and `class="!size-12"`). `id` on an input goes to the root `<label>`.
342
- 8. Popups: `Select`, `Combobox`, `DatePicker` lists are positioned `absolute` inside the component (the old kit used floating-ui). Inside a scrolling `Modal` body the list scrolls into view instead of floating above it. The compat `Dropdown` and `Tooltip` are teleported and never clipped; the new `Dropdown`/`Tooltip` are not.
343
- 9. `change` on every new component is emitted with the value; `@change` handlers that expected a DOM event break (none found in the two projects).
344
- 10. Emitted payloads: `DatePicker` and `Calendar` give a `Date`; `TimePicker` gives `HH:mm`.
345
- 11. The new `Modal` unmounts its content when closed (state inside is lost on close, same as the old `v-if`).
346
- 12. Built-in English strings (see section 5 for what the bridge translates). RTL works through `dir="rtl"`.
347
- 13. Dropdown/Menu: keyboard (arrows, Home, End, Escape) kept; no arrow/shift middleware, the panel is clamped to the viewport.
348
- 14. `UiAlert` `closable` shows the close button only because compat attaches `onClose` for you; with the new `Alert` no handler means no button.
349
- 15. Toasts have no enter/leave animation; the stack is rendered by compat (`UiToastContainer`), the new `UiToast` is a static card.
350
-
351
- ## 8. What to test
352
-
353
- Compat smoke (render once, no console errors): the 29 kit demo pages in qrbon `src/pages/*Page.vue` (one per component) render through compat without console errors; keep them as a visual regression route while migrating.
354
-
355
- Manual checklist (each item has a real counterpart in the two apps):
356
-
357
- - [ ] Light and dark toggle, reload keeps the choice (`useTheme`, old key adopted once).
358
- - [ ] App shell: sidebar rail, collapse persists (`storage-key`), mobile drawer (< 1024 px, scrim, close button), navbar dropdowns (language, user menu open above/below, close on outside click and Escape), ⌘K palette, toast stack and action buttons.
359
- - [ ] Tables: sort, loading skeleton, empty slot, `cell-*` slots with `value`, row click, right-aligned numeric columns, width classes.
360
- - [ ] Forms: Input with prefix slot and `v-model.number`, required asterisk, error and hint text, Select inside a Modal (opens and scrolls into view), Switch with hint, Textarea counter, DatePicker min/max/clear, DateTimePicker ISO value round trip across a timezone change.
361
- - [ ] Modal sizes 3xl-5xl, `#subtitle`, Esc and backdrop close, `ConfirmModal` with `loading`.
362
- - [ ] Buttons: `icon-only` square, `loading` hides icons, `type="submit"` inside a form, `class="w-full"`.
363
- - [ ] Avatar status dots, rings, `!size-12` overrides.
364
- - [ ] RTL (`dir="rtl"`) for a table page and the shell.
365
- - [ ] Production build: `vite build` and look for `@import` order in the emitted CSS (fonts first, then the layered kit).
366
-
367
- Automated: `node --test tests/unit/compat.test.mjs` in the kit repo covers the mapping tables (sizes, tones, variants, events, slots, i18n fallbacks) and renders the main compat components on the server.
368
-
369
- ## 9. Codemod checklist (phase 2)
370
-
371
- Run on copies or in a clean working tree; each command is idempotent. All use perl (BSD and GNU).
372
-
373
- 1. Tags that are pure renames (props are mechanically the same after step 3):
374
-
375
- ```bash
376
- find src -name '*.vue' -not -path 'src/components/ui/*' -print0 | xargs -0 perl -pi -e '
377
- s/<(\/?)UiInput(?=[\s>\/])/<$1UiTextField/g;
378
- s/<(\/?)UiConfirmModal(?=[\s>\/])/<$1UiConfirmDialog/g;
379
- s/<(\/?)UiKpiCard(?=[\s>\/])/<$1UiKpiTile/g;
380
- s/<(\/?)UiOtpInput(?=[\s>\/])/<$1UiCodeInput/g;'
381
- ```
382
-
383
- Then import those names from `@ziyarago/ui/vue` (the resolver `ZiyaragoUiResolver` from `@ziyarago/ui/resolver` does it for auto-import; put it AFTER `ZiyaragoCompatResolver` and add `exclude` for the names you still take from compat).
384
-
385
- 2. Shift sizes to the new scale on components you moved off compat (static `size="..."` only; the pattern tolerates `=>` inside other attributes):
386
-
387
- ```bash
388
- find src -name '*.vue' -not -path 'src/components/ui/*' -print0 | xargs -0 perl -0777 -pi -e '
389
- s{(<Ui(?:Button|TextField|Select|Combobox|DatePicker)\b(?:[^>]|=>)*?\ssize=")(xl|lg|md|sm)(")}{$1.{xl=>"xl",lg=>"xl",md=>"lg",sm=>"md"}->{$2}.$3}gse'
390
- ```
391
-
392
- Tags without a `size` attribute used the old default (`lg`, 56 px) and now get `md` (40 px): list them and add `size="xl"` where the height matters:
393
-
394
- ```bash
395
- grep -rnE '<Ui(Button|TextField|Select|Combobox)\b' src --include='*.vue' | grep -v 'size=' # single-line tags only
396
- ```
397
-
398
- 3. Props and events:
399
-
400
- ```bash
401
- # ConfirmDialog: message -> description, confirm-variant -> tone
402
- perl -0777 -pi -e 's{(<UiConfirmDialog\b[^>]*?)\smessage=}{$1 description=}g; s{(<UiConfirmDialog\b[^>]*?)\sconfirm-variant="danger"}{$1 tone="danger"}g; s{(<UiConfirmDialog\b[^>]*?)\sconfirm-variant="[a-z]+"}{$1}g' $(find src -name '*.vue')
403
- # Pagination: current/total/v-model:current -> page/pages/@change
404
- perl -pi -e 's/v-model:current="([^"]+)"/:page="$1" \@change="$1 = \$event"/; s/(<UiPagination\b[^>]*?)\s:total=/$1 :pages=/' $(find src -name '*.vue')
405
- # Card padding -> size (xl stays xl, lg stays lg; none needs padding="none")
406
- perl -pi -e 's/(<UiCard\b[^>]*?)\spadding="(sm|md|lg|xl)"/$1 size="$2"/' $(find src -name '*.vue')
407
- # Modal: v-model:open -> :open + @close (check each one: handlers that did more than close need a manual change)
408
- perl -pi -e 's/(<UiModal\b[^>]*?)\sv-model:open="([^"]+)"/$1 :open="$2" \@close="$2 = false"/' $(find src -name '*.vue')
409
- # Avatar: initials -> name is NOT a rename (initials is kept), tone cobalt|sunshine|petrol|pink|ink
410
- perl -pi -e 's/(<UiAvatar\b[^>]*?)\stone="cobalt"/$1 tone="brand"/; s/(<UiAvatar\b[^>]*?)\stone="sunshine"/$1 tone="warning"/; s/(<UiAvatar\b[^>]*?)\stone="petrol"/$1 tone="sky"/; s/(<UiAvatar\b[^>]*?)\stone="pink"/$1 tone="danger"/; s/(<UiAvatar\b[^>]*?)\stone="ink"/$1 tone="neutral"/; s/(<UiAvatar\b[^>]*?)\sshape="squircle"/$1 shape="rounded"/' $(find src -name '*.vue')
411
- # Badge tones
412
- perl -pi -e 's/(<UiBadge\b[^>]*?)\stone="(sunshine|cobalt)"/$1 tone="brand"/; s/(<UiBadge\b[^>]*?)\stone="petrol"/$1 tone="info"/; s/(<UiBadge\b[^>]*?)\stone="ink"/$1 tone="inverse"/; s/(<UiBadge\b[^>]*?)\stone="(glass|neutral)"/$1/' $(find src -name '*.vue')
413
- ```
414
-
415
- Single-line tags only; multi-line tags need `perl -0777` with `[^>]*?` (as in step 2). Review the diff: `[^>]` stops at the first `>` inside an attribute value.
416
-
417
- 4. Things that need a hand (grep lists):
418
-
419
- ```bash
420
- grep -rnE '#icon-(left|right)' src --include='*.vue' | wc -l # Button/Badge/MenuItem icon slots: keep through compat, or move to icon="trash-2" (string name)
421
- grep -rnE '<UiTable\b|#cell-' src --include='*.vue' -l # move to UiDataTable columns[].render
422
- grep -rnE '<UiTabs\b|<UiTabsList\b|<UiTab\b' src --include='*.vue' -l
423
- grep -rnE '<UiRadio(Group)?\b' src --include='*.vue' -l
424
- grep -rnE '<UiDropdown\b|<UiMenu\b' src --include='*.vue' -l # trigger slot -> items[] when the menu is simple
425
- grep -rnE '<UiSidebar|<UiNavbar|<UiSubNav' src --include='*.vue' -l
426
- grep -rn 'components/ui/' src --include='*.vue' --include='*.ts' | grep import # imports the compat plugin rewrites
427
- ```
428
-
429
- 5. Cleanup: when `grep -rn "@ziyarago/ui/compat"` shows nothing, remove the resolver and the Vite plugin, `rm -r src/components/ui` (MultilangInput excepted), and the `@floating-ui/*`, `class-variance-authority` dependencies.
430
-
431
- ## Appendix A. Published kit components without a same-name component in the new kit
432
-
433
- DashboardLayout (to `AppShell`), Form (to `FormField`, `LoginForm`, or a plain `<form>`; compat has `UiForm`), Input (to `TextField`), Navbar (to `TopNav`), Page (to `PageHeader`, `SplitLayout`), Radio (to `RadioGroup`), OtpInput (to `CodeInput`), Sidebar (to `SideNav`), KpiCard (to `KpiTile`), Table (to `DataTable`), RichTextEditor (to `RichEditor`), Kanban (to `PipelineBoard`, `KanbanColumn`), PillTab (to `PillTabs`), ActivityTimeline (to `Timeline`), Chart (to `MiniChart`, `ChartCard`), AuthPages (to `PageAuth`), CrmDashboard (to `PageCrm`), TourBiDashboard and AdminBiDashboard (to `PageDashboard`), Icons (to `Icon`), ChatBubble and ConversationList (to `ChatAi`).
434
-
435
- No equivalent (not used by the two apps): ChunkCard, the AI group (ModelSelector, AiSettingsPanel, AiChatBox, PromptInput, StreamingMessage, RagSourcesList, AiFeedback, TokenCounter, AiCostBadge, AgentTaskCard, McpToolBadge), Restaurants (OrderCard, ServiceTimeline, TableMap, MenuCategoryGrid, KitchenDisplay, OrderStream), Logistics (ShipmentCard, DeliveryTimeline, RouteMap, VehicleStatus, WarehouseStock).
436
-
437
- ## Appendix B. What the compat package exports
438
-
439
- Components: `UiAlert UiAvatar UiAvatarGroup UiBadge UiBanner UiBreadcrumbs UiButton UiCalendar UiCard UiCheckbox UiCombobox UiCommandPalette UiConfirmModal UiDatePicker UiDateTimePicker UiDivider UiDrawer UiDropdown UiEmptyState UiFileUpload UiForm UiFormField UiInput UiKpiCard UiMenu UiMenuItem UiMenuSeparator UiModal UiMultilangInput (also MultilangInput) UiNavbar UiOtpInput UiPagination UiProgress UiRadio UiRadioGroup UiSegmentedControl UiSelect UiSidebar UiSidebarItem UiSidebarSection UiSkeleton UiSpinner UiStatsCard UiSubNav UiSwitch UiTab UiTabPanel UiTable UiTabs UiTabsList UiTag UiTextarea UiToastContainer UiTooltip`.
440
-
441
- Functions: `useToast`, `useCommandPalette`, `useTheme`, `configureCompat`, `iconName`, `install` (default export, `app.use(compat)` registers every component globally), plus `translate`, `SPECS`, `COMPAT_NAMES` for tooling. `@ziyarago/ui/compat/resolver` and `@ziyarago/ui/compat/vite` for the build.
442
-
443
- ## Appendix C. Known gaps and effort
444
-
445
- | gap | now | effort to close in the kit |
446
- |---|---|---|
447
- | Built-in strings are English (Modal/Alert close, calendar arrows, Spinner, FileUpload) | compat bridge covers placeholders, empty texts, pagination and confirm labels | 0.5 to 1 day: a `labels` prop on about 12 components or a global `setLabels()` |
448
- | Select, Combobox, DatePicker lists are not teleported (clipped by `overflow:hidden` ancestors) | list scrolls into view inside a Modal | about 1 day: render the list through the adapters' `portal` with fixed positioning, flip, tests |
449
- | Table: no striped/bordered/hoverable, built-in sorting and loading | compat sorts and shows skeletons | 0.5 day for core `loading`, `striped`, optional client sort |
450
- | Toast manager (stack, timers, actions) | compat `useToast` + container | 1 day for a core `Toaster` |
451
- | Command palette groups and descriptions | folded into search text | 0.5 day |
452
- | `AvatarGroup`, `DateTimePicker`, `MultilangInput`, compound Tabs/Radio/Dropdown | compat only | AvatarGroup 0.5 h, DateTimePicker 2 h, MultilangInput 2 h, compound APIs not planned |
453
- | Icons by name from a CDN | `setIconBase` self-hosting | 1 to 2 days to bundle an SVG sprite |
454
- | Calendar range / `initialMonth`, Progress indeterminate, Skeleton `lastLineWidth`, Alert `neutral` / `glass` | ignored or mapped to the nearest | 1 to 2 h each |
455
- | React compat | none: the old kit was Vue-only | not needed |