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,102 @@
|
|
|
1
|
+
---
|
|
2
|
+
kind: component
|
|
3
|
+
category: Form inputs
|
|
4
|
+
purpose: form wrapper, submission, validation surface, auto-bind form model
|
|
5
|
+
short: form wrapper that auto-binds child controls by `name` prop to a single object v-model
|
|
6
|
+
invariants: true
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Form — agent-only invariants
|
|
10
|
+
|
|
11
|
+
`<orio-form>` is a form wrapper that walks its slot vnodes and **auto-binds
|
|
12
|
+
child controls to a single object v-model** by matching each child's
|
|
13
|
+
`name` prop to a key (or dot path) in the model. Generic over `T extends
|
|
14
|
+
object`.
|
|
15
|
+
|
|
16
|
+
## Invariants
|
|
17
|
+
|
|
18
|
+
- **v-model is an object `T`.** Each child with a `name="..."` prop
|
|
19
|
+
receives `modelValue` and `onUpdate:modelValue` cloned in via
|
|
20
|
+
`cloneVNode`. The user's hand-written v-model on that child is
|
|
21
|
+
overridden if `name` matches a field.
|
|
22
|
+
- **Auto-bind requires both `name` and a matching field path.** Children
|
|
23
|
+
without `name`, or with a `name` that doesn't resolve via `getByPath`,
|
|
24
|
+
render unchanged. Auto-bind is silent — no warning when a name doesn't
|
|
25
|
+
match.
|
|
26
|
+
- **Dot-paths supported**: `name="user.email"` writes to `model.user.email`.
|
|
27
|
+
The intermediate path must already exist (`setByPath` no-ops if the
|
|
28
|
+
parent is missing).
|
|
29
|
+
- **Children must follow the `modelValue` / `update:modelValue` contract.**
|
|
30
|
+
Auto-bind injects these props directly; v-model sugar is not used. Any
|
|
31
|
+
child that doesn't honor that pair is silently un-bound. All orio
|
|
32
|
+
form components do; custom children need to follow the contract.
|
|
33
|
+
- **Slot walking is recursive.** Vnode children, slot functions, and slot
|
|
34
|
+
objects are all descended. Wrappers (`<div>` groupers, layout columns)
|
|
35
|
+
are transparent.
|
|
36
|
+
- **`disabled` wraps the children in `<fieldset disabled>`** with
|
|
37
|
+
`display: contents` so nothing visual changes. The native `disabled`
|
|
38
|
+
attribute on the fieldset gates every form control inside (browser-
|
|
39
|
+
native behavior).
|
|
40
|
+
- **`loading` adds `pointer-events: none`** plus 0.6 opacity on the form.
|
|
41
|
+
Visual + interaction lock without disabling form values.
|
|
42
|
+
- **`novalidate` is set on the `<form>`** — HTML5 native validation
|
|
43
|
+
bubbles are off. Pair with `useValidation` for declarative checks.
|
|
44
|
+
- **`@submit` emits a single `submit` event** after `preventDefault`. The
|
|
45
|
+
emit is gated by `disabled || loading`. There is **no** built-in submit
|
|
46
|
+
button — render `<orio-button type="submit">` (or similar) inside.
|
|
47
|
+
|
|
48
|
+
## Gotchas
|
|
49
|
+
|
|
50
|
+
- **No per-field error wiring.** `error` props on children are still
|
|
51
|
+
user-managed. The Form is only a binder, not a validator.
|
|
52
|
+
- **Reactivity through `setByPath` requires mutable nested objects.** A
|
|
53
|
+
frozen / readonly model breaks silently. Use a plain ref'd object.
|
|
54
|
+
- **Auto-bind clones the vnode** — keyed lists work, but if a child also
|
|
55
|
+
emits something other than `update:modelValue` via the same `onUpdate`
|
|
56
|
+
pattern, the clone replaces only the listed handlers.
|
|
57
|
+
- **Slot is descended into every render** — performance scales linearly
|
|
58
|
+
with slot depth. For very large forms (hundreds of fields), consider
|
|
59
|
+
splitting into nested `<orio-form>`s by section.
|
|
60
|
+
- **`name` collisions silently overwrite.** Two children with the same
|
|
61
|
+
`name` both bind to the same field; both updates write to the same
|
|
62
|
+
spot. Order is undefined.
|
|
63
|
+
|
|
64
|
+
## Quick reference
|
|
65
|
+
|
|
66
|
+
```vue
|
|
67
|
+
<script setup lang="ts">
|
|
68
|
+
interface Profile {
|
|
69
|
+
name: string;
|
|
70
|
+
email: string;
|
|
71
|
+
preferences: { newsletter: boolean };
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const profile = ref<Profile>({
|
|
75
|
+
name: "",
|
|
76
|
+
email: "",
|
|
77
|
+
preferences: { newsletter: false },
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
function save() {
|
|
81
|
+
// Submit profile.value to API
|
|
82
|
+
}
|
|
83
|
+
</script>
|
|
84
|
+
|
|
85
|
+
<template>
|
|
86
|
+
<orio-form v-model="profile" :loading="saving" @submit="save">
|
|
87
|
+
<orio-input name="name" :label="$t('profile.name')" />
|
|
88
|
+
<orio-input name="email" :label="$t('profile.email')" type="email" />
|
|
89
|
+
<orio-check-box name="preferences.newsletter">
|
|
90
|
+
{{ $t("profile.newsletter") }}
|
|
91
|
+
</orio-check-box>
|
|
92
|
+
|
|
93
|
+
<orio-button type="submit">{{ $t("common.save") }}</orio-button>
|
|
94
|
+
</orio-form>
|
|
95
|
+
</template>
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## Related
|
|
99
|
+
|
|
100
|
+
- `useValidation` — declarative validation rules for form fields.
|
|
101
|
+
- `<orio-control-element>` — wraps every form input with label/error.
|
|
102
|
+
- Public API reference: `docs/components/form.md`.
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
---
|
|
2
|
+
kind: component
|
|
3
|
+
category: Buttons & indicators
|
|
4
|
+
purpose: icon, SVG renderer, glyph, symbol
|
|
5
|
+
short: SVG icon renderer that pulls from `utils/icon-registry` and renders via v-html
|
|
6
|
+
invariants: true
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Icon — agent-only invariants
|
|
10
|
+
|
|
11
|
+
`<orio-icon>` renders an SVG icon from `utils/icon-registry` into a
|
|
12
|
+
`<span>` via `v-html`. Unknown icon names render empty.
|
|
13
|
+
|
|
14
|
+
## Invariants
|
|
15
|
+
|
|
16
|
+
- **`name` is required.** Either a registered `IconName` (typed) or any
|
|
17
|
+
string. Unregistered names render as empty string (no visible
|
|
18
|
+
output, no warning).
|
|
19
|
+
- **`size`**: `string | number`. A number is treated as pixels (`"24px"`).
|
|
20
|
+
A string is used verbatim (`"1.5em"`, `"2rem"`, `"100%"`). When
|
|
21
|
+
`undefined`, falls back to the CSS var `--control-icon-size` (default
|
|
22
|
+
`1.5em`).
|
|
23
|
+
- **`color` defaults to `"currentColor"`** — the icon inherits parent
|
|
24
|
+
text color. Pass a CSS color string to override.
|
|
25
|
+
- **`v-html` is used to inject the raw SVG markup.** The icon registry
|
|
26
|
+
is the trust boundary — never render dynamic / user-controlled SVG
|
|
27
|
+
strings through it.
|
|
28
|
+
- **SVGs inside use `fill: currentColor`** so the `color` prop / parent
|
|
29
|
+
color flows through.
|
|
30
|
+
- **`flex-shrink: 0`** is set on the span. The icon never shrinks inside
|
|
31
|
+
a flex layout — important for inline labels with overflow.
|
|
32
|
+
- **The wrapper is `display: inline-flex; align-items: center; justify-content: center`**
|
|
33
|
+
so the SVG is centered when the size exceeds the SVG viewBox.
|
|
34
|
+
|
|
35
|
+
## Gotchas
|
|
36
|
+
|
|
37
|
+
- **Bypass-XSS surface is the registry.** Anything in `utils/icon-registry`
|
|
38
|
+
is rendered as raw HTML. Do not extend the registry with strings
|
|
39
|
+
derived from user input.
|
|
40
|
+
- **Sizing a string `100%` requires a sized parent.** The span goes to
|
|
41
|
+
the size you pass; the inner SVG fills 100% of that.
|
|
42
|
+
- **`color` is applied via inline style**, so it beats class-based
|
|
43
|
+
styling. To recolor via CSS class, omit the `color` prop.
|
|
44
|
+
- **Spelling errors silently render nothing.** If an icon is missing
|
|
45
|
+
during dev, check the registry — there's no console warning.
|
|
46
|
+
|
|
47
|
+
## Quick reference
|
|
48
|
+
|
|
49
|
+
```vue
|
|
50
|
+
<template>
|
|
51
|
+
<orio-icon name="check" :size="24" />
|
|
52
|
+
<orio-icon name="warning" size="1.5rem" color="var(--color-alert)" />
|
|
53
|
+
<orio-icon name="search" />
|
|
54
|
+
</template>
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Related
|
|
58
|
+
|
|
59
|
+
- `<orio-loading-spinner>` — thin wrapper for the `loading-loop` icon.
|
|
60
|
+
- `utils/icon-registry` — the source of all available icon names.
|
|
61
|
+
- Public API reference: `docs/components/icon.md`.
|
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
---
|
|
2
|
+
kind: component
|
|
3
|
+
category: Form inputs
|
|
4
|
+
purpose: text input, single-line input
|
|
5
|
+
short: text input wrapping ControlElement; supports inner-floating label layout
|
|
6
|
+
invariants: true
|
|
7
|
+
---
|
|
8
|
+
|
|
1
9
|
# Input — agent-only invariants
|
|
2
10
|
|
|
3
11
|
`<orio-input>` is the text input wrapping `ControlElement`. Read
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
---
|
|
2
|
+
kind: component
|
|
3
|
+
category: Media & misc
|
|
4
|
+
purpose: list row, list item, selectable row, list entry
|
|
5
|
+
short: `<li>` row with start/end slots and optional selectable checkbox-style behavior
|
|
6
|
+
invariants: true
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# ListItem — agent-only invariants
|
|
10
|
+
|
|
11
|
+
`<orio-list-item>` is a `<li>` row with three content zones (start /
|
|
12
|
+
center / end) and an optional selectable mode. It is also the row
|
|
13
|
+
primitive used internally by `<orio-selector>`.
|
|
14
|
+
|
|
15
|
+
## Invariants
|
|
16
|
+
|
|
17
|
+
- **Renders an `<li>`.** Must be a child of `<ul>` / `<ol>` for valid
|
|
18
|
+
HTML. Rendering it loose works but breaks list semantics.
|
|
19
|
+
- **`selectable` prop** turns the row into an interactive checkbox-style
|
|
20
|
+
control:
|
|
21
|
+
- `tabindex="0"` (keyboard focusable).
|
|
22
|
+
- `role="checkbox"` + `aria-checked` reflect `selected`.
|
|
23
|
+
- Click and Enter / Space toggle the v-model.
|
|
24
|
+
- `cursor: pointer`.
|
|
25
|
+
- **v-model is `selected: boolean`.** Updates on toggle. Without
|
|
26
|
+
`selectable`, the model is still bound but no toggle handler runs.
|
|
27
|
+
- **Three slots**:
|
|
28
|
+
- `#start` — left zone. **Only renders** when the slot is provided
|
|
29
|
+
OR `selectable` is true. When selectable and no slot, defaults to
|
|
30
|
+
`<orio-check-box :model-value="selected">`.
|
|
31
|
+
- `#default` — center content. Always renders, `flex-grow: 1`.
|
|
32
|
+
- `#end` — right zone. Only renders when the slot is provided.
|
|
33
|
+
- **Selected state** uses `--color-accent` background and
|
|
34
|
+
`--color-accent-soft-darker` text. Hover swaps to surface bg when
|
|
35
|
+
not selected.
|
|
36
|
+
- **Used internally by `<orio-selector>`** as `role="option"` rows. In
|
|
37
|
+
that usage the role gets overridden via `$attrs`.
|
|
38
|
+
|
|
39
|
+
## Gotchas
|
|
40
|
+
|
|
41
|
+
- **No multi-selection grouping.** A single ListItem holds one
|
|
42
|
+
selected boolean. For grouped list selection, wire each item's
|
|
43
|
+
`v-model:selected` to a parent array.
|
|
44
|
+
- **Default `<orio-check-box>` in `#start`** uses bare props — it has
|
|
45
|
+
no label, no accent state of its own. If the row is `selected`, the
|
|
46
|
+
checkbox shows checked.
|
|
47
|
+
- **Keyboard `Enter` and `Space` `.preventDefault()`** — Space won't
|
|
48
|
+
scroll the page, Enter won't submit a form. Useful, but
|
|
49
|
+
unconfigurable.
|
|
50
|
+
- **Without `selectable`**, the row is still clickable but no toggle /
|
|
51
|
+
focus / role is applied. To make it a button-like row without a
|
|
52
|
+
checkbox-style toggle, wrap content in a real `<button>` inside
|
|
53
|
+
`#default`.
|
|
54
|
+
|
|
55
|
+
## Quick reference
|
|
56
|
+
|
|
57
|
+
```vue
|
|
58
|
+
<template>
|
|
59
|
+
<ul>
|
|
60
|
+
<orio-list-item
|
|
61
|
+
v-for="item in items"
|
|
62
|
+
:key="item.id"
|
|
63
|
+
v-model:selected="item.selected"
|
|
64
|
+
selectable
|
|
65
|
+
>
|
|
66
|
+
<template #start>
|
|
67
|
+
<orio-icon :name="item.icon" />
|
|
68
|
+
</template>
|
|
69
|
+
|
|
70
|
+
{{ item.label }}
|
|
71
|
+
|
|
72
|
+
<template #end>
|
|
73
|
+
<orio-tag :text="item.badge" variant="accent" />
|
|
74
|
+
</template>
|
|
75
|
+
</orio-list-item>
|
|
76
|
+
</ul>
|
|
77
|
+
</template>
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Related
|
|
81
|
+
|
|
82
|
+
- `<orio-selector>` — uses this as listbox rows.
|
|
83
|
+
- `<orio-check-box>` — default `#start` content when selectable.
|
|
84
|
+
- Public API reference: `docs/components/list-item.md`.
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
---
|
|
2
|
+
kind: component
|
|
3
|
+
category: Buttons & indicators
|
|
4
|
+
purpose: spinner, loading indicator, loading icon, busy indicator
|
|
5
|
+
short: thin wrapper that renders the bundled `loading-loop` icon; no props
|
|
6
|
+
invariants: false
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# LoadingSpinner — agent-only invariants
|
|
10
|
+
|
|
11
|
+
`<orio-loading-spinner>` renders the bundled `loading-loop` icon. That's
|
|
12
|
+
it. There are **no props**, no slots, no emits.
|
|
13
|
+
|
|
14
|
+
## Invariants
|
|
15
|
+
|
|
16
|
+
- **Zero-prop wrapper.** Template is literally
|
|
17
|
+
`<orio-icon name="loading-loop" />`.
|
|
18
|
+
- **Animation lives in the icon SVG itself** (via the registry's
|
|
19
|
+
`loading-loop` entry). The component does not apply any CSS animation.
|
|
20
|
+
- **Size and color follow `<orio-icon>` defaults** — `1.5em` from
|
|
21
|
+
`--control-icon-size`, `currentColor`. Override via parent CSS:
|
|
22
|
+
`font-size`, `color`, or by passing direct CSS to a wrapper.
|
|
23
|
+
- **Used internally by `<orio-button :loading>`** — when wiring loading
|
|
24
|
+
states into buttons, prefer `:loading="..."` on the button to swapping
|
|
25
|
+
in this spinner manually.
|
|
26
|
+
|
|
27
|
+
## Gotchas
|
|
28
|
+
|
|
29
|
+
- **No way to change spin direction, speed, or thickness.** The SVG is
|
|
30
|
+
fixed. For a custom spinner, render `<orio-icon>` with your own icon
|
|
31
|
+
name + CSS animation.
|
|
32
|
+
- **Aria semantics are absent.** No `role="status"`, no
|
|
33
|
+
`aria-label`. For screen-reader announcement, wrap in a `<span
|
|
34
|
+
role="status" aria-label="Loading">` at the consumer.
|
|
35
|
+
|
|
36
|
+
## Quick reference
|
|
37
|
+
|
|
38
|
+
```vue
|
|
39
|
+
<template>
|
|
40
|
+
<orio-loading-spinner v-if="loading" />
|
|
41
|
+
</template>
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Related
|
|
45
|
+
|
|
46
|
+
- `<orio-icon>` — under the hood; use it directly for non-spinner
|
|
47
|
+
glyphs.
|
|
48
|
+
- `<orio-button :loading>` — preferred way to show busy state on
|
|
49
|
+
buttons.
|
|
50
|
+
- Public API reference: `docs/components/loading-spinner.md`.
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
---
|
|
2
|
+
kind: component
|
|
3
|
+
category: Media & misc
|
|
4
|
+
purpose: locale switcher, language toggle, i18n switcher
|
|
5
|
+
short: preconfigured Selector that mutates vue-i18n's locale; defaults to English + Ukrainian with flag emojis
|
|
6
|
+
invariants: true
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# LocaleSwitcher — agent-only invariants
|
|
10
|
+
|
|
11
|
+
`<orio-locale-switcher>` is a thin wrapper around `<orio-selector>` that
|
|
12
|
+
reads and writes `useI18n().locale` directly. Drop it anywhere in the app
|
|
13
|
+
to give users a language toggle.
|
|
14
|
+
|
|
15
|
+
## Invariants
|
|
16
|
+
|
|
17
|
+
- **Mutates `useI18n().locale` on selection.** No model is exposed —
|
|
18
|
+
the side effect is the API.
|
|
19
|
+
- **Default `locales`**:
|
|
20
|
+
```ts
|
|
21
|
+
[
|
|
22
|
+
{ code: "en", flag: "🇬🇧", label: "English" },
|
|
23
|
+
{ code: "uk", flag: "🇺🇦", label: "Українська" },
|
|
24
|
+
]
|
|
25
|
+
```
|
|
26
|
+
Override with the `locales` prop if your app supports a different set.
|
|
27
|
+
- **`LocaleOption` shape** (exported): `{ code, flag, label }`. All three
|
|
28
|
+
are strings; `flag` is rendered verbatim (emoji or any unicode).
|
|
29
|
+
- **Selector wiring**: `field: "code"`, `optionName: "label"`. Active
|
|
30
|
+
option matches by `code === currentLocale`. Falls back to the first
|
|
31
|
+
locale if the current i18n locale isn't in the list.
|
|
32
|
+
- **Custom `#trigger-label` and `#option` slots** render `flag + label`
|
|
33
|
+
side-by-side with 0.5rem gap.
|
|
34
|
+
- **Requires `useI18n` setup.** The component throws if called outside
|
|
35
|
+
a vue-i18n context.
|
|
36
|
+
|
|
37
|
+
## Gotchas
|
|
38
|
+
|
|
39
|
+
- **Direct locale mutation bypasses any persistence layer.** If your
|
|
40
|
+
app saves locale to cookies / localStorage / API, hook into
|
|
41
|
+
`useI18n().locale` from elsewhere — this component does not call
|
|
42
|
+
any side effect beyond the i18n update.
|
|
43
|
+
- **Flag emojis depend on font support.** macOS / iOS render them
|
|
44
|
+
correctly; Windows often shows letter pairs (e.g. "GB", "UA"). For
|
|
45
|
+
cross-platform consistency, swap to icons via a custom `locales`
|
|
46
|
+
prop with icon names + a custom `#option`/`#trigger-label`.
|
|
47
|
+
- **No client/server hydration story.** If the locale is mutated
|
|
48
|
+
before vue-i18n is hydrated on the client, mismatches can occur.
|
|
49
|
+
Best to initialize locale in your app setup and let this switcher
|
|
50
|
+
only handle user-driven changes.
|
|
51
|
+
|
|
52
|
+
## Quick reference
|
|
53
|
+
|
|
54
|
+
```vue
|
|
55
|
+
<template>
|
|
56
|
+
<orio-locale-switcher />
|
|
57
|
+
|
|
58
|
+
<orio-locale-switcher
|
|
59
|
+
:locales="[
|
|
60
|
+
{ code: 'en', flag: '🇺🇸', label: 'English' },
|
|
61
|
+
{ code: 'es', flag: '🇪🇸', label: 'Español' },
|
|
62
|
+
{ code: 'pt', flag: '🇧🇷', label: 'Português' },
|
|
63
|
+
]"
|
|
64
|
+
/>
|
|
65
|
+
</template>
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Related
|
|
69
|
+
|
|
70
|
+
- `<orio-selector>` — under the hood. Build your own switcher from
|
|
71
|
+
Selector if you need different side effects (e.g. routing).
|
|
72
|
+
- Public API reference: `docs/components/locale-switcher.md` (if
|
|
73
|
+
present).
|
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
---
|
|
2
|
+
kind: component
|
|
3
|
+
category: Layout & containers
|
|
4
|
+
purpose: modal, dialog, popup overlay, lightbox
|
|
5
|
+
short: teleported overlay dialog with open-from-origin animation
|
|
6
|
+
invariants: true
|
|
7
|
+
---
|
|
8
|
+
|
|
1
9
|
# Modal — agent-only invariants
|
|
2
10
|
|
|
3
11
|
`<orio-modal>` is the teleported overlay dialog.
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
---
|
|
2
|
+
kind: component
|
|
3
|
+
category: Buttons & indicators
|
|
4
|
+
purpose: nav button, link-styled button, navigation item, sidebar item
|
|
5
|
+
short: bare nav-styled button with `active` state and `aria-current="page"` for the current route
|
|
6
|
+
invariants: true
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# NavButton — agent-only invariants
|
|
10
|
+
|
|
11
|
+
`<orio-nav-button>` is a transparent, text-styled button for navigation
|
|
12
|
+
menus and tab bars. It is not a `<router-link>` — wrap it or wire
|
|
13
|
+
navigation in the `click` handler yourself.
|
|
14
|
+
|
|
15
|
+
## Invariants
|
|
16
|
+
|
|
17
|
+
- **`active` prop is the "is this the current item" flag.** When true:
|
|
18
|
+
- Text becomes accent color, font-weight 600.
|
|
19
|
+
- `aria-current="page"` is set on the inner `<button>`.
|
|
20
|
+
- `undefined` (not removed) otherwise — so it doesn't appear in the
|
|
21
|
+
DOM at all when inactive.
|
|
22
|
+
- **`icon` prop OR `#icon` slot** — same pattern as `<orio-button>`.
|
|
23
|
+
- **Icon-only mode is auto-detected** (icon + no default slot) →
|
|
24
|
+
`border-radius: 50%`, `aspect-ratio: 1`, `padding: var(--control-py)`.
|
|
25
|
+
- **No `variant` prop.** One look only — transparent background, text
|
|
26
|
+
color, no border.
|
|
27
|
+
- **`disabled` blocks click** and applies 0.5 opacity + `cursor:
|
|
28
|
+
not-allowed`.
|
|
29
|
+
- **Only emits `click`.** No mousedown/mouseup like `<orio-button>`.
|
|
30
|
+
- **Focus ring**: `outline: 2px solid var(--color-accent)` with
|
|
31
|
+
`outline-offset: 2px`. Keyboard-only via `:focus-visible`.
|
|
32
|
+
|
|
33
|
+
## Gotchas
|
|
34
|
+
|
|
35
|
+
- **Not a router link.** No `to`, no `href`. Wire navigation in
|
|
36
|
+
`@click`. If a real anchor is needed for a11y / right-click-to-open,
|
|
37
|
+
fall back to your router's link component.
|
|
38
|
+
- **Same `$attrs` duplication caveat as `<orio-button>`** — attrs may
|
|
39
|
+
land on both the wrapper and the inner `<button>`.
|
|
40
|
+
- **Active state is purely visual + ARIA**; the component does not
|
|
41
|
+
detect the current route. Compute `active` from `useRoute()` or your
|
|
42
|
+
router state.
|
|
43
|
+
- **`type` defaults to `submit`** (native default). Pass `type="button"`
|
|
44
|
+
if mounted inside a form to avoid accidental submits.
|
|
45
|
+
|
|
46
|
+
## Quick reference
|
|
47
|
+
|
|
48
|
+
```vue
|
|
49
|
+
<script setup lang="ts">
|
|
50
|
+
import { useRoute, useRouter } from "vue-router";
|
|
51
|
+
|
|
52
|
+
const route = useRoute();
|
|
53
|
+
const router = useRouter();
|
|
54
|
+
</script>
|
|
55
|
+
|
|
56
|
+
<template>
|
|
57
|
+
<nav>
|
|
58
|
+
<orio-nav-button
|
|
59
|
+
icon="home"
|
|
60
|
+
:active="route.path === '/'"
|
|
61
|
+
@click="router.push('/')"
|
|
62
|
+
>
|
|
63
|
+
{{ $t("nav.home") }}
|
|
64
|
+
</orio-nav-button>
|
|
65
|
+
|
|
66
|
+
<orio-nav-button
|
|
67
|
+
icon="settings"
|
|
68
|
+
:active="route.path === '/settings'"
|
|
69
|
+
@click="router.push('/settings')"
|
|
70
|
+
>
|
|
71
|
+
{{ $t("nav.settings") }}
|
|
72
|
+
</orio-nav-button>
|
|
73
|
+
</nav>
|
|
74
|
+
</template>
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Related
|
|
78
|
+
|
|
79
|
+
- `<orio-button>` — primary actions; use that for CTAs.
|
|
80
|
+
- Public API reference: `docs/components/nav-button.md`.
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
---
|
|
2
|
+
kind: component
|
|
3
|
+
category: Form inputs
|
|
4
|
+
purpose: number input horizontal, minus-plus stepper, quantity stepper
|
|
5
|
+
short: number input variant with minus/plus buttons flanking the field and press-and-hold repeat
|
|
6
|
+
invariants: true
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# NumberInput/Horizontal — agent-only invariants
|
|
10
|
+
|
|
11
|
+
`<orio-number-input-horizontal>` is a pre-styled wrapper around
|
|
12
|
+
`<orio-number-input>` that renders minus/plus buttons on either side of the
|
|
13
|
+
field. Read `NumberInput/USAGE.md` first — this variant inherits all of its
|
|
14
|
+
contract.
|
|
15
|
+
|
|
16
|
+
## Invariants
|
|
17
|
+
|
|
18
|
+
- **Accepts the full `NumberInputProps` interface** plus a `disabled` prop
|
|
19
|
+
for the buttons. All props are forwarded via `v-bind="$props"`.
|
|
20
|
+
- **Buttons use `usePressAndHold`** — `@mousedown` starts the auto-repeat,
|
|
21
|
+
`@mouseup`/`@mouseleave` stops it. Hold to ramp through a range.
|
|
22
|
+
- **`disabled` and the per-button bound state both apply.** A minus button
|
|
23
|
+
is disabled when `disabled || isAtMin`; a plus button when
|
|
24
|
+
`disabled || isAtMax`.
|
|
25
|
+
- **Input text is centered** (`text-align: center` via `:deep(.number-input)`).
|
|
26
|
+
- **Controls are full-width inside the wrapper**: `justify-content: space-between`
|
|
27
|
+
with 3px horizontal padding. Buttons sit at the edges.
|
|
28
|
+
- **`layout="inner"` is supported** — the label centers between the two
|
|
29
|
+
buttons (`left: 0; right: 0; text-align: center`).
|
|
30
|
+
- **No keyboard auto-repeat.** Press-and-hold listens to mouse events
|
|
31
|
+
only; holding Enter on a focused button does not ramp.
|
|
32
|
+
|
|
33
|
+
## Gotchas
|
|
34
|
+
|
|
35
|
+
- **Buttons are `<orio-button appearance="minimal" variant="subdued">`** —
|
|
36
|
+
they take theme tokens but are not slotted. To swap iconography, fall
|
|
37
|
+
back to the base `<orio-number-input>` with a custom `#controls` slot.
|
|
38
|
+
- **Touch behavior**: press-and-hold uses `@mousedown`/`@mouseup`. On touch
|
|
39
|
+
devices these may not fire reliably across all browsers — confirm on
|
|
40
|
+
iOS Safari if mobile is a target.
|
|
41
|
+
|
|
42
|
+
## Quick reference
|
|
43
|
+
|
|
44
|
+
```vue
|
|
45
|
+
<template>
|
|
46
|
+
<orio-number-input-horizontal
|
|
47
|
+
v-model="quantity"
|
|
48
|
+
:min="0"
|
|
49
|
+
:max="10"
|
|
50
|
+
:step="1"
|
|
51
|
+
:label="$t('cart.quantity')"
|
|
52
|
+
/>
|
|
53
|
+
</template>
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Related
|
|
57
|
+
|
|
58
|
+
- `<orio-number-input>` — the base; use it when you need custom button
|
|
59
|
+
iconography or layout.
|
|
60
|
+
- `<orio-number-input-vertical>` — chevron-stack variant.
|
|
61
|
+
- `usePressAndHold` — composable behind the auto-repeat.
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
---
|
|
2
|
+
kind: component
|
|
3
|
+
category: Form inputs
|
|
4
|
+
purpose: number input, numeric input, custom-control numeric stepper
|
|
5
|
+
short: numeric input base with overlay controls slot; pair with Horizontal/Vertical variants for ready-made spinners
|
|
6
|
+
invariants: true
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# NumberInput — agent-only invariants
|
|
10
|
+
|
|
11
|
+
`<orio-number-input>` is the **base** numeric input. By itself it renders a
|
|
12
|
+
`<input type="number">` with no spinner. Custom step controls go in the
|
|
13
|
+
`#controls` slot. For ready-made stepper UIs, use
|
|
14
|
+
`<orio-number-input-horizontal>` or `<orio-number-input-vertical>`.
|
|
15
|
+
|
|
16
|
+
## Invariants
|
|
17
|
+
|
|
18
|
+
- **Extends `NumberInputProps`** (exported from this file): `ControlProps`
|
|
19
|
+
minus `layout`, plus `layout?: InputLayout`, `min`, `max`, `step`,
|
|
20
|
+
`decimalPlaces`. Same `"inner"` layout trick as Input/Textarea.
|
|
21
|
+
- **v-model is `number`** (default `0`). Native input value is coerced via
|
|
22
|
+
Vue's `v-model.number` semantics.
|
|
23
|
+
- **Validation runs on blur and on every increase/decrease.** Value is
|
|
24
|
+
clamped to `[min, max]` (if finite) and then rounded to `decimalPlaces`
|
|
25
|
+
via `toFixed`. Typing an out-of-range value is allowed *during* edit;
|
|
26
|
+
blur snaps it back.
|
|
27
|
+
- **`decimalPlaces` defaults to `0`.** Decimal input requires both
|
|
28
|
+
`decimalPlaces` and a matching `step` (e.g. `:decimalPlaces="2" :step="0.01"`).
|
|
29
|
+
- **Native webkit/firefox spin buttons are hidden** via CSS. Always
|
|
30
|
+
`appearance: textfield`.
|
|
31
|
+
- **`#controls` slot overlays the input absolutely** with `pointer-events:
|
|
32
|
+
none` on the container and `:deep(button) { pointer-events: auto }`.
|
|
33
|
+
Only buttons receive clicks; the rest of the overlay passes through to
|
|
34
|
+
the input.
|
|
35
|
+
- **`#controls` slot props**: `{ increase, decrease, isAtMax, isAtMin }`.
|
|
36
|
+
`increase`/`decrease` apply `step` and run validation; `isAtMax`/`isAtMin`
|
|
37
|
+
are `false` when `min`/`max` are undefined.
|
|
38
|
+
- **`min`, `max`, `step`, `decimalPlaces` are stripped from `controlProps`**
|
|
39
|
+
before forwarding to ControlElement — they do not pollute the wrapper's
|
|
40
|
+
prop bag.
|
|
41
|
+
- **`$attrs` is spread before `control`** on the inner `<input>`, same as
|
|
42
|
+
Input.
|
|
43
|
+
|
|
44
|
+
## Gotchas
|
|
45
|
+
|
|
46
|
+
- **No spinner UI without the slot or a variant.** A bare
|
|
47
|
+
`<orio-number-input v-model="n" />` renders nothing in the controls area.
|
|
48
|
+
- **`min`/`max` of `0` are honored** because the check uses `Number.isFinite`,
|
|
49
|
+
not truthiness.
|
|
50
|
+
- **Blur normalization rewrites the model.** Even if the user types a value
|
|
51
|
+
that is already valid, it gets re-`toFixed`d on blur — `"3"` becomes
|
|
52
|
+
`"3.00"` displayed when `decimalPlaces: 2`.
|
|
53
|
+
- **Negative `step` is not blocked.** Passing `step: -1` makes increase
|
|
54
|
+
decrement.
|
|
55
|
+
|
|
56
|
+
## Quick reference — custom controls slot
|
|
57
|
+
|
|
58
|
+
```vue
|
|
59
|
+
<template>
|
|
60
|
+
<orio-number-input v-model="quantity" :min="1" :max="99" :label="$t('cart.qty')">
|
|
61
|
+
<template #controls="{ increase, decrease, isAtMax, isAtMin }">
|
|
62
|
+
<orio-button :disabled="isAtMin" @click="decrease">−</orio-button>
|
|
63
|
+
<orio-button :disabled="isAtMax" @click="increase">+</orio-button>
|
|
64
|
+
</template>
|
|
65
|
+
</orio-number-input>
|
|
66
|
+
</template>
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Related
|
|
70
|
+
|
|
71
|
+
- `<orio-number-input-horizontal>` — pre-styled minus/plus on either side.
|
|
72
|
+
- `<orio-number-input-vertical>` — pre-styled chevron stack on the right.
|
|
73
|
+
- `<orio-input>` — when you want raw text, not a number.
|
|
74
|
+
- Public API reference: `docs/components/number-input.md`.
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
---
|
|
2
|
+
kind: component
|
|
3
|
+
category: Form inputs
|
|
4
|
+
purpose: number input vertical, chevron stepper, stacked-arrow numeric input
|
|
5
|
+
short: number input variant with chevron up/down stacked on the right and press-and-hold repeat
|
|
6
|
+
invariants: true
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# NumberInput/Vertical — agent-only invariants
|
|
10
|
+
|
|
11
|
+
`<orio-number-input-vertical>` is a pre-styled wrapper around
|
|
12
|
+
`<orio-number-input>` that renders stacked chevron up/down buttons on the
|
|
13
|
+
right edge. Read `NumberInput/USAGE.md` first — this variant inherits all
|
|
14
|
+
of its contract.
|
|
15
|
+
|
|
16
|
+
## Invariants
|
|
17
|
+
|
|
18
|
+
- **Accepts the full `NumberInputProps` interface** plus a `disabled` prop
|
|
19
|
+
for the buttons. Forwarded via `v-bind="$props"`.
|
|
20
|
+
- **Buttons use `usePressAndHold`** — `@mousedown` starts auto-repeat,
|
|
21
|
+
`@mouseup`/`@mouseleave` stops.
|
|
22
|
+
- **Chevron up = `increase`, chevron down = `decrease`.** Standard
|
|
23
|
+
direction; do not swap them in a consumer.
|
|
24
|
+
- **Buttons are stacked vertically** in a column flex (`flex-direction:
|
|
25
|
+
column; justify-content: space-around`) anchored to `right: 3px`.
|
|
26
|
+
- **Input remains left-aligned** — text stays at its natural alignment;
|
|
27
|
+
only the controls move.
|
|
28
|
+
|
|
29
|
+
## Gotchas
|
|
30
|
+
|
|
31
|
+
- **Padding-right may need a bump on long values.** The chevron stack
|
|
32
|
+
overlaps the input's right edge. For decimal values with many digits,
|
|
33
|
+
numbers may render under the buttons. Pad the input or switch to
|
|
34
|
+
`<orio-number-input-horizontal>`.
|
|
35
|
+
- **No keyboard auto-repeat.** Same limitation as the horizontal variant.
|
|
36
|
+
|
|
37
|
+
## Quick reference
|
|
38
|
+
|
|
39
|
+
```vue
|
|
40
|
+
<template>
|
|
41
|
+
<orio-number-input-vertical
|
|
42
|
+
v-model="zoomPercent"
|
|
43
|
+
:min="10"
|
|
44
|
+
:max="400"
|
|
45
|
+
:step="5"
|
|
46
|
+
:label="$t('editor.zoom')"
|
|
47
|
+
/>
|
|
48
|
+
</template>
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Related
|
|
52
|
+
|
|
53
|
+
- `<orio-number-input>` — base; use for custom controls.
|
|
54
|
+
- `<orio-number-input-horizontal>` — minus/plus variant.
|
|
55
|
+
- `usePressAndHold` — composable behind auto-repeat.
|