orio-ui 1.27.0 → 1.28.1

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 (72) hide show
  1. package/README.md +76 -1
  2. package/bin/orio-ui.mjs +72 -0
  3. package/dist/agents/ROUTING.md +140 -0
  4. package/dist/agents/component-finder.md +142 -0
  5. package/dist/agents/component-worker.md +152 -0
  6. package/dist/agents/snippet.md +6 -0
  7. package/dist/module.json +1 -1
  8. package/dist/runtime/components/AnimatedContainer.USAGE.md +79 -0
  9. package/dist/runtime/components/Badge.USAGE.md +75 -0
  10. package/dist/runtime/components/Banner.USAGE.md +52 -0
  11. package/dist/runtime/components/Button.USAGE.md +83 -0
  12. package/dist/runtime/components/Button.d.vue.ts +1 -0
  13. package/dist/runtime/components/Button.vue +5 -1
  14. package/dist/runtime/components/Button.vue.d.ts +1 -0
  15. package/dist/runtime/components/Calendar.USAGE.md +8 -0
  16. package/dist/runtime/components/Canvas/USAGE.md +8 -0
  17. package/dist/runtime/components/CheckBox.USAGE.md +63 -0
  18. package/dist/runtime/components/CheckboxGroup.USAGE.md +95 -0
  19. package/dist/runtime/components/ControlElement.USAGE.md +8 -0
  20. package/dist/runtime/components/ControlElement.d.vue.ts +1 -1
  21. package/dist/runtime/components/ControlElement.vue.d.ts +1 -1
  22. package/dist/runtime/components/DashedContainer.USAGE.md +65 -0
  23. package/dist/runtime/components/EmptyState.USAGE.md +65 -0
  24. package/dist/runtime/components/Form.USAGE.md +102 -0
  25. package/dist/runtime/components/Icon.USAGE.md +61 -0
  26. package/dist/runtime/components/Input.USAGE.md +8 -0
  27. package/dist/runtime/components/ListItem.USAGE.md +84 -0
  28. package/dist/runtime/components/LoadingSpinner.USAGE.md +50 -0
  29. package/dist/runtime/components/LocaleSwitcher.USAGE.md +73 -0
  30. package/dist/runtime/components/Modal.USAGE.md +8 -0
  31. package/dist/runtime/components/NavButton.USAGE.md +80 -0
  32. package/dist/runtime/components/NumberInput/Horizontal.USAGE.md +61 -0
  33. package/dist/runtime/components/NumberInput/Horizontal.vue +5 -0
  34. package/dist/runtime/components/NumberInput/USAGE.md +74 -0
  35. package/dist/runtime/components/NumberInput/Vertical.USAGE.md +55 -0
  36. package/dist/runtime/components/NumberInput/Vertical.vue +9 -1
  37. package/dist/runtime/components/Popover.USAGE.md +103 -0
  38. package/dist/runtime/components/RadioButton.USAGE.md +72 -0
  39. package/dist/runtime/components/Selector.USAGE.md +131 -0
  40. package/dist/runtime/components/SwitchButton.USAGE.md +62 -0
  41. package/dist/runtime/components/Tag.USAGE.md +51 -0
  42. package/dist/runtime/components/TaggableSelector.USAGE.md +73 -0
  43. package/dist/runtime/components/Textarea.USAGE.md +72 -0
  44. package/dist/runtime/components/Tooltip.USAGE.md +84 -0
  45. package/dist/runtime/components/ZoomableContainer.USAGE.md +108 -0
  46. package/dist/runtime/components/date/Picker.USAGE.md +8 -0
  47. package/dist/runtime/components/date/PickerTrigger.USAGE.md +65 -0
  48. package/dist/runtime/components/date/RangePicker.USAGE.md +97 -0
  49. package/dist/runtime/components/gallery/Carousel.USAGE.md +98 -0
  50. package/dist/runtime/components/gallery/CarouselPreview.USAGE.md +51 -0
  51. package/dist/runtime/components/upload/USAGE.md +91 -0
  52. package/dist/runtime/components/view/Dates.USAGE.md +67 -0
  53. package/dist/runtime/components/view/KeyBinds.USAGE.md +58 -0
  54. package/dist/runtime/components/view/Separator.USAGE.md +57 -0
  55. package/dist/runtime/components/view/Text.USAGE.md +68 -0
  56. package/dist/runtime/composables/useApi.USAGE.md +64 -0
  57. package/dist/runtime/composables/useControlSize.USAGE.md +73 -0
  58. package/dist/runtime/composables/useControlSize.js +12 -0
  59. package/dist/runtime/composables/useDecimalFormatter.USAGE.md +72 -0
  60. package/dist/runtime/composables/useFilter.USAGE.md +120 -0
  61. package/dist/runtime/composables/useFuzzySearch.USAGE.md +68 -0
  62. package/dist/runtime/composables/useInertia.USAGE.md +80 -0
  63. package/dist/runtime/composables/useListKeyboard.USAGE.md +97 -0
  64. package/dist/runtime/composables/useModal.USAGE.md +82 -0
  65. package/dist/runtime/composables/usePinchZoom.USAGE.md +95 -0
  66. package/dist/runtime/composables/usePressAndHold.USAGE.md +70 -0
  67. package/dist/runtime/composables/useRovingGrid.USAGE.md +106 -0
  68. package/dist/runtime/composables/useSound.USAGE.md +74 -0
  69. package/dist/runtime/composables/useTheme.USAGE.md +76 -0
  70. package/dist/runtime/composables/useUrlSync.USAGE.md +91 -0
  71. package/dist/runtime/composables/useValidation.USAGE.md +100 -0
  72. package/package.json +12 -2
