@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.
Files changed (60) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +116 -2
  3. package/package.json +56 -5
  4. package/resources/css/xelqun.css +162 -0
  5. package/resources/js/components/XqFieldWrapper.vue +59 -0
  6. package/resources/js/components/XqForm.vue +128 -0
  7. package/resources/js/components/XqNode.vue +22 -0
  8. package/resources/js/components/XqUnknownNode.vue +15 -0
  9. package/resources/js/components/defaults.ts +44 -0
  10. package/resources/js/components/fields/XqCheckboxField.vue +48 -0
  11. package/resources/js/components/fields/XqDatePickerField.vue +96 -0
  12. package/resources/js/components/fields/XqFileUploadField.vue +247 -0
  13. package/resources/js/components/fields/XqRadioField.vue +45 -0
  14. package/resources/js/components/fields/XqRepeaterField.vue +166 -0
  15. package/resources/js/components/fields/XqSelectField.vue +151 -0
  16. package/resources/js/components/fields/XqTextInputField.vue +100 -0
  17. package/resources/js/components/fields/XqTextareaField.vue +28 -0
  18. package/resources/js/components/fields/XqToggleField.vue +47 -0
  19. package/resources/js/components/layouts/XqFieldset.vue +16 -0
  20. package/resources/js/components/layouts/XqGrid.vue +13 -0
  21. package/resources/js/components/layouts/XqSection.vue +62 -0
  22. package/resources/js/components/layouts/XqTabsLayout.vue +64 -0
  23. package/resources/js/composables/context.ts +22 -0
  24. package/resources/js/composables/layout.ts +55 -0
  25. package/resources/js/composables/registry.ts +25 -0
  26. package/resources/js/composables/useField.ts +41 -0
  27. package/resources/js/composables/useXqForm.ts +16 -0
  28. package/resources/js/core/conditions.ts +52 -0
  29. package/resources/js/core/expressions.ts +131 -0
  30. package/resources/js/core/http.ts +101 -0
  31. package/resources/js/core/log.ts +93 -0
  32. package/resources/js/core/patch.ts +65 -0
  33. package/resources/js/core/paths.ts +122 -0
  34. package/resources/js/core/queue.ts +190 -0
  35. package/resources/js/core/rows.ts +26 -0
  36. package/resources/js/core/store.ts +560 -0
  37. package/resources/js/core/values.ts +161 -0
  38. package/resources/js/index.ts +24 -0
  39. package/resources/js/lib/utils.ts +18 -0
  40. package/resources/js/plugin.ts +17 -0
  41. package/resources/js/schema/types.ts +99 -0
  42. package/resources/js/ui/XqBadge.vue +33 -0
  43. package/resources/js/ui/XqButton.vue +59 -0
  44. package/resources/js/ui/XqCalendar.vue +90 -0
  45. package/resources/js/ui/XqCard.vue +20 -0
  46. package/resources/js/ui/XqCheckbox.vue +28 -0
  47. package/resources/js/ui/XqCombobox.vue +184 -0
  48. package/resources/js/ui/XqInput.vue +16 -0
  49. package/resources/js/ui/XqLabel.vue +17 -0
  50. package/resources/js/ui/XqRadioGroup.vue +24 -0
  51. package/resources/js/ui/XqRadioGroupItem.vue +31 -0
  52. package/resources/js/ui/XqSelect.vue +79 -0
  53. package/resources/js/ui/XqSwitch.vue +27 -0
  54. package/resources/js/ui/XqTabs.vue +19 -0
  55. package/resources/js/ui/XqTabsContent.vue +17 -0
  56. package/resources/js/ui/XqTabsList.vue +21 -0
  57. package/resources/js/ui/XqTabsTrigger.vue +27 -0
  58. package/resources/js/ui/XqTextarea.vue +16 -0
  59. package/resources/js/ui/classes.ts +12 -0
  60. 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';