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.
- 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 +83 -0
- package/dist/runtime/components/Button.d.vue.ts +1 -0
- package/dist/runtime/components/Button.vue +5 -1
- package/dist/runtime/components/Button.vue.d.ts +1 -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/ControlElement.d.vue.ts +1 -1
- package/dist/runtime/components/ControlElement.vue.d.ts +1 -1
- 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/Horizontal.vue +5 -0
- package/dist/runtime/components/NumberInput/USAGE.md +74 -0
- package/dist/runtime/components/NumberInput/Vertical.USAGE.md +55 -0
- package/dist/runtime/components/NumberInput/Vertical.vue +9 -1
- 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/useControlSize.js +12 -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,79 @@
|
|
|
1
|
+
---
|
|
2
|
+
kind: component
|
|
3
|
+
category: Layout & containers
|
|
4
|
+
purpose: animation wrapper, fade/slide a slot, animated list, mount-stagger layout
|
|
5
|
+
short: flex container that fade-slides its direct children up on mount and exposes a sound `play` callback
|
|
6
|
+
invariants: true
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# AnimatedContainer — agent-only invariants
|
|
10
|
+
|
|
11
|
+
`<orio-animated-container>` is a flex layout that runs a fade-in-up CSS
|
|
12
|
+
animation on every direct child when it mounts. It is **not** a Vue
|
|
13
|
+
`<Transition>` — there is no leave animation and reactive state changes do
|
|
14
|
+
not retrigger it.
|
|
15
|
+
|
|
16
|
+
## Invariants
|
|
17
|
+
|
|
18
|
+
- **Animation fires on mount only.** The `containerFadeInUp` keyframe is a
|
|
19
|
+
plain CSS animation on `.animated-container > *`. To replay, change
|
|
20
|
+
`:key` on the container (or on the child). Re-rendering reactive content
|
|
21
|
+
inside the slot does **not** restart the animation.
|
|
22
|
+
- **Only direct children animate.** The selector is `> *`. Wrapping children
|
|
23
|
+
in an extra `<div>` moves the animation up to the wrapper. Nested deep
|
|
24
|
+
trees do not animate per-node.
|
|
25
|
+
- **Layout is opinionated.** The container is `display: flex; flex-wrap:
|
|
26
|
+
wrap; justify-content: center; gap: 1rem; padding-inline: 1rem;`. Treat it
|
|
27
|
+
as a layout primitive, not a transparent wrapper.
|
|
28
|
+
- **`direction` toggles flex-direction _and_ justification.** `"row"`
|
|
29
|
+
(default) → wrap + centered. `"column"` → `flex-direction: column` +
|
|
30
|
+
`justify-content: flex-start`.
|
|
31
|
+
- **`play` slot prop is `useSound().play`.** The default slot exposes
|
|
32
|
+
`{ play }`. Wire it to `@mouseenter` / `@click` on children for hover or
|
|
33
|
+
tap feedback. There is no prop to disable or swap the sound — it is
|
|
34
|
+
global `useSound`.
|
|
35
|
+
|
|
36
|
+
## Gotchas
|
|
37
|
+
|
|
38
|
+
- **No leave animation.** Items removed from the slot disappear instantly.
|
|
39
|
+
If you need exit animation, wrap each item in `<Transition>` or use a
|
|
40
|
+
different primitive.
|
|
41
|
+
- **Newly inserted children animate independently.** When the slot's child
|
|
42
|
+
list grows (v-for over a reactive array), each new node animates on its
|
|
43
|
+
own mount — there is no list-coordinated stagger.
|
|
44
|
+
- **Built-in horizontal padding bleeds.** `padding-inline: 1rem` is on the
|
|
45
|
+
container. If the consumer wraps it in a tight column, the inner content
|
|
46
|
+
starts 1rem in. Override with `:deep(.animated-container)` or pad the
|
|
47
|
+
parent.
|
|
48
|
+
- **`direction="column"` does not stretch children.** They still wrap by
|
|
49
|
+
default (`flex-wrap: wrap` is unchanged). Children narrower than the
|
|
50
|
+
container will not fill the row.
|
|
51
|
+
|
|
52
|
+
## Quick reference
|
|
53
|
+
|
|
54
|
+
```vue
|
|
55
|
+
<script setup lang="ts">
|
|
56
|
+
import { ref } from "vue";
|
|
57
|
+
|
|
58
|
+
const animationKey = ref(0);
|
|
59
|
+
function replay() {
|
|
60
|
+
animationKey.value += 1;
|
|
61
|
+
}
|
|
62
|
+
</script>
|
|
63
|
+
|
|
64
|
+
<template>
|
|
65
|
+
<orio-button @click="replay">Replay</orio-button>
|
|
66
|
+
|
|
67
|
+
<orio-animated-container :key="animationKey" direction="column" v-slot="{ play }">
|
|
68
|
+
<div v-for="item in items" :key="item.id" @mouseenter="play">
|
|
69
|
+
{{ item.label }}
|
|
70
|
+
</div>
|
|
71
|
+
</orio-animated-container>
|
|
72
|
+
</template>
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Related
|
|
76
|
+
|
|
77
|
+
- `useSound` composable — the source of the `play` callback. See
|
|
78
|
+
`docs/composables/use-sound.md`.
|
|
79
|
+
- Public API reference: `docs/components/animated-container.md`.
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
---
|
|
2
|
+
kind: component
|
|
3
|
+
category: Buttons & indicators
|
|
4
|
+
purpose: badge, small status pill, notification dot, count indicator, corner badge
|
|
5
|
+
short: small status pill or dot indicator; optionally positioned in the top-right corner of a wrapped element
|
|
6
|
+
invariants: true
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Badge — agent-only invariants
|
|
10
|
+
|
|
11
|
+
`<orio-badge>` renders one of two shapes:
|
|
12
|
+
- A standalone inline status pill / dot.
|
|
13
|
+
- A **positioned** badge anchored to the top-right corner of an element
|
|
14
|
+
passed via the `#wrapping` slot (typical notification-on-icon pattern).
|
|
15
|
+
|
|
16
|
+
## Invariants
|
|
17
|
+
|
|
18
|
+
- **`#wrapping` slot toggles positioning mode.** When provided, the badge
|
|
19
|
+
is rendered absolutely at `top: 15%; right: 15%; transform: translate(50%, -50%)`
|
|
20
|
+
on top of the wrapped element. Without `#wrapping`, the badge is plain
|
|
21
|
+
inline.
|
|
22
|
+
- **Default slot determines dot vs text mode.** Empty default slot → dot
|
|
23
|
+
badge (0.5rem circle, no padding). Any content → text/number badge.
|
|
24
|
+
- **`variant`**: `"primary"` (default), `"danger"`, `"alert"`, `"grey"`.
|
|
25
|
+
Maps to accent / danger / alert / surface color tokens.
|
|
26
|
+
- **`pill` prop**: switches the border-radius to pill shape; ignored when
|
|
27
|
+
in dot mode.
|
|
28
|
+
- **`hidden` prop**: gates the badge render via `v-if`. The wrapped slot
|
|
29
|
+
still renders when in wrapping mode — only the indicator hides.
|
|
30
|
+
- **No `count` prop.** Use the default slot for the number: `<orio-badge>3</orio-badge>`.
|
|
31
|
+
|
|
32
|
+
## Gotchas
|
|
33
|
+
|
|
34
|
+
- **No automatic "99+" cap.** Long content (e.g. `1234`) renders fully,
|
|
35
|
+
pushing the corner badge wider. Cap in the consumer.
|
|
36
|
+
- **Positioned badge offsets are percentages of the wrapping element.**
|
|
37
|
+
Tiny wrapped icons may have the badge clip outside the wrapper. The
|
|
38
|
+
wrapper is `position: relative; display: inline-flex` — `overflow: visible`
|
|
39
|
+
is implicit, but parent overflow may clip it.
|
|
40
|
+
- **No interactive behavior.** Click does not bubble specially; it's a
|
|
41
|
+
`<span>`. For removable chips, use `<orio-tag>` instead.
|
|
42
|
+
|
|
43
|
+
## Quick reference — corner badge on an icon
|
|
44
|
+
|
|
45
|
+
```vue
|
|
46
|
+
<template>
|
|
47
|
+
<orio-badge variant="danger">
|
|
48
|
+
<template #wrapping>
|
|
49
|
+
<orio-button icon="bell" variant="subdued" />
|
|
50
|
+
</template>
|
|
51
|
+
{{ unreadCount }}
|
|
52
|
+
</orio-badge>
|
|
53
|
+
|
|
54
|
+
<orio-badge variant="alert" :hidden="!hasUpdates">
|
|
55
|
+
<template #wrapping>
|
|
56
|
+
<orio-icon name="refresh" />
|
|
57
|
+
</template>
|
|
58
|
+
</orio-badge>
|
|
59
|
+
</template>
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Quick reference — inline status pill
|
|
63
|
+
|
|
64
|
+
```vue
|
|
65
|
+
<template>
|
|
66
|
+
<orio-badge variant="grey" pill>{{ $t("status.draft") }}</orio-badge>
|
|
67
|
+
<orio-badge variant="primary">{{ $t("status.live") }}</orio-badge>
|
|
68
|
+
</template>
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Related
|
|
72
|
+
|
|
73
|
+
- `<orio-tag>` — chip with text and optional `id` / variant for filters
|
|
74
|
+
and selections.
|
|
75
|
+
- Public API reference: `docs/components/badge.md`.
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
---
|
|
2
|
+
kind: component
|
|
3
|
+
category: Buttons & indicators
|
|
4
|
+
purpose: banner, page-level notice, alert strip, inline notification, info bar
|
|
5
|
+
short: page-level notice strip with danger/alert/success/info variants; default slot for content
|
|
6
|
+
invariants: true
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Banner — agent-only invariants
|
|
10
|
+
|
|
11
|
+
`<orio-banner>` is a plain styled `<div>` for page-level notices. No icon,
|
|
12
|
+
no close button, no auto-dismiss — bring your own.
|
|
13
|
+
|
|
14
|
+
## Invariants
|
|
15
|
+
|
|
16
|
+
- **`variant`**: `"info"` (default), `"success"`, `"alert"`, `"danger"`.
|
|
17
|
+
Each maps to a soft-background + matching border color from the
|
|
18
|
+
project's color tokens.
|
|
19
|
+
- **Default slot is the entire body.** Text, links, action buttons —
|
|
20
|
+
whatever fits the layout.
|
|
21
|
+
- **Padding (`0.75rem 1rem`) and border-radius (`--border-radius-sm`)
|
|
22
|
+
are fixed.** No size variants.
|
|
23
|
+
|
|
24
|
+
## Gotchas
|
|
25
|
+
|
|
26
|
+
- **No semantic role / ARIA live region.** The `<div>` has no
|
|
27
|
+
`role="alert"` or `role="status"`. For screen-reader-announced
|
|
28
|
+
notices, add `role="alert"` (urgent) or `role="status"` (polite) via
|
|
29
|
+
`$attrs`.
|
|
30
|
+
- **No close button.** Pair with `v-if` on the consumer side for
|
|
31
|
+
dismissable banners.
|
|
32
|
+
- **No icon prop.** Render `<orio-icon>` inside the slot if needed —
|
|
33
|
+
the banner does not auto-prepend a variant-matching glyph.
|
|
34
|
+
|
|
35
|
+
## Quick reference
|
|
36
|
+
|
|
37
|
+
```vue
|
|
38
|
+
<template>
|
|
39
|
+
<orio-banner variant="alert" role="alert" v-if="paymentFailed">
|
|
40
|
+
<strong>{{ $t("billing.failed.title") }}</strong>
|
|
41
|
+
<orio-button variant="subdued" @click="retry">
|
|
42
|
+
{{ $t("billing.failed.retry") }}
|
|
43
|
+
</orio-button>
|
|
44
|
+
</orio-banner>
|
|
45
|
+
</template>
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Related
|
|
49
|
+
|
|
50
|
+
- `<orio-empty-state>` — for empty-list placeholders.
|
|
51
|
+
- `<orio-tooltip>` — for transient hover hints.
|
|
52
|
+
- Public API reference: `docs/components/banner.md`.
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
---
|
|
2
|
+
kind: component
|
|
3
|
+
category: Buttons & indicators
|
|
4
|
+
purpose: button, primary action, CTA, icon button, action button
|
|
5
|
+
short: primary action button with variants, loading, icon slots, and auto icon-only sizing
|
|
6
|
+
invariants: true
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Button — agent-only invariants
|
|
10
|
+
|
|
11
|
+
`<orio-button>` is the primary action button. Wraps `<orio-control-element>`
|
|
12
|
+
so it can carry a label/error like other form controls, but it is most
|
|
13
|
+
often used standalone.
|
|
14
|
+
|
|
15
|
+
## Invariants
|
|
16
|
+
|
|
17
|
+
- **`variant`**: `"primary"` (default), `"secondary"`, `"subdued"`. Primary
|
|
18
|
+
is filled; secondary is outline; subdued is bare text. All use the
|
|
19
|
+
global `gradient-hover` class for the hover treatment.
|
|
20
|
+
- **`icon` prop OR `#icon` slot**: pass an icon name (registered in
|
|
21
|
+
`utils/iconRegistry`) OR slot in arbitrary content before the label.
|
|
22
|
+
Slot wins.
|
|
23
|
+
- **`#icon-right` slot**: trailing icon, rendered after the default slot.
|
|
24
|
+
- **`loading` prop**: renders `<orio-loading-spinner>` in place of all
|
|
25
|
+
slot content (icon, label, icon-right are all hidden). Clicks and
|
|
26
|
+
mousedown are blocked while loading.
|
|
27
|
+
- **Icon-only mode is auto-detected.** When an icon is present and the
|
|
28
|
+
default slot is empty, `.icon-only` is applied → `aspect-ratio: 1`,
|
|
29
|
+
`line-height: 0`. No need to pass a prop.
|
|
30
|
+
- **`pill` prop**: `false` by default. When `true`, swaps the button's
|
|
31
|
+
`border-radius` to `var(--border-radius-pill)` (fully rounded),
|
|
32
|
+
overriding the size-driven `--control-radius`. No effect when omitted.
|
|
33
|
+
- **`disabled` blocks `click` and `mousedown` emits.** `mouseup` and
|
|
34
|
+
`mouseleave` always fire (so press-and-hold callers can release
|
|
35
|
+
state).
|
|
36
|
+
- **Emits**: `click`, `mousedown`, `mouseup`, `mouseleave`. Only `click`
|
|
37
|
+
and `mousedown` honor the loading/disabled gates.
|
|
38
|
+
- **Wraps ControlElement** — supports `label`, `error`, `size`, `layout`,
|
|
39
|
+
etc. The control bag is spread on the inner `<button>` along with
|
|
40
|
+
`$attrs`.
|
|
41
|
+
|
|
42
|
+
## Gotchas
|
|
43
|
+
|
|
44
|
+
- **`$attrs` are duplicated.** Because ControlElement does **not** set
|
|
45
|
+
`inheritAttrs: false` and Button also spreads `$attrs` on the inner
|
|
46
|
+
`<button>`, an attr like `data-test="x"` may appear on both the
|
|
47
|
+
wrapper `<div>` (from ControlElement's root) and the inner `<button>`.
|
|
48
|
+
For attrs that must be unique (e.g. `data-key` for list keying,
|
|
49
|
+
`tabindex` override), bind a plain native `<button>` instead of
|
|
50
|
+
`<orio-button>`.
|
|
51
|
+
- **`type` defaults to `submit`** (native default). Inside an
|
|
52
|
+
`<orio-form>`, every `<orio-button>` will submit unless you pass
|
|
53
|
+
`type="button"`.
|
|
54
|
+
- **Loading hides the icon and the icon-right slot.** No way to keep
|
|
55
|
+
the trailing icon while showing a spinner — render the spinner
|
|
56
|
+
yourself in `#icon-right` if you need that.
|
|
57
|
+
- **No `aria-busy` on loading.** Set it via `$attrs` if you need
|
|
58
|
+
screen-reader signal.
|
|
59
|
+
|
|
60
|
+
## Quick reference
|
|
61
|
+
|
|
62
|
+
```vue
|
|
63
|
+
<template>
|
|
64
|
+
<orio-button @click="onSave" :loading="saving">
|
|
65
|
+
{{ $t("common.save") }}
|
|
66
|
+
</orio-button>
|
|
67
|
+
|
|
68
|
+
<orio-button variant="secondary" icon="trash" @click="onDelete">
|
|
69
|
+
{{ $t("common.delete") }}
|
|
70
|
+
</orio-button>
|
|
71
|
+
|
|
72
|
+
<orio-button variant="subdued" icon="close" aria-label="Close" />
|
|
73
|
+
|
|
74
|
+
<orio-button pill icon="plus" aria-label="Add" />
|
|
75
|
+
</template>
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Related
|
|
79
|
+
|
|
80
|
+
- `<orio-nav-button>` — link-styled button with `active` state.
|
|
81
|
+
- `<orio-loading-spinner>` — used internally when `loading` is true.
|
|
82
|
+
- `<orio-icon>` — used internally for the `icon` prop and slots.
|
|
83
|
+
- Public API reference: `docs/components/button.md`.
|
|
@@ -4,6 +4,7 @@ const props = defineProps({
|
|
|
4
4
|
variant: { type: String, required: false, default: "primary" },
|
|
5
5
|
icon: { type: String, required: false },
|
|
6
6
|
loading: { type: Boolean, required: false },
|
|
7
|
+
pill: { type: Boolean, required: false },
|
|
7
8
|
appearance: { type: String, required: false },
|
|
8
9
|
error: { type: [String, null], required: false },
|
|
9
10
|
group: { type: Boolean, required: false },
|
|
@@ -47,7 +48,7 @@ function onMouseleave(event) {
|
|
|
47
48
|
<orio-control-element v-slot="{ control }" v-bind="props">
|
|
48
49
|
<button
|
|
49
50
|
v-bind="{ ...$attrs, ...control }"
|
|
50
|
-
:class="[variant, 'gradient-hover', { 'icon-only': isIconOnly }]"
|
|
51
|
+
:class="[variant, 'gradient-hover', { 'icon-only': isIconOnly, pill }]"
|
|
51
52
|
@click="click"
|
|
52
53
|
@mousedown="onMousedown"
|
|
53
54
|
@mouseup="onMouseup"
|
|
@@ -85,6 +86,9 @@ button.icon-only {
|
|
|
85
86
|
line-height: 0;
|
|
86
87
|
aspect-ratio: 1;
|
|
87
88
|
}
|
|
89
|
+
button.pill {
|
|
90
|
+
border-radius: var(--border-radius-pill);
|
|
91
|
+
}
|
|
88
92
|
button:disabled, button:disabled:hover {
|
|
89
93
|
background-color: var(--color-accent-soft-base);
|
|
90
94
|
color: var(--color-muted);
|
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
---
|
|
2
|
+
kind: component
|
|
3
|
+
category: Date
|
|
4
|
+
purpose: month calendar, date grid, day picker UI
|
|
5
|
+
short: month grid with roving-focus keyboard a11y
|
|
6
|
+
invariants: true
|
|
7
|
+
---
|
|
8
|
+
|
|
1
9
|
# Calendar — agent-only invariants
|
|
2
10
|
|
|
3
11
|
`<orio-calendar>` is the month-grid primitive. `date/Picker.vue` and
|
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
---
|
|
2
|
+
kind: component
|
|
3
|
+
category: Layout & containers
|
|
4
|
+
purpose: canvas, drawing board, whiteboard, sketch, freeform editor, pluggable tools
|
|
5
|
+
short: pannable workspace with pluggable tools and detached toolbar registry
|
|
6
|
+
invariants: true
|
|
7
|
+
---
|
|
8
|
+
|
|
1
9
|
# Canvas — agent-only invariants
|
|
2
10
|
|
|
3
11
|
Read this before integrating `<orio-canvas>` into a consumer app. Public API
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
---
|
|
2
|
+
kind: component
|
|
3
|
+
category: Form inputs
|
|
4
|
+
purpose: single checkbox, boolean toggle, opt-in
|
|
5
|
+
short: single boolean checkbox wrapping ControlElement; custom check icon via prop or slot
|
|
6
|
+
invariants: true
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# CheckBox — agent-only invariants
|
|
10
|
+
|
|
11
|
+
`<orio-check-box>` is a single boolean checkbox. The native `<input
|
|
12
|
+
type="checkbox">` is visually hidden; the rendered tick lives in a sibling
|
|
13
|
+
`<span class="checkbox-box">`. Read `ControlElement.USAGE.md` first.
|
|
14
|
+
|
|
15
|
+
## Invariants
|
|
16
|
+
|
|
17
|
+
- **v-model is `boolean`**, not required (renders as unchecked when
|
|
18
|
+
unbound).
|
|
19
|
+
- **Default slot is the label text** rendered to the right of the box.
|
|
20
|
+
- **Default tick is a CSS rotate-45-border checkmark** drawn in
|
|
21
|
+
`::after`, applied when no `checkedIcon` is passed.
|
|
22
|
+
- **`checkedIcon` / `uncheckedIcon` props** swap in `<orio-icon>` glyphs
|
|
23
|
+
for either state. Pass icon names registered in `utils/iconRegistry`.
|
|
24
|
+
- **`#icon` slot** lets you render arbitrary indicator content; receives
|
|
25
|
+
`{ checked }`. Overrides both the default tick and any icon props.
|
|
26
|
+
- **ControlElement is passed `fill`** so the checkbox row fills the wrapper
|
|
27
|
+
width. Label slot of ControlElement is bypassed — the visible label is
|
|
28
|
+
the CheckBox's own default slot, not ControlElement's `label` prop.
|
|
29
|
+
- **`--box-size` defaults to `var(--control-icon-size, 1rem)`**. Override
|
|
30
|
+
the CSS var on the host to resize the box.
|
|
31
|
+
|
|
32
|
+
## Gotchas
|
|
33
|
+
|
|
34
|
+
- **The native input has `tabindex="-1"`** in the template, but the
|
|
35
|
+
styles target `:focus-visible` on it. Keyboard focus for the checkbox
|
|
36
|
+
may not behave the way an a11y audit expects — confirm tab order
|
|
37
|
+
before shipping critical forms. If you need keyboard-focusable
|
|
38
|
+
checkboxes, override `tabindex` via `$attrs`.
|
|
39
|
+
- **`required` from `ControlProps` flows through**, but native checkbox
|
|
40
|
+
required validation only fires inside a `<form>` that calls
|
|
41
|
+
`reportValidity`.
|
|
42
|
+
- **No indeterminate state.** v-model is strictly boolean; the
|
|
43
|
+
underlying input never gets `indeterminate = true`.
|
|
44
|
+
- **Passing both `checkedIcon` and `#icon` slot** — the slot wins; the
|
|
45
|
+
prop becomes dead code.
|
|
46
|
+
|
|
47
|
+
## Quick reference
|
|
48
|
+
|
|
49
|
+
```vue
|
|
50
|
+
<template>
|
|
51
|
+
<orio-check-box v-model="agreed" :error="errors.terms">
|
|
52
|
+
{{ $t("signup.acceptTerms") }}
|
|
53
|
+
</orio-check-box>
|
|
54
|
+
|
|
55
|
+
<orio-check-box v-model="bookmarked" checked-icon="bookmark-filled" unchecked-icon="bookmark" />
|
|
56
|
+
</template>
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Related
|
|
60
|
+
|
|
61
|
+
- `<orio-checkbox-group>` — multi-value group of checkboxes.
|
|
62
|
+
- `<orio-control-element>` — wrapper; owns label/error/a11y.
|
|
63
|
+
- Public API reference: `docs/components/checkbox.md`.
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
---
|
|
2
|
+
kind: component
|
|
3
|
+
category: Form inputs
|
|
4
|
+
purpose: group of checkboxes, multi-value boolean group, multi-select boolean
|
|
5
|
+
short: group of CheckBox children bound to an `unknown[]` v-model; supports `options` prop or default slot
|
|
6
|
+
invariants: true
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# CheckboxGroup — agent-only invariants
|
|
10
|
+
|
|
11
|
+
`<orio-checkbox-group>` wires a list of checkboxes to a single array
|
|
12
|
+
v-model. It uses `<orio-control-element group>` so the wrapper renders as
|
|
13
|
+
a `role="group"` with an `aria-labelledby` label — semantically a fieldset
|
|
14
|
+
group, not a `<fieldset>` element.
|
|
15
|
+
|
|
16
|
+
## Invariants
|
|
17
|
+
|
|
18
|
+
- **v-model is `unknown[]`** (default `[]`). Each entry is one option's
|
|
19
|
+
`value`. Comparison is strict `===` — primitives or shared references,
|
|
20
|
+
not deep equality.
|
|
21
|
+
- **Two modes**: `options` prop (array of `{ label, value }`) **or** the
|
|
22
|
+
default slot (you render `<orio-check-box>` children yourself). The slot
|
|
23
|
+
wins when present.
|
|
24
|
+
- **In `options` mode**, each rendered checkbox is `appearance="minimal"`
|
|
25
|
+
(hardcoded). To change appearance, switch to the slot.
|
|
26
|
+
- **Toggle replaces the array**: `modelValue.value = modelValue.value.filter(...)`
|
|
27
|
+
/ `[...modelValue.value, value]`. Reactive arrays survive; readonly arrays
|
|
28
|
+
break silently.
|
|
29
|
+
- **Wrapper omits `appearance`, `group`, `id` from `ControlProps`.**
|
|
30
|
+
`group` is forced true; `id` is internal.
|
|
31
|
+
- **Defaults**: `layout: "vertical"`, `size: "md"`, `error: null`.
|
|
32
|
+
- **Horizontal layout aligns label top** (`align-items: flex-start`) so the
|
|
33
|
+
label sits next to the first checkbox, not centered against the full
|
|
34
|
+
column.
|
|
35
|
+
|
|
36
|
+
## Gotchas
|
|
37
|
+
|
|
38
|
+
- **`isChecked` uses `Array.includes` with `===`.** Object option values
|
|
39
|
+
must be the **same reference** as in the model, not a structural match.
|
|
40
|
+
For object values, store ids and resolve to objects in the parent.
|
|
41
|
+
- **No "select all" / "indeterminate parent" affordance.** Build it in the
|
|
42
|
+
consumer if needed.
|
|
43
|
+
- **Label rendering depends on `group` mode in ControlElement** — the
|
|
44
|
+
label becomes a `<span>` with `aria-labelledby` wiring, not a `<legend>`.
|
|
45
|
+
Screen readers announce it as a group label.
|
|
46
|
+
- **`error` is forwarded** to the group wrapper; per-checkbox errors are
|
|
47
|
+
not supported. Surface validation at the group level.
|
|
48
|
+
|
|
49
|
+
## Quick reference — options prop
|
|
50
|
+
|
|
51
|
+
```vue
|
|
52
|
+
<script setup lang="ts">
|
|
53
|
+
const interests = defineModel<string[]>({ default: () => [] });
|
|
54
|
+
const options = [
|
|
55
|
+
{ label: "Books", value: "books" },
|
|
56
|
+
{ label: "Films", value: "films" },
|
|
57
|
+
{ label: "Music", value: "music" },
|
|
58
|
+
];
|
|
59
|
+
</script>
|
|
60
|
+
|
|
61
|
+
<template>
|
|
62
|
+
<orio-checkbox-group
|
|
63
|
+
v-model="interests"
|
|
64
|
+
:options="options"
|
|
65
|
+
:label="$t('profile.interests')"
|
|
66
|
+
/>
|
|
67
|
+
</template>
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Quick reference — slot mode (custom rendering)
|
|
71
|
+
|
|
72
|
+
```vue
|
|
73
|
+
<template>
|
|
74
|
+
<orio-checkbox-group v-model="features" :label="$t('settings.features')">
|
|
75
|
+
<orio-check-box
|
|
76
|
+
v-for="feature in availableFeatures"
|
|
77
|
+
:key="feature.id"
|
|
78
|
+
:model-value="features.includes(feature.id)"
|
|
79
|
+
checked-icon="star-filled"
|
|
80
|
+
unchecked-icon="star"
|
|
81
|
+
@update:model-value="toggle(feature.id)"
|
|
82
|
+
>
|
|
83
|
+
{{ feature.name }}
|
|
84
|
+
</orio-check-box>
|
|
85
|
+
</orio-checkbox-group>
|
|
86
|
+
</template>
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Related
|
|
90
|
+
|
|
91
|
+
- `<orio-check-box>` — single checkbox; rendered children inside this
|
|
92
|
+
group.
|
|
93
|
+
- `<orio-control-element>` (`group` mode) — wrapper; provides the group
|
|
94
|
+
label semantics.
|
|
95
|
+
- Public API reference: `docs/components/checkbox-group.md`.
|
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
---
|
|
2
|
+
kind: component
|
|
3
|
+
category: Form inputs
|
|
4
|
+
purpose: label + error + a11y wrapper for any form control
|
|
5
|
+
short: label/legend wrapper, owns a11y attrs, exposes the `control` slot prop bag
|
|
6
|
+
invariants: true
|
|
7
|
+
---
|
|
8
|
+
|
|
1
9
|
# ControlElement — agent-only invariants
|
|
2
10
|
|
|
3
11
|
`ControlElement` is the wrapper every form input uses (Input, Textarea,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export type ControlLayout = "vertical" | "horizontal";
|
|
2
|
-
export type ControlSize = "sm" | "md" | "lg" | "xl";
|
|
2
|
+
export type ControlSize = "xs" | "sm" | "md" | "lg" | "xl";
|
|
3
3
|
/**
|
|
4
4
|
* A11y + form attrs that flow from the caller through ControlElement to the
|
|
5
5
|
* actual interactive element via the `control` slot prop. Never rendered on
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export type ControlLayout = "vertical" | "horizontal";
|
|
2
|
-
export type ControlSize = "sm" | "md" | "lg" | "xl";
|
|
2
|
+
export type ControlSize = "xs" | "sm" | "md" | "lg" | "xl";
|
|
3
3
|
/**
|
|
4
4
|
* A11y + form attrs that flow from the caller through ControlElement to the
|
|
5
5
|
* actual interactive element via the `control` slot prop. Never rendered on
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
---
|
|
2
|
+
kind: component
|
|
3
|
+
category: Layout & containers
|
|
4
|
+
purpose: dashed empty/drop zone, add-item card, upload tile, empty state with action
|
|
5
|
+
short: clickable dashed-border tile with icon and label, used for add/upload affordances
|
|
6
|
+
invariants: true
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# DashedContainer — agent-only invariants
|
|
10
|
+
|
|
11
|
+
`<orio-dashed-container>` is a self-styled clickable tile that renders an
|
|
12
|
+
optional icon and a label inside a dashed border. It is **not** a generic
|
|
13
|
+
slot wrapper.
|
|
14
|
+
|
|
15
|
+
## Invariants
|
|
16
|
+
|
|
17
|
+
- **No default slot.** The template only renders `icon` and `text` props.
|
|
18
|
+
Children placed between the tags are dropped. To compose richer content,
|
|
19
|
+
use a different primitive.
|
|
20
|
+
- **Always clickable.** The wrapper has `cursor: pointer` and emits
|
|
21
|
+
`click` unconditionally — even with no listener attached, the tile looks
|
|
22
|
+
interactive. Do not use as a passive container.
|
|
23
|
+
- **`size` only scales the icon.** `small` → 2rem, `medium` → 3rem, `large`
|
|
24
|
+
→ 5rem. Padding (2rem), gap (0.5rem), text size, and border are fixed
|
|
25
|
+
regardless of `size`.
|
|
26
|
+
- **`icon` is forwarded to `<orio-icon :name>`.** Must be a name registered
|
|
27
|
+
in `utils/iconRegistry`. Missing names render nothing; check the registry
|
|
28
|
+
before passing a string.
|
|
29
|
+
- **Hover effect comes from the global `gradient-hover` class**, not from
|
|
30
|
+
scoped styles. The dashed border, padding, and layout are scoped; the
|
|
31
|
+
hover gradient is project-global.
|
|
32
|
+
|
|
33
|
+
## Gotchas
|
|
34
|
+
|
|
35
|
+
- **`text` defaults to English in consumer code.** Project convention
|
|
36
|
+
(see CLAUDE.md / translations note) is to pass an i18n key: `:text="$t('addItem')"`,
|
|
37
|
+
not `text="Add Item"`.
|
|
38
|
+
- **Both `icon` and `text` are optional.** With neither, the tile is an
|
|
39
|
+
empty dashed box that still emits `click`. Confirm at least one is set
|
|
40
|
+
unless that empty look is intentional.
|
|
41
|
+
- **The `<span :size>` shorthand** in the template forwards `size` as a
|
|
42
|
+
DOM attribute on the label. It's not styled — harmless, but visible
|
|
43
|
+
in devtools.
|
|
44
|
+
- **No `disabled` state.** If a consumer needs disabled-looking behaviour,
|
|
45
|
+
wrap or override styles externally; do not rely on a prop.
|
|
46
|
+
|
|
47
|
+
## Quick reference
|
|
48
|
+
|
|
49
|
+
```vue
|
|
50
|
+
<template>
|
|
51
|
+
<orio-dashed-container
|
|
52
|
+
icon="plus"
|
|
53
|
+
:text="$t('gallery.addImage')"
|
|
54
|
+
size="medium"
|
|
55
|
+
@click="openPicker"
|
|
56
|
+
/>
|
|
57
|
+
</template>
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Related
|
|
61
|
+
|
|
62
|
+
- `orio-icon` — icon renderer driven by the same `name` string.
|
|
63
|
+
- `orio-upload` — full file-picker widget; prefer it over hand-rolling
|
|
64
|
+
click-to-upload on a `DashedContainer`.
|
|
65
|
+
- Public API reference: `docs/components/dashed-container.md`.
|