@@ -0,0 +1,91 @@
1
+ ---
2
+ kind: composable
3
+ category: Composables
4
+ purpose: sync state to URL query params, URL-backed state, persist state in URL, shareable URL state
5
+ purpose-extras: filter URL persistence, query params sync
6
+ short: bidirectional sync between a reactive object and URL query params; SSR-safe initial read, client-only writes
7
+ invariants: true
8
+ ---
9
+
10
+ # useUrlSync — agent-only invariants
11
+
12
+ `useUrlSync(state, keys?)` keeps a reactive `Record<string, unknown>` in
13
+ sync with the URL's query string. Nuxt-only (depends on `#imports`'s
14
+ `useRoute`).
15
+
16
+ ## Invariants
17
+
18
+ - **Initial population is synchronous and SSR-safe.** Reads `useRoute().query`
19
+ (populated by Nuxt from the request URL on the server) and writes
20
+ matching keys into `state.value` before child components mount —
21
+ no hydration mismatch.
22
+ - **Reactive writes are client-only.** Uses
23
+ `useUrlSearchParams('history')` which only writes to
24
+ `window.location`. On the server the watcher never fires.
25
+ - **`keys` is optional but recommended.**
26
+ - When omitted: initial population pulls *all* top-level keys
27
+ currently in the URL; ongoing sync watches *all* keys in
28
+ `state.value`. Easy to leak unrelated state into the URL.
29
+ - When provided: only listed keys go in either direction.
30
+ - **Serialization conventions**:
31
+ - Plain values: `?key=value`.
32
+ - Nested objects: dot notation (`filters.category=shirts`).
33
+ - Arrays: bracket notation (`tags[0]=cats&tags[1]=dogs`).
34
+ - Combinations: mixed (`items[0].name=John`).
35
+ - `File` / `Blob` values: **silently skipped** (not serializable).
36
+ - Empty strings: **removed** from the URL.
37
+ - **URL writes use `router.replace`-like semantics** (via
38
+ `useUrlSearchParams` `'history'` mode) — no new browser history
39
+ entry. Back button still goes to the previous page, not the
40
+ previous filter state.
41
+ - **Watcher key**: the composable serializes only the synced keys to a
42
+ primitive string for cheap diffing — avoids deep-equality on File
43
+ arrays.
44
+ - **Flatten/unflatten helpers** live in `utils/urlParams`. Forming the
45
+ same dot/bracket conventions from outside requires going through
46
+ those helpers, not building strings manually.
47
+
48
+ ## Gotchas
49
+
50
+ - **Nuxt-only**: imports `useRoute` from `#imports`. Will not run in a
51
+ Vite/Vue-only consumer without Nuxt's auto-import config or a
52
+ manual shim.
53
+ - **No two-way URL-changes-update-state listener.** This composable
54
+ flows state → URL, not URL → state, after the initial read. Back /
55
+ forward navigation does not re-populate the state.
56
+ - **`keys` omitted = global sync.** Local UI state (open/close flags,
57
+ hover, cursor positions) accidentally listed in `state` will land in
58
+ the URL. Always pass an explicit `keys` array unless `state` IS the
59
+ URL state.
60
+ - **Top-level key matching includes dotted and bracketed children**:
61
+ `key === "filter"` matches `filter`, `filter.tag`, `filter[0]`.
62
+ - **`File` values are dropped**: the user-visible behavior is "I added
63
+ a file then the URL didn't update". By design, but surprising.
64
+
65
+ ## Quick reference
66
+
67
+ ```ts
68
+ import { ref } from "vue";
69
+ import { useUrlSync } from "../composables/useUrlSync";
70
+
71
+ const filters = ref({
72
+ category: "",
73
+ tags: [] as string[],
74
+ sort: "newest",
75
+ });
76
+
77
+ // Sync only the listed keys — recommended.
78
+ useUrlSync(filters, ["category", "tags", "sort"]);
79
+ ```
80
+
81
+ ```ts
82
+ // State IS the URL state — sync all keys.
83
+ const properties = ref<Record<string, string | File[]>>({});
84
+ useUrlSync(properties);
85
+ ```
86
+
87
+ ## Related
88
+
89
+ - `useFilter` — wraps this for filter groups; pass `urlSync: true`.
90
+ - `utils/urlParams` — `flattenParams`, `unflattenParams`, `topLevelKeys`.
91
+ - Public API reference: `docs/composables/use-url-sync.md`.
@@ -0,0 +1,100 @@
1
+ ---
2
+ kind: composable
3
+ category: Composables
4
+ purpose: form validation, declarative form rules, field validation, error state
5
+ short: declarative rule-based form validation with reactive errors keyed by field id and auto scroll-to-first-error
6
+ invariants: true
7
+ ---
8
+
9
+ # useValidation — agent-only invariants
10
+
11
+ `useValidation(rules?)` returns `{ checkValidity, errors, clearError,
12
+ clearAllErrors, changeRules }`. Errors are keyed by a field `id` that must
13
+ match a real DOM element id so the composable can scroll the offending
14
+ field into view.
15
+
16
+ ## Invariants
17
+
18
+ - **`rules: ValidationRule[]`**: `{ model, id, validator, message? }`.
19
+ - `model: MaybeRef<any>` — the value to validate. Unwrapped via
20
+ `unref` at check time.
21
+ - `id: string` — the DOM id of the field. Must be unique and must
22
+ match an element in the DOM at validation time so `scrollIntoView`
23
+ works.
24
+ - `validator(model) => boolean` — returns true if valid.
25
+ - `message?: string` — error message; falls back to
26
+ `t("validation.fieldError")`.
27
+ - **`errors`** is a reactive `Record<string, string | null>`. Truthy
28
+ string = error; `null` = no error.
29
+ - **`checkValidity()`** clears all errors, runs every rule with
30
+ `reduceRight`, and returns true if all passed.
31
+ - **`reduceRight` is used so that all rules execute** even after one
32
+ fails. The reducer is `(valid, rule) => validate(rule) && valid` —
33
+ `validate` runs unconditionally; the boolean is just aggregated.
34
+ - **The first failing rule (in reduceRight order = bottom-up of the
35
+ array) is scrolled into view.** Later rules' errors are still set
36
+ but don't trigger another scroll because `errors[id]` is already
37
+ set (the guard inside `validate`).
38
+ - **`changeRules(newRules)`** swaps the rule set at runtime — useful
39
+ when validation depends on a step / mode.
40
+ - **i18n fallback**: `useI18n().t` is used when available; outside an
41
+ i18n context it falls back to the bundled `en.json`'s
42
+ `validation.fieldError` key.
43
+ - **Exported helpers**: `isFilled(value)` (length-based truthy),
44
+ `isEmail(value)` (regex; empty string is treated as valid for
45
+ optional fields).
46
+
47
+ ## Gotchas
48
+
49
+ - **`id` must match a real DOM element.** If it doesn't,
50
+ `scrollIntoView` is a no-op but the error still records. Test that
51
+ `id`s collide with rendered form controls — orio form components
52
+ generally need the same `id` passed via `ControlProps`.
53
+ - **`reduceRight` order is reversed** relative to the rules array.
54
+ The "first" rule to fail (scroll target) is the LAST one in the
55
+ array. Order rules with the highest-priority field at the bottom
56
+ if scroll behavior matters.
57
+ - **Errors don't auto-clear when the user fixes the field.** The
58
+ caller must call `clearError(id)` on input, or re-run `checkValidity`
59
+ on every change.
60
+ - **No async validators.** `validator` must be synchronous; for
61
+ server-side checks, run them outside this composable.
62
+ - **No structural rules** (required-if, cross-field). Compose them
63
+ inside individual validators.
64
+
65
+ ## Quick reference
66
+
67
+ ```ts
68
+ import { useValidation, isFilled, isEmail } from "../composables/useValidation";
69
+
70
+ const email = ref("");
71
+ const name = ref("");
72
+
73
+ const { checkValidity, errors, clearError } = useValidation([
74
+ { model: email, id: "email", validator: isEmail,
75
+ message: $t("validation.email") },
76
+ { model: name, id: "name", validator: isFilled },
77
+ ]);
78
+
79
+ async function submit() {
80
+ if (!checkValidity()) return;
81
+ await api.save({ email: email.value, name: name.value });
82
+ }
83
+ ```
84
+
85
+ ```vue
86
+ <template>
87
+ <orio-input id="email" v-model="email" :error="errors.email"
88
+ @update:model-value="clearError('email')" />
89
+ <orio-input id="name" v-model="name" :error="errors.name"
90
+ @update:model-value="clearError('name')" />
91
+ <orio-button @click="submit">Submit</orio-button>
92
+ </template>
93
+ ```
94
+
95
+ ## Related
96
+
97
+ - `<orio-form>` — pairs with this for full-form validation.
98
+ - `<orio-control-element>` — accepts the `error` string from
99
+ `errors[id]`.
100
+ - Public API reference: `docs/composables/use-validation.md`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "orio-ui",
3
- "version": "1.27.0",
3
+ "version": "1.28.1",
4
4
  "description": "Modern Nuxt component library with theme support",
