orio-ui 1.18.0 → 1.19.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 +6 -6
- package/dist/module.json +1 -1
- package/dist/runtime/components/AnimatedContainer.d.vue.ts +1 -1
- package/dist/runtime/components/AnimatedContainer.vue.d.ts +1 -1
- package/dist/runtime/components/ControlElement.vue +5 -49
- package/dist/runtime/components/Form.d.vue.ts +33 -0
- package/dist/runtime/components/Form.vue +102 -0
- package/dist/runtime/components/Form.vue.d.ts +33 -0
- package/dist/runtime/components/Selector.d.vue.ts +2 -2
- package/dist/runtime/components/Selector.vue +4 -2
- package/dist/runtime/components/Selector.vue.d.ts +2 -2
- package/dist/runtime/components/gallery/Carousel.vue +9 -9
- package/dist/runtime/composables/useControlSize.d.ts +9 -0
- package/dist/runtime/composables/useControlSize.js +67 -0
- package/dist/runtime/composables/useSound.d.ts +3 -1
- package/dist/runtime/composables/useSound.js +49 -9
- package/dist/runtime/index.d.ts +1 -0
- package/dist/runtime/index.js +1 -0
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -8,12 +8,12 @@ A delightful, lightweight component library for Nuxt 3+ applications. Built with
|
|
|
8
8
|
|
|
9
9
|
## Features
|
|
10
10
|
|
|
11
|
-
✨ **
|
|
11
|
+
✨ **32 Components** - Beautiful, accessible components ready to use
|
|
12
12
|
🎨 **Themeable** - 5 built-in accent themes with light/dark mode support
|
|
13
13
|
🚀 **Auto-imported** - Works seamlessly with Nuxt's auto-import system
|
|
14
14
|
📦 **Tree-shakeable** - Only bundle what you use
|
|
15
15
|
🎯 **TypeScript** - Fully typed for great developer experience
|
|
16
|
-
🧪 **Tested** -
|
|
16
|
+
🧪 **Tested** - 29 test suites for reliability
|
|
17
17
|
📱 **Responsive** - Mobile-first design approach
|
|
18
18
|
♿ **Accessible** - ARIA-compliant components
|
|
19
19
|
|
|
@@ -66,7 +66,7 @@ function handleClick() {
|
|
|
66
66
|
|
|
67
67
|
## What's Included
|
|
68
68
|
|
|
69
|
-
### Components (
|
|
69
|
+
### Components (32)
|
|
70
70
|
|
|
71
71
|
#### Form Controls
|
|
72
72
|
|
|
@@ -112,7 +112,7 @@ function handleClick() {
|
|
|
112
112
|
|
|
113
113
|
- **Upload** - File upload component
|
|
114
114
|
|
|
115
|
-
### Composables (
|
|
115
|
+
### Composables (11)
|
|
116
116
|
|
|
117
117
|
- **useTheme** - Theme and color mode management
|
|
118
118
|
- **useModal** - Modal state with animation origin tracking
|
|
@@ -187,8 +187,8 @@ npm run docs:dev
|
|
|
187
187
|
orio-ui/
|
|
188
188
|
├── src/
|
|
189
189
|
│ ├── runtime/
|
|
190
|
-
│ │ ├── components/ #
|
|
191
|
-
│ │ ├── composables/ #
|
|
190
|
+
│ │ ├── components/ # 32 Vue components
|
|
191
|
+
│ │ ├── composables/ # 11 composables
|
|
192
192
|
│ │ ├── assets/css/ # Theme CSS files
|
|
193
193
|
│ │ └── utils/ # Icon registry
|
|
194
194
|
│ └── module.ts # Nuxt Module definition
|
package/dist/module.json
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<script setup>
|
|
2
|
-
import { useId } from "vue";
|
|
2
|
+
import { computed, toRef, useId } from "vue";
|
|
3
|
+
import { provideControlSize, sizeTokens } from "../composables/useControlSize";
|
|
3
4
|
defineOptions({ inheritAttrs: false });
|
|
4
5
|
const props = defineProps({
|
|
5
6
|
appearance: { type: String, required: false, default: "normal" },
|
|
@@ -10,12 +11,15 @@ const props = defineProps({
|
|
|
10
11
|
layout: { type: String, required: false, default: "vertical" },
|
|
11
12
|
size: { type: String, required: false, default: "md" }
|
|
12
13
|
});
|
|
14
|
+
provideControlSize(toRef(props, "size"));
|
|
15
|
+
const sizeStyle = computed(() => sizeTokens[props.size]);
|
|
13
16
|
</script>
|
|
14
17
|
|
|
15
18
|
<template>
|
|
16
19
|
<div
|
|
17
20
|
class="control"
|
|
18
21
|
:class="[appearance, layout, `size-${size}`, { 'has-error': error, group }]"
|
|
22
|
+
:style="sizeStyle"
|
|
19
23
|
v-bind="{
|
|
20
24
|
...$attrs,
|
|
21
25
|
...group ? { role: 'group', ...label ? { 'aria-labelledby': id } : {} } : {}
|
|
@@ -39,54 +43,6 @@ const props = defineProps({
|
|
|
39
43
|
</template>
|
|
40
44
|
|
|
41
45
|
<style scoped>
|
|
42
|
-
.control {
|
|
43
|
-
--control-font-size: var(--font-md);
|
|
44
|
-
--control-label-font-size: var(--font-sm);
|
|
45
|
-
--control-py: 0.5rem;
|
|
46
|
-
--control-px: 0.75rem;
|
|
47
|
-
--control-gap: 0.5rem;
|
|
48
|
-
--control-radius: var(--border-radius-md);
|
|
49
|
-
--control-icon-size: 1rem;
|
|
50
|
-
--control-inner-block-start: 1.25rem;
|
|
51
|
-
--control-inner-block-end: 0.2rem;
|
|
52
|
-
--control-label-block-start: 0.25rem;
|
|
53
|
-
}
|
|
54
|
-
.control.size-sm {
|
|
55
|
-
--control-font-size: var(--font-sm);
|
|
56
|
-
--control-label-font-size: var(--font-xs);
|
|
57
|
-
--control-py: 0.25rem;
|
|
58
|
-
--control-px: 0.5rem;
|
|
59
|
-
--control-gap: 0.25rem;
|
|
60
|
-
--control-radius: var(--border-radius-sm);
|
|
61
|
-
--control-icon-size: 0.75rem;
|
|
62
|
-
--control-inner-block-start: 1rem;
|
|
63
|
-
--control-inner-block-end: 0.1rem;
|
|
64
|
-
--control-label-block-start: 0.2rem;
|
|
65
|
-
}
|
|
66
|
-
.control.size-lg {
|
|
67
|
-
--control-font-size: var(--font-lg);
|
|
68
|
-
--control-label-font-size: var(--font-md);
|
|
69
|
-
--control-py: 0.625rem;
|
|
70
|
-
--control-px: 1rem;
|
|
71
|
-
--control-gap: 0.5rem;
|
|
72
|
-
--control-radius: var(--border-radius-md);
|
|
73
|
-
--control-icon-size: 1.25rem;
|
|
74
|
-
--control-inner-block-start: 1.1rem;
|
|
75
|
-
--control-inner-block-end: 0.2rem;
|
|
76
|
-
--control-label-block-start: 0.25rem;
|
|
77
|
-
}
|
|
78
|
-
.control.size-xl {
|
|
79
|
-
--control-font-size: var(--font-xl);
|
|
80
|
-
--control-label-font-size: var(--font-lg);
|
|
81
|
-
--control-py: 0.75rem;
|
|
82
|
-
--control-px: 1.25rem;
|
|
83
|
-
--control-gap: 0.75rem;
|
|
84
|
-
--control-radius: var(--border-radius-lg);
|
|
85
|
-
--control-icon-size: 1.5rem;
|
|
86
|
-
--control-inner-block-start: 1.5rem;
|
|
87
|
-
--control-inner-block-end: 0;
|
|
88
|
-
--control-label-block-start: 0.25rem;
|
|
89
|
-
}
|
|
90
46
|
.control {
|
|
91
47
|
margin: 0.5rem;
|
|
92
48
|
display: flex;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export interface FormProps {
|
|
2
|
+
/**
|
|
3
|
+
* Disables all form controls and the submit button
|
|
4
|
+
*/
|
|
5
|
+
disabled?: boolean;
|
|
6
|
+
/**
|
|
7
|
+
* Shows a loading state (e.g. during async submission)
|
|
8
|
+
*/
|
|
9
|
+
loading?: boolean;
|
|
10
|
+
}
|
|
11
|
+
declare const __VLS_export: <T extends object>(__VLS_props: NonNullable<Awaited<typeof __VLS_setup>>["props"], __VLS_ctx?: __VLS_PrettifyLocal<Pick<NonNullable<Awaited<typeof __VLS_setup>>, "attrs" | "emit" | "slots">>, __VLS_exposed?: NonNullable<Awaited<typeof __VLS_setup>>["expose"], __VLS_setup?: Promise<{
|
|
12
|
+
props: import("vue").PublicProps & __VLS_PrettifyLocal<(FormProps & {
|
|
13
|
+
modelValue?: T;
|
|
14
|
+
}) & {
|
|
15
|
+
"onUpdate:modelValue"?: ((value: T | undefined) => any) | undefined;
|
|
16
|
+
onSubmit?: (() => any) | undefined;
|
|
17
|
+
}> & (typeof globalThis extends {
|
|
18
|
+
__VLS_PROPS_FALLBACK: infer P;
|
|
19
|
+
} ? P : {});
|
|
20
|
+
expose: (exposed: {}) => void;
|
|
21
|
+
attrs: any;
|
|
22
|
+
slots: {};
|
|
23
|
+
emit: ((evt: "submit") => void) & ((event: "update:modelValue", value: T | undefined) => void);
|
|
24
|
+
}>) => import("vue").VNode & {
|
|
25
|
+
__ctx?: Awaited<typeof __VLS_setup>;
|
|
26
|
+
};
|
|
27
|
+
declare const _default: typeof __VLS_export;
|
|
28
|
+
export default _default;
|
|
29
|
+
type __VLS_PrettifyLocal<T> = (T extends any ? {
|
|
30
|
+
[K in keyof T]: T[K];
|
|
31
|
+
} : {
|
|
32
|
+
[K in keyof T as K]: T[K];
|
|
33
|
+
}) & {};
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { useSlots, cloneVNode } from "vue";
|
|
3
|
+
const { disabled = false, loading = false } = defineProps({
|
|
4
|
+
disabled: { type: Boolean, required: false },
|
|
5
|
+
loading: { type: Boolean, required: false }
|
|
6
|
+
});
|
|
7
|
+
const modelValue = defineModel({ type: null });
|
|
8
|
+
const emit = defineEmits(["submit"]);
|
|
9
|
+
const slots = useSlots();
|
|
10
|
+
function getByPath(obj, path) {
|
|
11
|
+
return path.split(".").reduce((current, key) => current?.[key], obj);
|
|
12
|
+
}
|
|
13
|
+
function setByPath(obj, path, value) {
|
|
14
|
+
const keys = path.split(".");
|
|
15
|
+
const lastKey = keys.pop();
|
|
16
|
+
const parent = keys.reduce((current, key) => current?.[key], obj);
|
|
17
|
+
if (parent) parent[lastKey] = value;
|
|
18
|
+
}
|
|
19
|
+
function wrapSlotFn(fn) {
|
|
20
|
+
return (...args) => bindFields(fn(...args));
|
|
21
|
+
}
|
|
22
|
+
function processChildren(children) {
|
|
23
|
+
if (Array.isArray(children)) {
|
|
24
|
+
return bindFields(children);
|
|
25
|
+
}
|
|
26
|
+
if (typeof children === "function") {
|
|
27
|
+
return wrapSlotFn(children);
|
|
28
|
+
}
|
|
29
|
+
if (children && typeof children === "object") {
|
|
30
|
+
const slotObj = children;
|
|
31
|
+
return Object.fromEntries(
|
|
32
|
+
Object.entries(slotObj).map(([key, fn]) => [key, wrapSlotFn(fn)])
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
return children;
|
|
36
|
+
}
|
|
37
|
+
function bindFields(vnodes) {
|
|
38
|
+
return vnodes.map((vnode) => {
|
|
39
|
+
const name = vnode.props?.name;
|
|
40
|
+
if (name && modelValue.value && getByPath(modelValue.value, name) !== void 0) {
|
|
41
|
+
return cloneVNode(vnode, {
|
|
42
|
+
modelValue: getByPath(modelValue.value, name),
|
|
43
|
+
"onUpdate:modelValue": (newValue) => {
|
|
44
|
+
setByPath(modelValue.value, name, newValue);
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
if (vnode.children) {
|
|
49
|
+
const clone = cloneVNode(vnode);
|
|
50
|
+
clone.children = processChildren(vnode.children);
|
|
51
|
+
return clone;
|
|
52
|
+
}
|
|
53
|
+
return vnode;
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
function renderSlot() {
|
|
57
|
+
const children = slots.default?.();
|
|
58
|
+
if (!children || !modelValue.value) return children;
|
|
59
|
+
return bindFields(children);
|
|
60
|
+
}
|
|
61
|
+
const slotRenderer = () => renderSlot();
|
|
62
|
+
function onSubmit(e) {
|
|
63
|
+
e.preventDefault();
|
|
64
|
+
if (disabled || loading) return;
|
|
65
|
+
emit("submit");
|
|
66
|
+
}
|
|
67
|
+
</script>
|
|
68
|
+
|
|
69
|
+
<template>
|
|
70
|
+
<form
|
|
71
|
+
:class="{ disabled, loading }"
|
|
72
|
+
:aria-disabled="disabled || void 0"
|
|
73
|
+
:aria-busy="loading || void 0"
|
|
74
|
+
novalidate
|
|
75
|
+
@submit="onSubmit"
|
|
76
|
+
>
|
|
77
|
+
<fieldset v-if="disabled" disabled>
|
|
78
|
+
<component :is="slotRenderer" />
|
|
79
|
+
</fieldset>
|
|
80
|
+
<component :is="slotRenderer" v-else />
|
|
81
|
+
</form>
|
|
82
|
+
</template>
|
|
83
|
+
|
|
84
|
+
<style scoped>
|
|
85
|
+
form {
|
|
86
|
+
display: flex;
|
|
87
|
+
flex-direction: column;
|
|
88
|
+
width: 100%;
|
|
89
|
+
}
|
|
90
|
+
form.disabled {
|
|
91
|
+
opacity: 0.6;
|
|
92
|
+
}
|
|
93
|
+
form.loading {
|
|
94
|
+
pointer-events: none;
|
|
95
|
+
}
|
|
96
|
+
form fieldset {
|
|
97
|
+
display: contents;
|
|
98
|
+
border: none;
|
|
99
|
+
padding: 0;
|
|
100
|
+
margin: 0;
|
|
101
|
+
}
|
|
102
|
+
</style>
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export interface FormProps {
|
|
2
|
+
/**
|
|
3
|
+
* Disables all form controls and the submit button
|
|
4
|
+
*/
|
|
5
|
+
disabled?: boolean;
|
|
6
|
+
/**
|
|
7
|
+
* Shows a loading state (e.g. during async submission)
|
|
8
|
+
*/
|
|
9
|
+
loading?: boolean;
|
|
10
|
+
}
|
|
11
|
+
declare const __VLS_export: <T extends object>(__VLS_props: NonNullable<Awaited<typeof __VLS_setup>>["props"], __VLS_ctx?: __VLS_PrettifyLocal<Pick<NonNullable<Awaited<typeof __VLS_setup>>, "attrs" | "emit" | "slots">>, __VLS_exposed?: NonNullable<Awaited<typeof __VLS_setup>>["expose"], __VLS_setup?: Promise<{
|
|
12
|
+
props: import("vue").PublicProps & __VLS_PrettifyLocal<(FormProps & {
|
|
13
|
+
modelValue?: T;
|
|
14
|
+
}) & {
|
|
15
|
+
"onUpdate:modelValue"?: ((value: T | undefined) => any) | undefined;
|
|
16
|
+
onSubmit?: (() => any) | undefined;
|
|
17
|
+
}> & (typeof globalThis extends {
|
|
18
|
+
__VLS_PROPS_FALLBACK: infer P;
|
|
19
|
+
} ? P : {});
|
|
20
|
+
expose: (exposed: {}) => void;
|
|
21
|
+
attrs: any;
|
|
22
|
+
slots: {};
|
|
23
|
+
emit: ((evt: "submit") => void) & ((event: "update:modelValue", value: T | undefined) => void);
|
|
24
|
+
}>) => import("vue").VNode & {
|
|
25
|
+
__ctx?: Awaited<typeof __VLS_setup>;
|
|
26
|
+
};
|
|
27
|
+
declare const _default: typeof __VLS_export;
|
|
28
|
+
export default _default;
|
|
29
|
+
type __VLS_PrettifyLocal<T> = (T extends any ? {
|
|
30
|
+
[K in keyof T]: T[K];
|
|
31
|
+
} : {
|
|
32
|
+
[K in keyof T as K]: T[K];
|
|
33
|
+
}) & {};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ControlProps } from "./ControlElement.vue.js";
|
|
2
2
|
export type SelectableOption<T extends object = object> = string | T;
|
|
3
|
-
export interface SelectProps
|
|
3
|
+
export interface SelectProps extends ControlProps {
|
|
4
4
|
options: SelectableOption[];
|
|
5
5
|
multiple?: boolean;
|
|
6
6
|
field?: string;
|
|
@@ -8,7 +8,7 @@ export interface SelectProps<T extends object = object> extends ControlProps {
|
|
|
8
8
|
placeholder?: string;
|
|
9
9
|
}
|
|
10
10
|
declare const __VLS_export: <T extends object>(__VLS_props: NonNullable<Awaited<typeof __VLS_setup>>["props"], __VLS_ctx?: __VLS_PrettifyLocal<Pick<NonNullable<Awaited<typeof __VLS_setup>>, "attrs" | "emit" | "slots">>, __VLS_exposed?: NonNullable<Awaited<typeof __VLS_setup>>["expose"], __VLS_setup?: Promise<{
|
|
11
|
-
props: import("vue").PublicProps & __VLS_PrettifyLocal<(SelectProps
|
|
11
|
+
props: import("vue").PublicProps & __VLS_PrettifyLocal<(SelectProps & {
|
|
12
12
|
modelValue: SelectableOption | SelectableOption[] | null | undefined;
|
|
13
13
|
}) & {
|
|
14
14
|
"onUpdate:modelValue"?: ((value: SelectableOption<object> | SelectableOption<object>[] | null | undefined) => any) | undefined;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<script setup>
|
|
2
|
-
import { computed, ref, toRefs } from "vue";
|
|
2
|
+
import { computed, ref, toRef, toRefs } from "vue";
|
|
3
|
+
import { useControlTokens } from "../composables/useControlSize";
|
|
3
4
|
import { useListKeyboard } from "../composables/useListKeyboard";
|
|
4
5
|
const props = defineProps({
|
|
5
6
|
options: { type: Array, required: true },
|
|
@@ -67,6 +68,7 @@ function getOptionKey(option) {
|
|
|
67
68
|
if (typeof option === "string") return option;
|
|
68
69
|
return String(option[key.value]);
|
|
69
70
|
}
|
|
71
|
+
const { tokens: controlTokens } = useControlTokens(toRef(props, "size"));
|
|
70
72
|
const controlProps = computed(() => {
|
|
71
73
|
const {
|
|
72
74
|
options: _options,
|
|
@@ -142,7 +144,7 @@ const {
|
|
|
142
144
|
</template>
|
|
143
145
|
|
|
144
146
|
<template #content="{ toggle }">
|
|
145
|
-
<div class="selector-content">
|
|
147
|
+
<div class="selector-content" :style="controlTokens">
|
|
146
148
|
<ul
|
|
147
149
|
v-if="options.length"
|
|
148
150
|
ref="listRef"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ControlProps } from "./ControlElement.vue.js";
|
|
2
2
|
export type SelectableOption<T extends object = object> = string | T;
|
|
3
|
-
export interface SelectProps
|
|
3
|
+
export interface SelectProps extends ControlProps {
|
|
4
4
|
options: SelectableOption[];
|
|
5
5
|
multiple?: boolean;
|
|
6
6
|
field?: string;
|
|
@@ -8,7 +8,7 @@ export interface SelectProps<T extends object = object> extends ControlProps {
|
|
|
8
8
|
placeholder?: string;
|
|
9
9
|
}
|
|
10
10
|
declare const __VLS_export: <T extends object>(__VLS_props: NonNullable<Awaited<typeof __VLS_setup>>["props"], __VLS_ctx?: __VLS_PrettifyLocal<Pick<NonNullable<Awaited<typeof __VLS_setup>>, "attrs" | "emit" | "slots">>, __VLS_exposed?: NonNullable<Awaited<typeof __VLS_setup>>["expose"], __VLS_setup?: Promise<{
|
|
11
|
-
props: import("vue").PublicProps & __VLS_PrettifyLocal<(SelectProps
|
|
11
|
+
props: import("vue").PublicProps & __VLS_PrettifyLocal<(SelectProps & {
|
|
12
12
|
modelValue: SelectableOption | SelectableOption[] | null | undefined;
|
|
13
13
|
}) & {
|
|
14
14
|
"onUpdate:modelValue"?: ((value: SelectableOption<object> | SelectableOption<object>[] | null | undefined) => any) | undefined;
|
|
@@ -25,23 +25,23 @@ const carousel = useTemplateRef("carousel");
|
|
|
25
25
|
const measureContainer = useTemplateRef("measureContainer");
|
|
26
26
|
const { width: carouselWidth } = useElementSize(carousel);
|
|
27
27
|
const { width: contentWidth, height: contentHeight } = useElementSize(measureContainer);
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
28
|
+
const measureWidth = computed(
|
|
29
|
+
() => isDynamicHeight.value ? `${rawSizes.value.width}px` : "max-content"
|
|
30
|
+
);
|
|
31
|
+
const measureHeight = computed(
|
|
32
|
+
() => isDynamicWidth.value ? `${rawSizes.value.height}px` : "max-content"
|
|
33
|
+
);
|
|
32
34
|
const calculatedSize = computed(() => {
|
|
33
35
|
const { width, height } = rawSizes.value;
|
|
34
36
|
if (isDynamicHeight.value) {
|
|
35
|
-
const dynamicHeight = contentAspectRatio.value ? width / contentAspectRatio.value : width;
|
|
36
37
|
return {
|
|
37
38
|
width: `${width}px`,
|
|
38
|
-
height: `${
|
|
39
|
+
height: contentHeight.value ? `${contentHeight.value}px` : `${width}px`
|
|
39
40
|
};
|
|
40
41
|
}
|
|
41
42
|
if (isDynamicWidth.value) {
|
|
42
|
-
const dynamicWidth = contentAspectRatio.value ? height * contentAspectRatio.value : height;
|
|
43
43
|
return {
|
|
44
|
-
width: `${
|
|
44
|
+
width: contentWidth.value ? `${contentWidth.value}px` : `${height}px`,
|
|
45
45
|
height: `${height}px`
|
|
46
46
|
};
|
|
47
47
|
}
|
|
@@ -157,5 +157,5 @@ onMounted(() => {
|
|
|
157
157
|
</template>
|
|
158
158
|
|
|
159
159
|
<style scoped>
|
|
160
|
-
.carousel-wrapper{display:block;position:relative}.carousel-measure{height
|
|
160
|
+
.carousel-wrapper{display:block;position:relative}.carousel-measure{height:v-bind(measureHeight);left:-9999px;pointer-events:none;position:fixed;visibility:hidden;width:v-bind(measureWidth)}.carousel-measure :deep(>*){display:block;height:100%;width:100%}.carousel{border:1px solid var(--color-border);border-radius:var(--border-radius-lg);height:v-bind("calculatedSize.height");max-height:v-bind(maxHeight);max-width:100%;overflow:hidden;transition:width .3s ease,height .3s ease;width:v-bind("calculatedSize.width")}.carousel-track{align-items:center;cursor:grab;display:flex;gap:.75rem;height:100%;position:relative;-webkit-user-select:none;-moz-user-select:none;user-select:none;width:100%}.carousel-track:active{cursor:grabbing}.carousel-item{background:var(--color-surface);border-radius:var(--border-radius-sm);color:var(--color-text);height:100%;inset:0;opacity:0;overflow:hidden;padding:.5rem .75rem;pointer-events:none;position:absolute;transition:opacity .5s ease-in-out,transform .5s ease-in-out;white-space:nowrap;width:100%}.carousel-item.previous-image{transform:translateX(-100%)}.carousel-item.next-image{transform:translateX(100%)}.carousel-item.active-image{opacity:1;pointer-events:auto;transform:translateX(0)}.carousel-item :deep(>*){height:100%;-o-object-fit:v-bind(fit);object-fit:v-bind(fit);width:100%}.carousel-empty{color:var(--color-muted)}.switch-button{position:absolute}.switch-button :deep(button){background:transparent!important;border:none!important;color:transparent!important}.switch-button :deep(button:hover){color:transparent!important}.switch-button :deep(.orio-icon){color:#fff!important;fill:#fff!important;filter:drop-shadow(0 0 2px rgba(0,0,0,.8)) drop-shadow(0 0 4px rgba(0,0,0,.6))}@supports (mix-blend-mode:difference) and (not (-webkit-hyphens:none)){.switch-button :deep(.orio-icon){color:#000!important;fill:#000!important;filter:grayscale(1) contrast(9) invert(1) drop-shadow(0 0 1px black) drop-shadow(0 0 2px black);mix-blend-mode:difference}}.switch-button.previous-button{left:0}.switch-button.next-button{right:0}.carousel--minimal{background:none;border:none}.carousel--minimal .carousel-item{background:none}.carousel--minimal .switch-button{opacity:0;transition:opacity .2s ease}.carousel--minimal:hover .switch-button{opacity:1}
|
|
161
161
|
</style>
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type ComputedRef, type Ref } from "vue";
|
|
2
|
+
import type { ControlSize } from "../components/ControlElement.vue.js";
|
|
3
|
+
declare const sizeTokens: Record<ControlSize, Record<string, string>>;
|
|
4
|
+
export declare function provideControlSize(size: Ref<ControlSize> | ComputedRef<ControlSize>): void;
|
|
5
|
+
export declare function useControlTokens(explicit?: Ref<ControlSize | undefined> | ComputedRef<ControlSize | undefined>, fallback?: ControlSize): {
|
|
6
|
+
size: ComputedRef<ControlSize>;
|
|
7
|
+
tokens: ComputedRef<Record<string, string>>;
|
|
8
|
+
};
|
|
9
|
+
export { sizeTokens };
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import {
|
|
2
|
+
computed,
|
|
3
|
+
inject,
|
|
4
|
+
provide,
|
|
5
|
+
ref
|
|
6
|
+
} from "vue";
|
|
7
|
+
const CONTROL_SIZE_KEY = Symbol("control-size");
|
|
8
|
+
const sizeTokens = {
|
|
9
|
+
sm: {
|
|
10
|
+
"--control-font-size": "var(--font-sm)",
|
|
11
|
+
"--control-label-font-size": "var(--font-xs)",
|
|
12
|
+
"--control-py": "0.25rem",
|
|
13
|
+
"--control-px": "0.5rem",
|
|
14
|
+
"--control-gap": "0.25rem",
|
|
15
|
+
"--control-radius": "var(--border-radius-sm)",
|
|
16
|
+
"--control-icon-size": "0.75rem",
|
|
17
|
+
"--control-inner-block-start": "1rem",
|
|
18
|
+
"--control-inner-block-end": "0.1rem",
|
|
19
|
+
"--control-label-block-start": "0.2rem"
|
|
20
|
+
},
|
|
21
|
+
md: {
|
|
22
|
+
"--control-font-size": "var(--font-md)",
|
|
23
|
+
"--control-label-font-size": "var(--font-sm)",
|
|
24
|
+
"--control-py": "0.5rem",
|
|
25
|
+
"--control-px": "0.75rem",
|
|
26
|
+
"--control-gap": "0.5rem",
|
|
27
|
+
"--control-radius": "var(--border-radius-md)",
|
|
28
|
+
"--control-icon-size": "1rem",
|
|
29
|
+
"--control-inner-block-start": "1.25rem",
|
|
30
|
+
"--control-inner-block-end": "0.2rem",
|
|
31
|
+
"--control-label-block-start": "0.25rem"
|
|
32
|
+
},
|
|
33
|
+
lg: {
|
|
34
|
+
"--control-font-size": "var(--font-lg)",
|
|
35
|
+
"--control-label-font-size": "var(--font-md)",
|
|
36
|
+
"--control-py": "0.625rem",
|
|
37
|
+
"--control-px": "1rem",
|
|
38
|
+
"--control-gap": "0.5rem",
|
|
39
|
+
"--control-radius": "var(--border-radius-md)",
|
|
40
|
+
"--control-icon-size": "1.25rem",
|
|
41
|
+
"--control-inner-block-start": "1.1rem",
|
|
42
|
+
"--control-inner-block-end": "0.2rem",
|
|
43
|
+
"--control-label-block-start": "0.25rem"
|
|
44
|
+
},
|
|
45
|
+
xl: {
|
|
46
|
+
"--control-font-size": "var(--font-xl)",
|
|
47
|
+
"--control-label-font-size": "var(--font-lg)",
|
|
48
|
+
"--control-py": "0.75rem",
|
|
49
|
+
"--control-px": "1.25rem",
|
|
50
|
+
"--control-gap": "0.75rem",
|
|
51
|
+
"--control-radius": "var(--border-radius-lg)",
|
|
52
|
+
"--control-icon-size": "1.5rem",
|
|
53
|
+
"--control-inner-block-start": "1.5rem",
|
|
54
|
+
"--control-inner-block-end": "0",
|
|
55
|
+
"--control-label-block-start": "0.25rem"
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
export function provideControlSize(size) {
|
|
59
|
+
provide(CONTROL_SIZE_KEY, size);
|
|
60
|
+
}
|
|
61
|
+
export function useControlTokens(explicit, fallback = "md") {
|
|
62
|
+
const injected = inject(CONTROL_SIZE_KEY, ref(fallback));
|
|
63
|
+
const size = computed(() => explicit?.value ?? injected.value);
|
|
64
|
+
const tokens = computed(() => sizeTokens[size.value]);
|
|
65
|
+
return { size, tokens };
|
|
66
|
+
}
|
|
67
|
+
export { sizeTokens };
|
|
@@ -1,13 +1,53 @@
|
|
|
1
1
|
const DEFAULT_SOUND = "https://cdn.jsdelivr.net/gh/oriondor/orio-ui@main/docs/public/sounds/mechanical-switch.wav";
|
|
2
|
+
let audioContext = null;
|
|
3
|
+
const bufferCache = /* @__PURE__ */ new Map();
|
|
4
|
+
function getAudioContext() {
|
|
5
|
+
if (typeof window === "undefined") return null;
|
|
6
|
+
if (audioContext) return audioContext;
|
|
7
|
+
const ContextClass = window.AudioContext ?? window.webkitAudioContext;
|
|
8
|
+
if (!ContextClass) return null;
|
|
9
|
+
try {
|
|
10
|
+
audioContext = new ContextClass();
|
|
11
|
+
} catch (e) {
|
|
12
|
+
console.error("[useSound] Failed to create AudioContext:", e);
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
return audioContext;
|
|
16
|
+
}
|
|
17
|
+
function fetchBuffer(ctx, src) {
|
|
18
|
+
const cached = bufferCache.get(src);
|
|
19
|
+
if (cached) return cached;
|
|
20
|
+
const promise = fetch(src).then((response) => response.arrayBuffer()).then((arrayBuffer) => ctx.decodeAudioData(arrayBuffer)).catch((e) => {
|
|
21
|
+
console.error(`[useSound] Failed to load sound "${src}":`, e);
|
|
22
|
+
bufferCache.delete(src);
|
|
23
|
+
return null;
|
|
24
|
+
});
|
|
25
|
+
bufferCache.set(src, promise);
|
|
26
|
+
return promise;
|
|
27
|
+
}
|
|
2
28
|
export function useSound(options = {}) {
|
|
3
|
-
const { src = DEFAULT_SOUND, volume = 0.3 } = options;
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
if (
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
29
|
+
const { src = DEFAULT_SOUND, volume = 0.3, prefetch = false } = options;
|
|
30
|
+
const warmUp = () => {
|
|
31
|
+
const ctx = getAudioContext();
|
|
32
|
+
if (ctx) fetchBuffer(ctx, src);
|
|
33
|
+
};
|
|
34
|
+
if (prefetch) warmUp();
|
|
35
|
+
const play = async () => {
|
|
36
|
+
const ctx = getAudioContext();
|
|
37
|
+
if (!ctx) return;
|
|
38
|
+
if (ctx.state === "suspended") {
|
|
39
|
+
await ctx.resume().catch(() => {
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
const buffer = await fetchBuffer(ctx, src);
|
|
43
|
+
if (!buffer) return;
|
|
44
|
+
const source = ctx.createBufferSource();
|
|
45
|
+
source.buffer = buffer;
|
|
46
|
+
const gainNode = ctx.createGain();
|
|
47
|
+
gainNode.gain.value = volume;
|
|
48
|
+
source.connect(gainNode);
|
|
49
|
+
gainNode.connect(ctx.destination);
|
|
50
|
+
source.start(0);
|
|
11
51
|
};
|
|
12
|
-
return { play };
|
|
52
|
+
return { play, prefetch: warmUp };
|
|
13
53
|
}
|
package/dist/runtime/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { default as AnimatedContainer } from "./components/AnimatedContainer.vue.js";
|
|
2
2
|
export { default as Button } from "./components/Button.vue.js";
|
|
3
|
+
export { default as Form, type FormProps } from "./components/Form.vue.js";
|
|
3
4
|
export { default as NavButton } from "./components/NavButton.vue.js";
|
|
4
5
|
export { default as Input } from "./components/Input.vue.js";
|
|
5
6
|
export { default as NumberInput } from "./components/NumberInput/index.vue.js";
|
package/dist/runtime/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { default as AnimatedContainer } from "./components/AnimatedContainer.vue";
|
|
2
2
|
export { default as Button } from "./components/Button.vue";
|
|
3
|
+
export { default as Form } from "./components/Form.vue";
|
|
3
4
|
export { default as NavButton } from "./components/NavButton.vue";
|
|
4
5
|
export { default as Input } from "./components/Input.vue";
|
|
5
6
|
export { default as NumberInput } from "./components/NumberInput/index.vue";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "orio-ui",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.19.0",
|
|
4
4
|
"description": "Modern Nuxt component library with theme support",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/module.mjs",
|
|
@@ -21,13 +21,15 @@
|
|
|
21
21
|
],
|
|
22
22
|
"scripts": {
|
|
23
23
|
"dev": "vitepress dev docs",
|
|
24
|
+
"prebuild": "node scripts/update-counts.mjs",
|
|
24
25
|
"build": "nuxt-module-build build",
|
|
25
26
|
"prepack": "npm run build",
|
|
26
27
|
"test": "vitest",
|
|
27
28
|
"test:unit": "vitest run",
|
|
28
29
|
"test:watch": "vitest watch",
|
|
29
30
|
"docs:dev": "vitepress dev docs --host",
|
|
30
|
-
"
|
|
31
|
+
"predocs:dev": "cp CHANGELOG.md docs/changelog.md",
|
|
32
|
+
"docs:build": "cp CHANGELOG.md docs/changelog.md && vitepress build docs",
|
|
31
33
|
"docs:preview": "vitepress preview docs",
|
|
32
34
|
"lint": "eslint .",
|
|
33
35
|
"lint:fix": "eslint . --fix",
|