orio-ui 1.27.0 → 1.28.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/README.md +76 -1
- package/bin/orio-ui.mjs +72 -0
- package/dist/agents/ROUTING.md +140 -0
- package/dist/agents/component-finder.md +142 -0
- package/dist/agents/component-worker.md +152 -0
- package/dist/agents/snippet.md +6 -0
- package/dist/module.json +1 -1
- package/dist/runtime/components/AnimatedContainer.USAGE.md +79 -0
- package/dist/runtime/components/Badge.USAGE.md +75 -0
- package/dist/runtime/components/Banner.USAGE.md +52 -0
- package/dist/runtime/components/Button.USAGE.md +78 -0
- package/dist/runtime/components/Calendar.USAGE.md +8 -0
- package/dist/runtime/components/Canvas/USAGE.md +8 -0
- package/dist/runtime/components/CheckBox.USAGE.md +63 -0
- package/dist/runtime/components/CheckboxGroup.USAGE.md +95 -0
- package/dist/runtime/components/ControlElement.USAGE.md +8 -0
- package/dist/runtime/components/DashedContainer.USAGE.md +65 -0
- package/dist/runtime/components/EmptyState.USAGE.md +65 -0
- package/dist/runtime/components/Form.USAGE.md +102 -0
- package/dist/runtime/components/Icon.USAGE.md +61 -0
- package/dist/runtime/components/Input.USAGE.md +8 -0
- package/dist/runtime/components/ListItem.USAGE.md +84 -0
- package/dist/runtime/components/LoadingSpinner.USAGE.md +50 -0
- package/dist/runtime/components/LocaleSwitcher.USAGE.md +73 -0
- package/dist/runtime/components/Modal.USAGE.md +8 -0
- package/dist/runtime/components/NavButton.USAGE.md +80 -0
- package/dist/runtime/components/NumberInput/Horizontal.USAGE.md +61 -0
- package/dist/runtime/components/NumberInput/USAGE.md +74 -0
- package/dist/runtime/components/NumberInput/Vertical.USAGE.md +55 -0
- package/dist/runtime/components/Popover.USAGE.md +103 -0
- package/dist/runtime/components/RadioButton.USAGE.md +72 -0
- package/dist/runtime/components/Selector.USAGE.md +131 -0
- package/dist/runtime/components/SwitchButton.USAGE.md +62 -0
- package/dist/runtime/components/Tag.USAGE.md +51 -0
- package/dist/runtime/components/TaggableSelector.USAGE.md +73 -0
- package/dist/runtime/components/Textarea.USAGE.md +72 -0
- package/dist/runtime/components/Tooltip.USAGE.md +84 -0
- package/dist/runtime/components/ZoomableContainer.USAGE.md +108 -0
- package/dist/runtime/components/date/Picker.USAGE.md +8 -0
- package/dist/runtime/components/date/PickerTrigger.USAGE.md +65 -0
- package/dist/runtime/components/date/RangePicker.USAGE.md +97 -0
- package/dist/runtime/components/gallery/Carousel.USAGE.md +98 -0
- package/dist/runtime/components/gallery/CarouselPreview.USAGE.md +51 -0
- package/dist/runtime/components/upload/USAGE.md +91 -0
- package/dist/runtime/components/view/Dates.USAGE.md +67 -0
- package/dist/runtime/components/view/KeyBinds.USAGE.md +58 -0
- package/dist/runtime/components/view/Separator.USAGE.md +57 -0
- package/dist/runtime/components/view/Text.USAGE.md +68 -0
- package/dist/runtime/composables/useApi.USAGE.md +64 -0
- package/dist/runtime/composables/useControlSize.USAGE.md +73 -0
- package/dist/runtime/composables/useDecimalFormatter.USAGE.md +72 -0
- package/dist/runtime/composables/useFilter.USAGE.md +120 -0
- package/dist/runtime/composables/useFuzzySearch.USAGE.md +68 -0
- package/dist/runtime/composables/useInertia.USAGE.md +80 -0
- package/dist/runtime/composables/useListKeyboard.USAGE.md +97 -0
- package/dist/runtime/composables/useModal.USAGE.md +82 -0
- package/dist/runtime/composables/usePinchZoom.USAGE.md +95 -0
- package/dist/runtime/composables/usePressAndHold.USAGE.md +70 -0
- package/dist/runtime/composables/useRovingGrid.USAGE.md +106 -0
- package/dist/runtime/composables/useSound.USAGE.md +74 -0
- package/dist/runtime/composables/useTheme.USAGE.md +76 -0
- package/dist/runtime/composables/useUrlSync.USAGE.md +91 -0
- package/dist/runtime/composables/useValidation.USAGE.md +100 -0
- 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.
|
|
3
|
+
"version": "1.28.0",
|
|
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"
|