@xelqun/forms 0.0.0-stage → 0.1.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/LICENSE +21 -0
- package/README.md +116 -2
- package/package.json +56 -5
- package/resources/css/xelqun.css +162 -0
- package/resources/js/components/XqFieldWrapper.vue +59 -0
- package/resources/js/components/XqForm.vue +128 -0
- package/resources/js/components/XqNode.vue +22 -0
- package/resources/js/components/XqUnknownNode.vue +15 -0
- package/resources/js/components/defaults.ts +44 -0
- package/resources/js/components/fields/XqCheckboxField.vue +48 -0
- package/resources/js/components/fields/XqDatePickerField.vue +96 -0
- package/resources/js/components/fields/XqFileUploadField.vue +247 -0
- package/resources/js/components/fields/XqRadioField.vue +45 -0
- package/resources/js/components/fields/XqRepeaterField.vue +166 -0
- package/resources/js/components/fields/XqSelectField.vue +151 -0
- package/resources/js/components/fields/XqTextInputField.vue +100 -0
- package/resources/js/components/fields/XqTextareaField.vue +28 -0
- package/resources/js/components/fields/XqToggleField.vue +47 -0
- package/resources/js/components/layouts/XqFieldset.vue +16 -0
- package/resources/js/components/layouts/XqGrid.vue +13 -0
- package/resources/js/components/layouts/XqSection.vue +62 -0
- package/resources/js/components/layouts/XqTabsLayout.vue +64 -0
- package/resources/js/composables/context.ts +22 -0
- package/resources/js/composables/layout.ts +55 -0
- package/resources/js/composables/registry.ts +25 -0
- package/resources/js/composables/useField.ts +41 -0
- package/resources/js/composables/useXqForm.ts +16 -0
- package/resources/js/core/conditions.ts +52 -0
- package/resources/js/core/expressions.ts +131 -0
- package/resources/js/core/http.ts +101 -0
- package/resources/js/core/log.ts +93 -0
- package/resources/js/core/patch.ts +65 -0
- package/resources/js/core/paths.ts +122 -0
- package/resources/js/core/queue.ts +190 -0
- package/resources/js/core/rows.ts +26 -0
- package/resources/js/core/store.ts +560 -0
- package/resources/js/core/values.ts +161 -0
- package/resources/js/index.ts +24 -0
- package/resources/js/lib/utils.ts +18 -0
- package/resources/js/plugin.ts +17 -0
- package/resources/js/schema/types.ts +99 -0
- package/resources/js/ui/XqBadge.vue +33 -0
- package/resources/js/ui/XqButton.vue +59 -0
- package/resources/js/ui/XqCalendar.vue +90 -0
- package/resources/js/ui/XqCard.vue +20 -0
- package/resources/js/ui/XqCheckbox.vue +28 -0
- package/resources/js/ui/XqCombobox.vue +184 -0
- package/resources/js/ui/XqInput.vue +16 -0
- package/resources/js/ui/XqLabel.vue +17 -0
- package/resources/js/ui/XqRadioGroup.vue +24 -0
- package/resources/js/ui/XqRadioGroupItem.vue +31 -0
- package/resources/js/ui/XqSelect.vue +79 -0
- package/resources/js/ui/XqSwitch.vue +27 -0
- package/resources/js/ui/XqTabs.vue +19 -0
- package/resources/js/ui/XqTabsContent.vue +17 -0
- package/resources/js/ui/XqTabsList.vue +21 -0
- package/resources/js/ui/XqTabsTrigger.vue +27 -0
- package/resources/js/ui/XqTextarea.vue +16 -0
- package/resources/js/ui/classes.ts +12 -0
- package/resources/js/ui/index.ts +17 -0
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { Check, ChevronDown } from 'lucide-vue-next';
|
|
3
|
+
import {
|
|
4
|
+
SelectContent,
|
|
5
|
+
SelectItem,
|
|
6
|
+
SelectItemIndicator,
|
|
7
|
+
SelectItemText,
|
|
8
|
+
SelectPortal,
|
|
9
|
+
SelectRoot,
|
|
10
|
+
SelectTrigger,
|
|
11
|
+
SelectValue,
|
|
12
|
+
SelectViewport,
|
|
13
|
+
} from 'reka-ui';
|
|
14
|
+
import type { HTMLAttributes } from 'vue';
|
|
15
|
+
import { cn } from '../lib/utils';
|
|
16
|
+
import type { SelectOption } from '../schema/types';
|
|
17
|
+
import { controlClass, optionClass, popoverClass } from './classes';
|
|
18
|
+
|
|
19
|
+
const props = defineProps<{
|
|
20
|
+
id?: string;
|
|
21
|
+
options: SelectOption[];
|
|
22
|
+
placeholder?: string | null;
|
|
23
|
+
disabled?: boolean;
|
|
24
|
+
invalid?: boolean;
|
|
25
|
+
describedBy?: string;
|
|
26
|
+
class?: HTMLAttributes['class'];
|
|
27
|
+
}>();
|
|
28
|
+
const model = defineModel<string | number | null>();
|
|
29
|
+
</script>
|
|
30
|
+
|
|
31
|
+
<template>
|
|
32
|
+
<SelectRoot v-model="model" :disabled="disabled">
|
|
33
|
+
<SelectTrigger
|
|
34
|
+
:id="id"
|
|
35
|
+
data-slot="xq-select"
|
|
36
|
+
:aria-invalid="invalid || undefined"
|
|
37
|
+
:aria-describedby="describedBy"
|
|
38
|
+
:class="
|
|
39
|
+
cn(
|
|
40
|
+
controlClass,
|
|
41
|
+
'h-9 gap-2 px-3 py-1 flex cursor-pointer items-center justify-between text-left data-[placeholder]:text-xq-muted-foreground',
|
|
42
|
+
props.class,
|
|
43
|
+
)
|
|
44
|
+
"
|
|
45
|
+
>
|
|
46
|
+
<SelectValue :placeholder="placeholder ?? 'Select an option'" class="truncate" />
|
|
47
|
+
<ChevronDown class="size-4 shrink-0 opacity-60" />
|
|
48
|
+
</SelectTrigger>
|
|
49
|
+
<SelectPortal>
|
|
50
|
+
<SelectContent
|
|
51
|
+
position="popper"
|
|
52
|
+
:side-offset="4"
|
|
53
|
+
:class="cn(popoverClass, 'max-h-72 min-w-32 w-(--reka-select-trigger-width)')"
|
|
54
|
+
>
|
|
55
|
+
<SelectViewport class="p-1">
|
|
56
|
+
<SelectItem
|
|
57
|
+
v-for="option in options"
|
|
58
|
+
:key="String(option.value)"
|
|
59
|
+
:value="option.value"
|
|
60
|
+
:class="optionClass"
|
|
61
|
+
>
|
|
62
|
+
<SelectItemText>{{ option.label }}</SelectItemText>
|
|
63
|
+
<SelectItemIndicator
|
|
64
|
+
class="right-2 size-3.5 absolute flex items-center justify-center"
|
|
65
|
+
>
|
|
66
|
+
<Check class="size-4" />
|
|
67
|
+
</SelectItemIndicator>
|
|
68
|
+
</SelectItem>
|
|
69
|
+
<div
|
|
70
|
+
v-if="options.length === 0"
|
|
71
|
+
class="px-2 py-6 text-sm text-center text-xq-muted-foreground"
|
|
72
|
+
>
|
|
73
|
+
No options
|
|
74
|
+
</div>
|
|
75
|
+
</SelectViewport>
|
|
76
|
+
</SelectContent>
|
|
77
|
+
</SelectPortal>
|
|
78
|
+
</SelectRoot>
|
|
79
|
+
</template>
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { SwitchRoot, SwitchThumb } from 'reka-ui';
|
|
3
|
+
import type { HTMLAttributes } from 'vue';
|
|
4
|
+
import { cn } from '../lib/utils';
|
|
5
|
+
|
|
6
|
+
const props = defineProps<{ id?: string; disabled?: boolean; class?: HTMLAttributes['class'] }>();
|
|
7
|
+
const model = defineModel<boolean>({ default: false });
|
|
8
|
+
</script>
|
|
9
|
+
|
|
10
|
+
<template>
|
|
11
|
+
<SwitchRoot
|
|
12
|
+
:id="id"
|
|
13
|
+
v-model="model"
|
|
14
|
+
data-slot="xq-switch"
|
|
15
|
+
:disabled="disabled"
|
|
16
|
+
:class="
|
|
17
|
+
cn(
|
|
18
|
+
'peer h-5 w-9 shadow-xs inline-flex shrink-0 cursor-pointer items-center rounded-full border border-transparent transition-colors outline-none focus-visible:ring-[3px] focus-visible:ring-xq-ring/30 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-xq-primary data-[state=unchecked]:bg-xq-input',
|
|
19
|
+
props.class,
|
|
20
|
+
)
|
|
21
|
+
"
|
|
22
|
+
>
|
|
23
|
+
<SwitchThumb
|
|
24
|
+
class="size-4 shadow-sm data-[state=unchecked]:translate-x-0.5 pointer-events-none block rounded-full bg-xq-background ring-0 transition-transform data-[state=checked]:translate-x-[calc(100%-1px)] motion-reduce:transition-none"
|
|
25
|
+
/>
|
|
26
|
+
</SwitchRoot>
|
|
27
|
+
</template>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { TabsRoot } from 'reka-ui';
|
|
3
|
+
import type { HTMLAttributes } from 'vue';
|
|
4
|
+
import { cn } from '../lib/utils';
|
|
5
|
+
|
|
6
|
+
const props = defineProps<{ class?: HTMLAttributes['class']; unmountOnHide?: boolean }>();
|
|
7
|
+
const model = defineModel<string | number>();
|
|
8
|
+
</script>
|
|
9
|
+
|
|
10
|
+
<template>
|
|
11
|
+
<TabsRoot
|
|
12
|
+
v-model="model"
|
|
13
|
+
data-slot="xq-tabs"
|
|
14
|
+
:unmount-on-hide="unmountOnHide ?? true"
|
|
15
|
+
:class="cn('gap-3 flex flex-col', props.class)"
|
|
16
|
+
>
|
|
17
|
+
<slot />
|
|
18
|
+
</TabsRoot>
|
|
19
|
+
</template>
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { TabsContent } from 'reka-ui';
|
|
3
|
+
import type { HTMLAttributes } from 'vue';
|
|
4
|
+
import { cn } from '../lib/utils';
|
|
5
|
+
|
|
6
|
+
const props = defineProps<{ value: string | number; class?: HTMLAttributes['class'] }>();
|
|
7
|
+
</script>
|
|
8
|
+
|
|
9
|
+
<template>
|
|
10
|
+
<TabsContent
|
|
11
|
+
data-slot="xq-tabs-content"
|
|
12
|
+
:value="value"
|
|
13
|
+
:class="cn('outline-none', props.class)"
|
|
14
|
+
>
|
|
15
|
+
<slot />
|
|
16
|
+
</TabsContent>
|
|
17
|
+
</template>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { TabsList } from 'reka-ui';
|
|
3
|
+
import type { HTMLAttributes } from 'vue';
|
|
4
|
+
import { cn } from '../lib/utils';
|
|
5
|
+
|
|
6
|
+
const props = defineProps<{ class?: HTMLAttributes['class'] }>();
|
|
7
|
+
</script>
|
|
8
|
+
|
|
9
|
+
<template>
|
|
10
|
+
<TabsList
|
|
11
|
+
data-slot="xq-tabs-list"
|
|
12
|
+
:class="
|
|
13
|
+
cn(
|
|
14
|
+
'h-9 gap-1 p-1 inline-flex w-fit max-w-full items-center overflow-x-auto rounded-xq-lg bg-xq-muted text-xq-muted-foreground',
|
|
15
|
+
props.class,
|
|
16
|
+
)
|
|
17
|
+
"
|
|
18
|
+
>
|
|
19
|
+
<slot />
|
|
20
|
+
</TabsList>
|
|
21
|
+
</template>
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { TabsTrigger } from 'reka-ui';
|
|
3
|
+
import type { HTMLAttributes } from 'vue';
|
|
4
|
+
import { cn } from '../lib/utils';
|
|
5
|
+
|
|
6
|
+
const props = defineProps<{
|
|
7
|
+
value: string | number;
|
|
8
|
+
disabled?: boolean;
|
|
9
|
+
class?: HTMLAttributes['class'];
|
|
10
|
+
}>();
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<template>
|
|
14
|
+
<TabsTrigger
|
|
15
|
+
data-slot="xq-tabs-trigger"
|
|
16
|
+
:value="value"
|
|
17
|
+
:disabled="disabled"
|
|
18
|
+
:class="
|
|
19
|
+
cn(
|
|
20
|
+
'gap-1.5 px-3 text-sm font-medium data-[state=active]:shadow-sm inline-flex h-full cursor-pointer items-center justify-center rounded-xq-md whitespace-nowrap transition-[color,box-shadow] outline-none hover:text-xq-foreground focus-visible:ring-[3px] focus-visible:ring-xq-ring/30 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-xq-background data-[state=active]:text-xq-foreground',
|
|
21
|
+
props.class,
|
|
22
|
+
)
|
|
23
|
+
"
|
|
24
|
+
>
|
|
25
|
+
<slot />
|
|
26
|
+
</TabsTrigger>
|
|
27
|
+
</template>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { HTMLAttributes } from 'vue';
|
|
3
|
+
import { cn } from '../lib/utils';
|
|
4
|
+
import { controlClass } from './classes';
|
|
5
|
+
|
|
6
|
+
const props = defineProps<{ class?: HTMLAttributes['class'] }>();
|
|
7
|
+
const model = defineModel<string | null>();
|
|
8
|
+
</script>
|
|
9
|
+
|
|
10
|
+
<template>
|
|
11
|
+
<textarea
|
|
12
|
+
v-model="model"
|
|
13
|
+
data-slot="xq-textarea"
|
|
14
|
+
:class="cn(controlClass, 'min-h-16 px-3 py-2', props.class)"
|
|
15
|
+
/>
|
|
16
|
+
</template>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared class strings for form controls. Components use only --xq-* token
|
|
3
|
+
* utilities, so they never collide with an app's own design tokens.
|
|
4
|
+
*/
|
|
5
|
+
export const controlClass =
|
|
6
|
+
'w-full min-w-0 rounded-xq-md border border-xq-input bg-xq-background text-sm text-xq-foreground shadow-xs transition-[color,box-shadow,border-color] outline-none placeholder:text-xq-muted-foreground focus-visible:border-xq-ring focus-visible:ring-[3px] focus-visible:ring-xq-ring/25 disabled:cursor-not-allowed disabled:opacity-60 aria-invalid:border-xq-destructive aria-invalid:ring-xq-destructive/20';
|
|
7
|
+
|
|
8
|
+
export const popoverClass =
|
|
9
|
+
'z-50 overflow-hidden rounded-xq-md border border-xq-border bg-xq-popover text-xq-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0';
|
|
10
|
+
|
|
11
|
+
export const optionClass =
|
|
12
|
+
'relative flex w-full cursor-default items-center gap-2 rounded-xq-sm py-1.5 pr-8 pl-2 text-sm outline-none select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[highlighted]:bg-xq-accent data-[highlighted]:text-xq-accent-foreground';
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export { default as XqBadge } from './XqBadge.vue';
|
|
2
|
+
export { default as XqButton } from './XqButton.vue';
|
|
3
|
+
export { default as XqCalendar } from './XqCalendar.vue';
|
|
4
|
+
export { default as XqCard } from './XqCard.vue';
|
|
5
|
+
export { default as XqCheckbox } from './XqCheckbox.vue';
|
|
6
|
+
export { default as XqCombobox } from './XqCombobox.vue';
|
|
7
|
+
export { default as XqInput } from './XqInput.vue';
|
|
8
|
+
export { default as XqLabel } from './XqLabel.vue';
|
|
9
|
+
export { default as XqRadioGroup } from './XqRadioGroup.vue';
|
|
10
|
+
export { default as XqRadioGroupItem } from './XqRadioGroupItem.vue';
|
|
11
|
+
export { default as XqSelect } from './XqSelect.vue';
|
|
12
|
+
export { default as XqSwitch } from './XqSwitch.vue';
|
|
13
|
+
export { default as XqTabs } from './XqTabs.vue';
|
|
14
|
+
export { default as XqTabsContent } from './XqTabsContent.vue';
|
|
15
|
+
export { default as XqTabsList } from './XqTabsList.vue';
|
|
16
|
+
export { default as XqTabsTrigger } from './XqTabsTrigger.vue';
|
|
17
|
+
export { default as XqTextarea } from './XqTextarea.vue';
|