5
5
  "type": "module",
6
6
  "main": "./dist/module.mjs",
@@ -20,13 +20,19 @@
20
20
  "./styles": "./dist/runtime/assets/css/main.css",
21
21
  "./theme": "./dist/runtime/assets/css/colors.css"
22
22
  },
23
+ "bin": {
24
+ "orio-ui": "./bin/orio-ui.mjs"
25
+ },
23
26
  "files": [
27
+ "bin",
24
28
  "dist"
25
29
  ],
26
30
  "scripts": {
27
31
  "dev": "vitepress dev docs",
28
- "prebuild": "node scripts/update-counts.mjs",
32
+ "prebuild": "node scripts/update-counts.mjs && node scripts/generate-routing.mjs",
29
33
  "build": "nuxt-module-build build",
34
+ "postbuild": "node scripts/emit-consumer-agents.mjs",
35
+ "prepare": "simple-git-hooks",
30
36
  "prepack": "npm run build",
31
37
  "test": "vitest",
32
38
  "test:unit": "vitest run",
@@ -68,6 +74,7 @@
68
74
  "prettier": "^3.7.4",
69
75
  "sass": "^1.70.0",
70
76
  "sass-embedded": "^1.92.1",
77
+ "simple-git-hooks": "^2.13.1",
71
78
  "typescript": "^5.9.3",
72
79
  "typescript-eslint": "^8.52.0",
73
80
  "vitepress": "^1.6.4",
@@ -76,6 +83,9 @@
76
83
  "vue": "^3.5.13",
77
84
  "vue-tsc": "^3.2.1"
78
85
  },
86
+ "simple-git-hooks": {
87
+ "pre-commit": "node scripts/generate-routing.mjs && git add CLAUDE.md .claude/agents/component-worker.md .claude/agents/component-finder.md"
88
+ },
79
89
  "peerDependencies": {
80
90
  "nuxt": "^3.0.0 || ^4.0.0",
81
91
  "vue": "^3.3.0"