orio-ui 1.24.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 +78 -3
- 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/Button.d.vue.ts +3 -2
- package/dist/runtime/components/Button.vue +19 -11
- package/dist/runtime/components/Button.vue.d.ts +3 -2
- package/dist/runtime/components/Calendar.USAGE.md +59 -0
- package/dist/runtime/components/Calendar.vue +254 -87
- package/dist/runtime/components/Canvas/USAGE.md +73 -0
- package/dist/runtime/components/CheckBox.USAGE.md +63 -0
- package/dist/runtime/components/CheckBox.vue +9 -3
- package/dist/runtime/components/CheckboxGroup.USAGE.md +95 -0
- package/dist/runtime/components/CheckboxGroup.vue +7 -1
- package/dist/runtime/components/ControlElement.USAGE.md +77 -0
- package/dist/runtime/components/ControlElement.d.vue.ts +42 -27
- package/dist/runtime/components/ControlElement.vue +28 -9
- package/dist/runtime/components/ControlElement.vue.d.ts +42 -27
- 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 +57 -0
- package/dist/runtime/components/Input.vue +13 -3
- 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 +72 -0
- package/dist/runtime/components/NavButton.USAGE.md +80 -0
- package/dist/runtime/components/NavButton.d.vue.ts +0 -1
- package/dist/runtime/components/NavButton.vue +9 -5
- package/dist/runtime/components/NavButton.vue.d.ts +0 -1
- package/dist/runtime/components/NumberInput/Horizontal.USAGE.md +61 -0
- package/dist/runtime/components/NumberInput/Horizontal.vue +7 -2
- 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 +7 -2
- package/dist/runtime/components/NumberInput/index.d.vue.ts +0 -2
- package/dist/runtime/components/NumberInput/index.vue +9 -7
- package/dist/runtime/components/NumberInput/index.vue.d.ts +0 -2
- package/dist/runtime/components/Popover.USAGE.md +103 -0
- package/dist/runtime/components/RadioButton.USAGE.md +72 -0
- package/dist/runtime/components/RadioButton.d.vue.ts +0 -2
- package/dist/runtime/components/RadioButton.vue +9 -4
- package/dist/runtime/components/RadioButton.vue.d.ts +0 -2
- package/dist/runtime/components/Selector.USAGE.md +131 -0
- package/dist/runtime/components/Selector.d.vue.ts +1 -0
- package/dist/runtime/components/Selector.vue +10 -4
- package/dist/runtime/components/Selector.vue.d.ts +1 -0
- package/dist/runtime/components/SwitchButton.USAGE.md +62 -0
- package/dist/runtime/components/SwitchButton.d.vue.ts +1 -4
- package/dist/runtime/components/SwitchButton.vue +10 -7
- package/dist/runtime/components/SwitchButton.vue.d.ts +1 -4
- package/dist/runtime/components/Tag.USAGE.md +51 -0
- package/dist/runtime/components/TaggableSelector.USAGE.md +73 -0
- package/dist/runtime/components/TaggableSelector.vue +7 -1
- package/dist/runtime/components/Textarea.USAGE.md +72 -0
- package/dist/runtime/components/Textarea.vue +13 -3
- 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 +52 -0
- package/dist/runtime/components/date/Picker.vue +7 -1
- package/dist/runtime/components/date/PickerTrigger.USAGE.md +65 -0
- package/dist/runtime/components/date/PickerTrigger.vue +9 -3
- package/dist/runtime/components/date/RangePicker.USAGE.md +97 -0
- package/dist/runtime/components/date/RangePicker.vue +7 -1
- 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/useFilter.d.ts +91 -0
- package/dist/runtime/composables/useFilter.js +111 -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/useRovingGrid.d.ts +35 -0
- package/dist/runtime/composables/useRovingGrid.js +115 -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/dist/runtime/i18n/en.json +4 -1
- package/dist/runtime/i18n/uk.json +4 -1
- package/package.json +12 -2
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
---
|
|
2
|
+
kind: component
|
|
3
|
+
category: Date
|
|
4
|
+
purpose: date range, from-to picker, date range input, calendar range
|
|
5
|
+
short: two-month range picker with hover-preview, min/max bounds, and ISO `{ start, end }` model
|
|
6
|
+
invariants: true
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# date/RangePicker — agent-only invariants
|
|
10
|
+
|
|
11
|
+
`<orio-date-range-picker>` is the date range picker: a
|
|
12
|
+
`<orio-date-picker-trigger>` opens a popover containing **two side-by-side
|
|
13
|
+
calendars** (left = start month, right = start month + 1). Read
|
|
14
|
+
`Calendar.USAGE.md` and `date/Picker.USAGE.md` first.
|
|
15
|
+
|
|
16
|
+
## Invariants
|
|
17
|
+
|
|
18
|
+
- **v-model is `DateRange = { start: string | null; end: string | null }`**
|
|
19
|
+
(ISO date strings). `DateRange` is re-exported from this file. Both
|
|
20
|
+
fields can be null (no selection) or just `start` (mid-pick).
|
|
21
|
+
- **Click sequence is "set start → set end".** First click clears `end`,
|
|
22
|
+
writes `start`. Second click writes `end`. If the second pick is
|
|
23
|
+
earlier than the existing start, start and end **swap automatically**.
|
|
24
|
+
- **The popover closes on range completion** (second pick), via
|
|
25
|
+
`toggle(false)`. A single click leaves it open.
|
|
26
|
+
- **Two-month anchored display** — `leftAnchor` = first of start's month,
|
|
27
|
+
`rightAnchor` = `leftAnchor + 1 month`. They re-sync when the model's
|
|
28
|
+
start changes to a month not currently visible.
|
|
29
|
+
- **Hover preview**: hovering a day in the popover renders an "accent"
|
|
30
|
+
range marker from `start` to the hovered day (or hovered day to start
|
|
31
|
+
if the hover is earlier). Requires `start` to be already picked.
|
|
32
|
+
- **`min` / `max` are ISO strings** that gate selection via the calendar's
|
|
33
|
+
`isDisabled`. Consumer's own `isDisabled(iso)` predicate ORs in.
|
|
34
|
+
- **`getMarker(iso)` is the consumer's marker provider.** The preview
|
|
35
|
+
marker **wins** over consumer markers for days inside the previewed
|
|
36
|
+
range.
|
|
37
|
+
- **Built on `<orio-calendar>` × 2**, side-by-side in a flex row with
|
|
38
|
+
0.75rem gap.
|
|
39
|
+
- **i18n key**: `dateRangePicker.placeholder` for the empty-display label.
|
|
40
|
+
- **Display string**: `"start – end"` when both exist (en-dash, spaces).
|
|
41
|
+
Just `start` or just `end` if only one. Empty string if both null.
|
|
42
|
+
|
|
43
|
+
## Gotchas
|
|
44
|
+
|
|
45
|
+
- **The picked `start` does NOT render a marker by itself** when no `end`
|
|
46
|
+
and no hover — `previewMarker` requires both `previewStart` and
|
|
47
|
+
`previewEnd`. There is no visible feedback that a start was picked
|
|
48
|
+
until the user hovers or clicks an end. If you need a "just-start"
|
|
49
|
+
indicator, pass it via `markers` from the consumer.
|
|
50
|
+
- **Both calendars share the same `markers`, `get-marker`, and
|
|
51
|
+
`is-disabled` props.** Consumer markers spanning across months draw
|
|
52
|
+
correctly because they're date-based, not anchor-based.
|
|
53
|
+
- **Hover state clears on `mouseleave` of the popover content**, not on
|
|
54
|
+
picking. Quick double-click sequences clear hover only after the
|
|
55
|
+
second click.
|
|
56
|
+
- **No keyboard support for range selection** — arrow-key roving inside
|
|
57
|
+
Calendar still works, but Enter on the keyboard-focused day picks one
|
|
58
|
+
end at a time, mirroring mouse behavior.
|
|
59
|
+
- **Min/max bounds are ISO string comparisons**: `iso < min` works
|
|
60
|
+
because ISO dates sort lexicographically. Pass YYYY-MM-DD strings, not
|
|
61
|
+
arbitrary Date objects.
|
|
62
|
+
|
|
63
|
+
## Quick reference
|
|
64
|
+
|
|
65
|
+
```vue
|
|
66
|
+
<script setup lang="ts">
|
|
67
|
+
import type { DateRange } from "../components/date/RangePicker.vue";
|
|
68
|
+
|
|
69
|
+
const range = defineModel<DateRange>({
|
|
70
|
+
default: () => ({ start: null, end: null }),
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
const bookedDays = ["2026-06-15", "2026-06-16"];
|
|
74
|
+
function isDisabled(iso: string) {
|
|
75
|
+
return bookedDays.includes(iso);
|
|
76
|
+
}
|
|
77
|
+
</script>
|
|
78
|
+
|
|
79
|
+
<template>
|
|
80
|
+
<orio-date-range-picker
|
|
81
|
+
v-model="range"
|
|
82
|
+
:label="$t('booking.dates')"
|
|
83
|
+
min="2026-06-10"
|
|
84
|
+
max="2026-12-31"
|
|
85
|
+
:is-disabled="isDisabled"
|
|
86
|
+
/>
|
|
87
|
+
</template>
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Related
|
|
91
|
+
|
|
92
|
+
- `<orio-calendar>` — the underlying grid; rendered twice.
|
|
93
|
+
- `<orio-date-picker>` — single-date variant.
|
|
94
|
+
- `<orio-date-picker-trigger>` — the shared trigger button.
|
|
95
|
+
- `utils/date` — `DateRange`, `parseISO`, `formatISO`, `addMonths`,
|
|
96
|
+
`startOfMonth`, `formatDate`.
|
|
97
|
+
- Public API reference: `docs/components/date/`.
|
|
@@ -22,7 +22,13 @@ const props = defineProps({
|
|
|
22
22
|
label: { type: String, required: false },
|
|
23
23
|
layout: { type: String, required: false },
|
|
24
24
|
size: { type: String, required: false },
|
|
25
|
-
fill: { type: Boolean, required: false }
|
|
25
|
+
fill: { type: Boolean, required: false },
|
|
26
|
+
tabindex: { type: [Number, String], required: false },
|
|
27
|
+
focusKey: { type: String, required: false },
|
|
28
|
+
disabled: { type: Boolean, required: false },
|
|
29
|
+
required: { type: Boolean, required: false },
|
|
30
|
+
name: { type: String, required: false },
|
|
31
|
+
ariaLabel: { type: String, required: false }
|
|
26
32
|
});
|
|
27
33
|
const range = defineModel({ type: Object, ...{
|
|
28
34
|
default: () => ({ start: null, end: null })
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
---
|
|
2
|
+
kind: component
|
|
3
|
+
category: Media & misc
|
|
4
|
+
purpose: carousel, image slider, gallery, lightbox slider, image viewer
|
|
5
|
+
short: image carousel with swipe gestures, prev/next buttons, dynamic sizing, and per-image slot
|
|
6
|
+
invariants: true
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# gallery/Carousel — agent-only invariants
|
|
10
|
+
|
|
11
|
+
`<orio-gallery-carousel>` cycles through a list of images with swipe and
|
|
12
|
+
prev/next buttons. Pair it with `<orio-gallery-carousel-preview>` for a
|
|
13
|
+
thumbnail strip that binds to the same `activeImage` model.
|
|
14
|
+
|
|
15
|
+
## Invariants
|
|
16
|
+
|
|
17
|
+
- **v-model name is `activeImage`** and the type is `string` (the image
|
|
18
|
+
URL or id), **not** an index. Bind via `v-model:active-image="..."`.
|
|
19
|
+
- **`size` prop is a `"W:H"` string** parsed as `width:height` in
|
|
20
|
+
pixels:
|
|
21
|
+
- `"400:550"` → fixed 400×550.
|
|
22
|
+
- `"400:"` (empty after colon) → fixed width, **dynamic height** —
|
|
23
|
+
measured from the slot content via a hidden `.carousel-measure`
|
|
24
|
+
container.
|
|
25
|
+
- `":550"` → fixed height, dynamic width.
|
|
26
|
+
- **`fit`**: `"contain"` (default), `"fill"`, `"cover"`, `"scale-down"`.
|
|
27
|
+
Applied as `object-fit` to the inner image via `v-bind(fit)` CSS
|
|
28
|
+
binding.
|
|
29
|
+
- **`appearance`**: `"default"` (border + background) or `"minimal"` (no
|
|
30
|
+
border, no background; prev/next buttons appear only on hover).
|
|
31
|
+
- **Swipe threshold is 10px** of horizontal pointer movement. Drag-right
|
|
32
|
+
→ `previousImage`, drag-left → `nextImage`. Below threshold = no
|
|
33
|
+
change.
|
|
34
|
+
- **Looping is implicit.** `nextImage` past the last → first, `previousImage`
|
|
35
|
+
before the first → last. No flag to disable.
|
|
36
|
+
- **Only 3 items are visible at once**: previous (translated −100%),
|
|
37
|
+
active (0), next (translated +100%). All others have `opacity: 0;
|
|
38
|
+
pointer-events: none`.
|
|
39
|
+
- **`#image` slot** overrides the default `<img>` render. Receives `{ image }`.
|
|
40
|
+
Use for videos, captions, complex viewer markup. Slotted content is
|
|
41
|
+
also rendered into the hidden measure container when `size` has a
|
|
42
|
+
dynamic dimension.
|
|
43
|
+
- **Auto-init on mount**: if `activeImage` is unbound or empty, it is
|
|
44
|
+
set to `images[0]`.
|
|
45
|
+
- **Switch buttons only render when `images.length > 1`.**
|
|
46
|
+
- **Transitions**: opacity + transform, 0.5s ease-in-out.
|
|
47
|
+
- **`max-height` clamp**: when both dimensions are fixed, the carousel
|
|
48
|
+
scales down to `carouselWidth / aspectRatio` to respect the parent
|
|
49
|
+
width while preserving the aspect.
|
|
50
|
+
|
|
51
|
+
## Gotchas
|
|
52
|
+
|
|
53
|
+
- **Image URLs must be unique** — they are used as v-for keys and the
|
|
54
|
+
active-image model. Duplicate URLs collapse to one logical slide.
|
|
55
|
+
- **No keyboard arrow nav.** Swipe + click only. Add `@keydown` on a
|
|
56
|
+
parent if needed.
|
|
57
|
+
- **Switch buttons use `mix-blend-mode: difference`** on supporting
|
|
58
|
+
browsers (not Safari) to remain visible over any image. Custom themes
|
|
59
|
+
may need to override the `.switch-button :deep(.icon)` styles.
|
|
60
|
+
- **Dynamic sizing causes a one-frame measurement flicker** while the
|
|
61
|
+
hidden measure container resolves. For non-changing content, prefer a
|
|
62
|
+
fixed `size` like `"400:550"`.
|
|
63
|
+
- **The carousel `<img>` has `alt="image-url"`** by default — visually
|
|
64
|
+
fine but bad for accessibility. Override via `#image` slot to render
|
|
65
|
+
proper alt text.
|
|
66
|
+
|
|
67
|
+
## Quick reference
|
|
68
|
+
|
|
69
|
+
```vue
|
|
70
|
+
<script setup lang="ts">
|
|
71
|
+
const images = [
|
|
72
|
+
"/photos/1.jpg",
|
|
73
|
+
"/photos/2.jpg",
|
|
74
|
+
"/photos/3.jpg",
|
|
75
|
+
];
|
|
76
|
+
const active = ref(images[0]);
|
|
77
|
+
</script>
|
|
78
|
+
|
|
79
|
+
<template>
|
|
80
|
+
<orio-gallery-carousel
|
|
81
|
+
v-model:active-image="active"
|
|
82
|
+
:images="images"
|
|
83
|
+
size="600:"
|
|
84
|
+
fit="contain"
|
|
85
|
+
/>
|
|
86
|
+
<orio-gallery-carousel-preview
|
|
87
|
+
v-model:active-image="active"
|
|
88
|
+
:images="images"
|
|
89
|
+
/>
|
|
90
|
+
</template>
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Related
|
|
94
|
+
|
|
95
|
+
- `<orio-gallery-carousel-preview>` — thumbnail strip bound to the same
|
|
96
|
+
active-image model.
|
|
97
|
+
- `<orio-modal>` — wrap a carousel for lightbox viewing.
|
|
98
|
+
- Public API reference: `docs/components/gallery/`.
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
---
|
|
2
|
+
kind: component
|
|
3
|
+
category: Media & misc
|
|
4
|
+
purpose: carousel preview, thumbnails strip, image picker strip, gallery thumbnails
|
|
5
|
+
short: horizontal thumbnail strip for the Carousel; clicking a thumb updates the shared `activeImage` model
|
|
6
|
+
invariants: true
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# gallery/CarouselPreview — agent-only invariants
|
|
10
|
+
|
|
11
|
+
`<orio-gallery-carousel-preview>` renders a horizontal scrollable strip of
|
|
12
|
+
thumbnails for `<orio-gallery-carousel>`. Bind both components to the same
|
|
13
|
+
`v-model:active-image` and they stay in sync.
|
|
14
|
+
|
|
15
|
+
## Invariants
|
|
16
|
+
|
|
17
|
+
- **v-model name is `activeImage`** (same as the Carousel) and the type
|
|
18
|
+
is `string`. Share the same ref between them.
|
|
19
|
+
- **Hidden when `images.length ≤ 1`.** Single-image galleries don't
|
|
20
|
+
render a strip.
|
|
21
|
+
- **Each thumbnail is a `<button>`** with `aria-pressed` (true when
|
|
22
|
+
active) and `aria-label` `"Show image N of M"` for screen readers.
|
|
23
|
+
- **Thumbnails are 3.5rem × 3.5rem** with `object-fit` driven by `fit`
|
|
24
|
+
(default `"cover"`, unlike Carousel's `"contain"` default).
|
|
25
|
+
- **`#image` slot** overrides the default `<img>` render. Receives
|
|
26
|
+
`{ image }`. Same signature as the Carousel slot.
|
|
27
|
+
- **Strip scrolls horizontally** with `overflow-x: auto`. No
|
|
28
|
+
auto-scroll-to-active — clicking a thumb that's offscreen won't
|
|
29
|
+
scroll it into view.
|
|
30
|
+
- **Active thumb gets**: opacity 1, accent border. Inactive: opacity
|
|
31
|
+
0.6 with a hover bump to 0.85.
|
|
32
|
+
|
|
33
|
+
## Gotchas
|
|
34
|
+
|
|
35
|
+
- **No keyboard arrow nav between thumbs.** Tab moves between buttons;
|
|
36
|
+
Enter / Space activates. Add roving-focus if needed.
|
|
37
|
+
- **No auto-scroll on active change.** If the consumer changes
|
|
38
|
+
`activeImage` from elsewhere, the strip doesn't follow — scroll it
|
|
39
|
+
into view yourself via `element.scrollIntoView()`.
|
|
40
|
+
- **Alt is `""`** on thumbnails by default — they're treated as
|
|
41
|
+
decorative because the `<button>` carries the accessible name.
|
|
42
|
+
|
|
43
|
+
## Quick reference
|
|
44
|
+
|
|
45
|
+
See `<orio-gallery-carousel>` USAGE.md.
|
|
46
|
+
|
|
47
|
+
## Related
|
|
48
|
+
|
|
49
|
+
- `<orio-gallery-carousel>` — the main viewer; share the
|
|
50
|
+
`activeImage` model.
|
|
51
|
+
- Public API reference: `docs/components/gallery/`.
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
---
|
|
2
|
+
kind: component
|
|
3
|
+
category: Media & misc
|
|
4
|
+
purpose: upload, file picker, drop-to-upload, file input, headless file upload
|
|
5
|
+
short: headless file upload — provides drop-zone state and file-dialog opener via slot props; consumer renders the UI
|
|
6
|
+
invariants: true
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# upload — agent-only invariants
|
|
10
|
+
|
|
11
|
+
`<orio-upload>` is a **headless** file-upload component. It owns drag-drop
|
|
12
|
+
detection and file-dialog opening; the consumer renders all UI through the
|
|
13
|
+
default slot. There is no built-in look.
|
|
14
|
+
|
|
15
|
+
## Invariants
|
|
16
|
+
|
|
17
|
+
- **Template is essentially `<div><slot :is-over-drop-zone :open-dialog /></div>`.**
|
|
18
|
+
No styling, no built-in drop hint, no preview list.
|
|
19
|
+
- **Slot props**:
|
|
20
|
+
- `isOverDropZone: boolean` — true while a dragged file is over the
|
|
21
|
+
zone (and the component is not disabled).
|
|
22
|
+
- `openDialog: () => void` — opens the native file picker.
|
|
23
|
+
- **v-model is `File[]`** (default `[]`). Drops and dialog selections
|
|
24
|
+
**append** to it; the array is then sliced to `maxFiles` if set.
|
|
25
|
+
- **`maxFiles`**:
|
|
26
|
+
- `undefined` (default) → unlimited.
|
|
27
|
+
- `> 1` → multi-select mode (drop & dialog).
|
|
28
|
+
- `1` → single-file mode; new selections replace the array (capped to
|
|
29
|
+
length 1 by the slice).
|
|
30
|
+
- **`allowedTypes`** is forwarded as `dataTypes` to `useDropZone`
|
|
31
|
+
(drop filter) and as `accept` (comma-joined) to the native dialog.
|
|
32
|
+
Be explicit — passing MIME-type strings (`"image/png"`) vs.
|
|
33
|
+
extensions (`".png"`) is the consumer's choice.
|
|
34
|
+
- **`disabled`** blocks both drop and `openDialog` calls. `isOverDropZone`
|
|
35
|
+
is also forced `false` while disabled so the slot UI doesn't flash an
|
|
36
|
+
"active" state during a no-op.
|
|
37
|
+
- **The whole template div is the drop zone.** The slot content sits
|
|
38
|
+
inside it; the consumer's hit area equals whatever they render.
|
|
39
|
+
|
|
40
|
+
## Gotchas
|
|
41
|
+
|
|
42
|
+
- **No UI at all by default.** A bare `<orio-upload v-model="files" />`
|
|
43
|
+
renders an empty `<div>` — clicking does nothing. You must provide a
|
|
44
|
+
default slot that calls `openDialog`.
|
|
45
|
+
- **Drops append**, including duplicates. Same-named files are added
|
|
46
|
+
again; dedupe in the consumer if needed.
|
|
47
|
+
- **`maxFiles` only enforces on append**. If the model is pre-populated
|
|
48
|
+
with more files than `maxFiles`, they stick around until the next
|
|
49
|
+
drop / dialog truncates them.
|
|
50
|
+
- **`useFileDialog` uses native input.** It's not styleable. The "dialog"
|
|
51
|
+
is the OS chooser; styling lives on the trigger element you render in
|
|
52
|
+
the slot.
|
|
53
|
+
- **No progress / upload semantics.** This component only collects File
|
|
54
|
+
objects. Uploading them to a server is the consumer's job.
|
|
55
|
+
- **`accept` attribute on the dialog vs. drop filter divergence**: the
|
|
56
|
+
drop filter is enforced by browser drag-drop semantics; the dialog's
|
|
57
|
+
`accept` is a hint, not a hard filter — users can choose any file via
|
|
58
|
+
the chooser depending on OS.
|
|
59
|
+
|
|
60
|
+
## Quick reference
|
|
61
|
+
|
|
62
|
+
```vue
|
|
63
|
+
<script setup lang="ts">
|
|
64
|
+
const files = ref<File[]>([]);
|
|
65
|
+
</script>
|
|
66
|
+
|
|
67
|
+
<template>
|
|
68
|
+
<orio-upload
|
|
69
|
+
v-model="files"
|
|
70
|
+
:max-files="5"
|
|
71
|
+
:allowed-types="['image/png', 'image/jpeg']"
|
|
72
|
+
>
|
|
73
|
+
<template #default="{ isOverDropZone, openDialog }">
|
|
74
|
+
<orio-dashed-container
|
|
75
|
+
:icon="isOverDropZone ? 'drop' : 'upload'"
|
|
76
|
+
:text="$t(isOverDropZone ? 'upload.drop' : 'upload.choose')"
|
|
77
|
+
@click="openDialog"
|
|
78
|
+
/>
|
|
79
|
+
</template>
|
|
80
|
+
</orio-upload>
|
|
81
|
+
|
|
82
|
+
<ul>
|
|
83
|
+
<li v-for="(file, index) in files" :key="index">{{ file.name }}</li>
|
|
84
|
+
</ul>
|
|
85
|
+
</template>
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Related
|
|
89
|
+
|
|
90
|
+
- `<orio-dashed-container>` — common UI shell for upload tiles.
|
|
91
|
+
- Public API reference: `docs/components/upload.md` (if present).
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
---
|
|
2
|
+
kind: component
|
|
3
|
+
category: Media & misc
|
|
4
|
+
purpose: read-only date display, formatted date range, date range view
|
|
5
|
+
short: locale-aware read-only date or date range display; inline `<orio-view-text>` for start/end
|
|
6
|
+
invariants: true
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# view/Dates — agent-only invariants
|
|
10
|
+
|
|
11
|
+
`<orio-view-dates>` renders a `DateRange` (`{ start, end }`) as inline
|
|
12
|
+
formatted text using the active vue-i18n locale.
|
|
13
|
+
|
|
14
|
+
## Invariants
|
|
15
|
+
|
|
16
|
+
- **`dates` is required** and typed `DateRange` (ISO strings).
|
|
17
|
+
- **Format options**:
|
|
18
|
+
- Default: `{ day: "numeric", month: "short", year: "numeric" }` →
|
|
19
|
+
`"10 Jun 2026"`.
|
|
20
|
+
- `month: true` → omits the day → `"Jun 2026"`. Use for
|
|
21
|
+
month-resolution ranges (subscription periods, etc.).
|
|
22
|
+
- **Uses `formatDate(iso, locale, options)` from `utils/date`**. Output
|
|
23
|
+
follows the locale — `en` vs `uk` will render different month
|
|
24
|
+
abbreviations.
|
|
25
|
+
- **Separator**: literal `" - "` rendered between start and end when
|
|
26
|
+
both are present. No en-dash, no localization.
|
|
27
|
+
- **Renders two `<orio-view-text>`** with `type` and `size` forwarded
|
|
28
|
+
(defaults `type: "italics"`, `size: "small"`).
|
|
29
|
+
- **`* { display: inline }`** on the wrapper forces both view-text
|
|
30
|
+
blocks inline so they read as one sentence.
|
|
31
|
+
|
|
32
|
+
## Gotchas
|
|
33
|
+
|
|
34
|
+
- **Only start, only end, or both**: rendering gracefully handles a
|
|
35
|
+
missing `end` (no separator, no second block). A missing `start` with
|
|
36
|
+
an `end` renders the separator alone — degraded UX.
|
|
37
|
+
- **No relative formatting** (e.g. "yesterday", "3 days ago"). For
|
|
38
|
+
relative output, format in the consumer and pass via
|
|
39
|
+
`<orio-view-text>` instead.
|
|
40
|
+
- **`size` is forwarded to view-text** but the wrapper itself has no
|
|
41
|
+
size. Custom CSS that targets the wrapper won't see a size class.
|
|
42
|
+
|
|
43
|
+
## Quick reference
|
|
44
|
+
|
|
45
|
+
```vue
|
|
46
|
+
<script setup lang="ts">
|
|
47
|
+
const period = { start: "2026-06-01", end: "2026-06-30" };
|
|
48
|
+
</script>
|
|
49
|
+
|
|
50
|
+
<template>
|
|
51
|
+
<orio-view-dates :dates="period" />
|
|
52
|
+
|
|
53
|
+
<orio-view-dates
|
|
54
|
+
:dates="{ start: subscriptionStart, end: subscriptionEnd }"
|
|
55
|
+
month
|
|
56
|
+
type="title"
|
|
57
|
+
size="medium"
|
|
58
|
+
/>
|
|
59
|
+
</template>
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Related
|
|
63
|
+
|
|
64
|
+
- `<orio-view-text>` — used internally for each end of the range.
|
|
65
|
+
- `<orio-date-range-picker>` — picker that produces `DateRange` values.
|
|
66
|
+
- `utils/date` — `formatDate`, `DateRange` type.
|
|
67
|
+
- Public API reference: `docs/components/view/dates.md` (if present).
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
---
|
|
2
|
+
kind: component
|
|
3
|
+
category: Media & misc
|
|
4
|
+
purpose: keyboard bindings hint display, shortcut display, kbd renderer
|
|
5
|
+
short: parses a backtick-delimited shortcut string and renders each key as `<kbd>` with separators inline
|
|
6
|
+
invariants: true
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# view/KeyBinds — agent-only invariants
|
|
10
|
+
|
|
11
|
+
`<orio-view-key-binds>` parses a backtick-delimited string like
|
|
12
|
+
`` "`Ctrl` + `Z`" `` and renders each backticked token as a `<kbd>`
|
|
13
|
+
element with the surrounding text as separator.
|
|
14
|
+
|
|
15
|
+
## Invariants
|
|
16
|
+
|
|
17
|
+
- **`bind` is the single string prop.** Tokens between backticks (`` `…` ``)
|
|
18
|
+
become `<kbd>` elements; everything else renders as a `.separator`
|
|
19
|
+
`<span>`.
|
|
20
|
+
- **Regex is `/`([^`]+)`/g`** — non-greedy match inside backticks.
|
|
21
|
+
Empty backticks (`` `` ``) and unmatched openings are passed through
|
|
22
|
+
as plain text.
|
|
23
|
+
- **No tokenization beyond backticks.** `"+"`, `" "`, `","`, `"or"`
|
|
24
|
+
between keys all render as plain separator text. Style them via the
|
|
25
|
+
`.separator` class.
|
|
26
|
+
- **Output structure**: one `<span class="keybinds">` wrapper, with
|
|
27
|
+
`<kbd>` and `<span class="separator">` children inline. Wrapper is
|
|
28
|
+
`inline-flex` with 0.2rem gap.
|
|
29
|
+
- **Kbd styling** is fixed: rgba white background tint, small font,
|
|
30
|
+
border. Designed for dark surfaces — over a light background, the
|
|
31
|
+
contrast may be poor; override `kbd` styles via global CSS.
|
|
32
|
+
|
|
33
|
+
## Gotchas
|
|
34
|
+
|
|
35
|
+
- **The string is rendered as-is.** No localization, no key-symbol
|
|
36
|
+
substitution (e.g. `Cmd` does not become `⌘`). Build that mapping in
|
|
37
|
+
the consumer if needed.
|
|
38
|
+
- **No `aria-label`.** Screen readers read each `<kbd>` token aloud
|
|
39
|
+
with the separator text — usually fine for `"Ctrl + Z"`, less great
|
|
40
|
+
for `" or "`-separated alternates.
|
|
41
|
+
- **Mismatched backticks render as text.** `` "`Ctrl + Z" `` (missing
|
|
42
|
+
closing tick) becomes plain text starting from the unmatched
|
|
43
|
+
backtick.
|
|
44
|
+
|
|
45
|
+
## Quick reference
|
|
46
|
+
|
|
47
|
+
```vue
|
|
48
|
+
<template>
|
|
49
|
+
<orio-view-key-binds bind="`Ctrl` + `Z`" />
|
|
50
|
+
<orio-view-key-binds bind="press `Esc` to close" />
|
|
51
|
+
<orio-view-key-binds bind="`Cmd` + `Shift` + `P` or `F1`" />
|
|
52
|
+
</template>
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Related
|
|
56
|
+
|
|
57
|
+
- Public API reference: `docs/components/view/key-binds.md` (if
|
|
58
|
+
present).
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
---
|
|
2
|
+
kind: component
|
|
3
|
+
category: Media & misc
|
|
4
|
+
purpose: separator, divider, horizontal rule, divider line
|
|
5
|
+
short: horizontal separator line with configurable border style, size in px, and block margin in rem
|
|
6
|
+
invariants: false
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# view/Separator — agent-only invariants
|
|
10
|
+
|
|
11
|
+
`<orio-view-separator>` is a horizontal rule rendered as a `<div>` with a
|
|
12
|
+
`border-block-end`. It is **not** an `<hr>` element.
|
|
13
|
+
|
|
14
|
+
## Invariants
|
|
15
|
+
|
|
16
|
+
- **`style` prop** is the CSS border style: `"solid"` (default),
|
|
17
|
+
`"dotted"`, `"dashed"`, `"double"`, `"groove"`, `"ridge"`.
|
|
18
|
+
- **`size`** (number, default `1`) is the border width in **pixels**.
|
|
19
|
+
- **`margin`** (number, default `1`) is the **rem** spacing above and
|
|
20
|
+
below via `margin-block`. So `margin: 1` → `1rem` top + `1rem`
|
|
21
|
+
bottom.
|
|
22
|
+
- **Color is `var(--color-border)`** — not themable per-instance. Use
|
|
23
|
+
CSS overrides if you need a different color.
|
|
24
|
+
- **Block-direction aware**: `border-block-end` and `margin-block`
|
|
25
|
+
respect the writing mode. In a horizontal writing mode it's a bottom
|
|
26
|
+
border + vertical margins; in vertical writing modes it flips.
|
|
27
|
+
|
|
28
|
+
## Gotchas
|
|
29
|
+
|
|
30
|
+
- **Renders a `<div>`, not an `<hr>`.** Screen readers may not announce
|
|
31
|
+
a section break. For semantic separation, add `role="separator"` via
|
|
32
|
+
`$attrs`.
|
|
33
|
+
- **`size` is unitless number → px** by template binding. Strings get
|
|
34
|
+
used verbatim (`"2px"` works, but loses the type signal).
|
|
35
|
+
- **No vertical orientation.** For a vertical divider, write a custom
|
|
36
|
+
`<div>` with `border-inline-start` rather than using this component.
|
|
37
|
+
|
|
38
|
+
## Quick reference
|
|
39
|
+
|
|
40
|
+
```vue
|
|
41
|
+
<template>
|
|
42
|
+
<p>First section</p>
|
|
43
|
+
|
|
44
|
+
<orio-view-separator />
|
|
45
|
+
|
|
46
|
+
<p>Second section</p>
|
|
47
|
+
|
|
48
|
+
<orio-view-separator style="dashed" :size="2" :margin="2" />
|
|
49
|
+
|
|
50
|
+
<p>Third section</p>
|
|
51
|
+
</template>
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Related
|
|
55
|
+
|
|
56
|
+
- Public API reference: `docs/components/view/separator.md` (if
|
|
57
|
+
present).
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
---
|
|
2
|
+
kind: component
|
|
3
|
+
category: Media & misc
|
|
4
|
+
purpose: read-only text display, formatted view, typography primitive, label
|
|
5
|
+
short: typed text primitive (text/title/subtitle/italics) with size, uppercase, line-clamp, and inline icon
|
|
6
|
+
invariants: true
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# view/Text — agent-only invariants
|
|
10
|
+
|
|
11
|
+
`<orio-view-text>` is the typography primitive for read-only labels and
|
|
12
|
+
text blocks. Use it instead of bare `<p>` / `<span>` / `<h*>` for inline
|
|
13
|
+
icons, theming, and line-clamping.
|
|
14
|
+
|
|
15
|
+
## Invariants
|
|
16
|
+
|
|
17
|
+
- **`type`**: `"text"` (default) / `"title"` (bold) / `"subtitle"`
|
|
18
|
+
(semi-bold, muted) / `"italics"` (italic, muted). Affects font-weight,
|
|
19
|
+
style, and color.
|
|
20
|
+
- **`size`**: `"small"` / `"medium"` (default) / `"large"` /
|
|
21
|
+
`"extra-large"`. Maps to `--font-sm` … `--font-xl` tokens.
|
|
22
|
+
- **`uppercase: true`** applies `text-transform: uppercase`.
|
|
23
|
+
- **`icon` prop** renders `<orio-icon>` inline before the text/slot. The
|
|
24
|
+
wrapper is `display: flex; align-items: center; gap: 0.25rem` so the
|
|
25
|
+
icon sits inline with the text.
|
|
26
|
+
- **`lineClamp` (number or string)** enables `-webkit-line-clamp` line
|
|
27
|
+
truncation with ellipsis. Defaults to 1 line when the prop is present
|
|
28
|
+
but unset.
|
|
29
|
+
- **Content sources**: default slot or `v-model:modelValue` string. Slot
|
|
30
|
+
wins. v-model is bound so the component plays nicely with
|
|
31
|
+
`<orio-form>` auto-bind by `name`.
|
|
32
|
+
- **`--view-text-color` CSS var** overrides the type's default color
|
|
33
|
+
without changing the type prop. Useful for accent/error states from
|
|
34
|
+
the consumer.
|
|
35
|
+
- **No `inheritAttrs: false`** — attrs flow to the wrapper `<div>`.
|
|
36
|
+
|
|
37
|
+
## Gotchas
|
|
38
|
+
|
|
39
|
+
- **The wrapper is a `<div>`, not a heading element.** Title type does
|
|
40
|
+
not produce an `<h1>` / `<h2>` etc. For semantic headings, write
|
|
41
|
+
`<h2><orio-view-text type="title">...</orio-view-text></h2>` or use a
|
|
42
|
+
plain heading element.
|
|
43
|
+
- **`lineClamp` requires multi-line content** to show the ellipsis. A
|
|
44
|
+
single-line value with `lineClamp: 2` simply renders as one line.
|
|
45
|
+
- **`white-space: pre-wrap`** is set globally on the wrapper —
|
|
46
|
+
whitespace and newlines from the source are preserved.
|
|
47
|
+
|
|
48
|
+
## Quick reference
|
|
49
|
+
|
|
50
|
+
```vue
|
|
51
|
+
<template>
|
|
52
|
+
<orio-view-text type="title" size="large">
|
|
53
|
+
{{ $t("article.title") }}
|
|
54
|
+
</orio-view-text>
|
|
55
|
+
|
|
56
|
+
<orio-view-text type="subtitle" :line-clamp="3" icon="info">
|
|
57
|
+
{{ description }}
|
|
58
|
+
</orio-view-text>
|
|
59
|
+
|
|
60
|
+
<orio-view-text type="italics" uppercase v-model="badgeLabel" />
|
|
61
|
+
</template>
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Related
|
|
65
|
+
|
|
66
|
+
- `<orio-view-dates>` — locale-aware date range display built on top.
|
|
67
|
+
- `<orio-empty-state>` — uses this internally for title and description.
|
|
68
|
+
- Public API reference: `docs/components/view/text.md` (if present).
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
---
|
|
2
|
+
kind: composable
|
|
3
|
+
category: Composables
|
|
4
|
+
purpose: fetch, API client, HTTP request, JSON fetch
|
|
5
|
+
short: thin typed wrapper around ofetch's `$fetch` for GET/POST/PUT/DELETE/PATCH requests
|
|
6
|
+
invariants: false
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# useApi — agent-only invariants
|
|
10
|
+
|
|
11
|
+
`useApi` is a typed async function (not a reactive composable) that
|
|
12
|
+
wraps `ofetch`'s `$fetch`. It returns a `Promise<T>` — no loading/error
|
|
13
|
+
refs, no Vue lifecycle.
|
|
14
|
+
|
|
15
|
+
## Invariants
|
|
16
|
+
|
|
17
|
+
- **Not a Vue composable.** It is a plain typed async function, safe to
|
|
18
|
+
call anywhere — outside `setup`, inside event handlers, in route
|
|
19
|
+
middleware.
|
|
20
|
+
- **Two overloads**: `useApi<T>(url)` for GET, `useApi<T>(url, options)`
|
|
21
|
+
for everything else.
|
|
22
|
+
- **`ApiOptions`** fields:
|
|
23
|
+
- `method`: `"GET"` (default), `"POST"`, `"PUT"`, `"DELETE"`, `"PATCH"`.
|
|
24
|
+
- `body`: `Record<string, unknown>` — JSON object. Not FormData, not
|
|
25
|
+
a string.
|
|
26
|
+
- `query`: `Record<string, unknown>` — appended as query params.
|
|
27
|
+
- `signal`: `AbortSignal` for cancellation.
|
|
28
|
+
- **Return type is `T`** — pass the generic for type safety. Without
|
|
29
|
+
it, you get `unknown`.
|
|
30
|
+
|
|
31
|
+
## Gotchas
|
|
32
|
+
|
|
33
|
+
- **No retry, no caching, no de-dupe.** Bring `@tanstack/vue-query` or
|
|
34
|
+
similar if you need those.
|
|
35
|
+
- **`body` does not handle non-JSON payloads.** For file uploads,
|
|
36
|
+
use `fetch` directly or extend the composable.
|
|
37
|
+
- **Errors throw.** Wrap calls in try/catch — `ofetch` throws on non-2xx
|
|
38
|
+
responses.
|
|
39
|
+
|
|
40
|
+
## Quick reference
|
|
41
|
+
|
|
42
|
+
```ts
|
|
43
|
+
import { useApi } from "../composables/useApi";
|
|
44
|
+
|
|
45
|
+
interface User { id: string; name: string }
|
|
46
|
+
|
|
47
|
+
const user = await useApi<User>("/api/users/123");
|
|
48
|
+
|
|
49
|
+
const created = await useApi<User>("/api/users", {
|
|
50
|
+
method: "POST",
|
|
51
|
+
body: { name: "Vlad" },
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
const controller = new AbortController();
|
|
55
|
+
const results = await useApi<User[]>("/api/users", {
|
|
56
|
+
query: { search: "vl" },
|
|
57
|
+
signal: controller.signal,
|
|
58
|
+
});
|
|
59
|
+
controller.abort(); // cancels the request
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Related
|
|
63
|
+
|
|
64
|
+
- Public API reference: `docs/composables/use-api.md` (if present).
|