@astrake/lumora-ui 0.2.0 → 0.2.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 (179) hide show
  1. package/CHANGELOG.md +141 -137
  2. package/dist/LuCodeBlock.vue_vue_type_script_setup_true_lang-BjwcjuXF.js +1623 -0
  3. package/dist/LuEmbeddedStatusBar.vue_vue_type_script_setup_true_lang-CIksvebU.js +218 -0
  4. package/dist/LuOverlay.vue_vue_type_script_setup_true_lang-DZch4Vrw.js +226 -0
  5. package/dist/components/LuAlert.vue.d.ts +20 -0
  6. package/dist/components/LuAvatar.vue.d.ts +19 -0
  7. package/dist/components/LuBadge.vue.d.ts +16 -0
  8. package/dist/components/LuBreadcrumb.vue.d.ts +16 -0
  9. package/dist/components/LuButton.vue.d.ts +24 -0
  10. package/dist/components/LuCard.vue.d.ts +21 -0
  11. package/dist/components/LuCheckbox.vue.d.ts +27 -0
  12. package/dist/components/LuCodeBlock.vue.d.ts +29 -0
  13. package/dist/components/LuCollapsible.vue.d.ts +25 -0
  14. package/dist/components/LuDivider.vue.d.ts +8 -0
  15. package/dist/components/LuForm.types.d.ts +18 -0
  16. package/dist/components/LuForm.vue.d.ts +55 -0
  17. package/dist/components/LuIcon.vue.d.ts +18 -0
  18. package/dist/components/LuInput.vue.d.ts +26 -0
  19. package/dist/components/LuLink.vue.d.ts +23 -0
  20. package/dist/components/LuMenu.vue.d.ts +26 -0
  21. package/dist/components/LuMenuItem.vue.d.ts +20 -0
  22. package/dist/components/LuModal.vue.d.ts +32 -0
  23. package/dist/components/LuPageHeader.vue.d.ts +10 -0
  24. package/dist/components/LuPagination.vue.d.ts +18 -0
  25. package/dist/components/LuProgressBar.vue.d.ts +7 -0
  26. package/dist/components/LuRadio.vue.d.ts +18 -0
  27. package/dist/components/LuRadioGroup.types.d.ts +8 -0
  28. package/dist/components/LuRadioGroup.vue.d.ts +24 -0
  29. package/dist/components/LuSelect.vue.d.ts +18 -0
  30. package/dist/components/LuSkeleton.vue.d.ts +5 -0
  31. package/dist/components/LuSpinner.vue.d.ts +5 -0
  32. package/dist/components/LuSwitch.vue.d.ts +15 -0
  33. package/dist/components/LuTab.vue.d.ts +16 -0
  34. package/dist/components/LuTabList.vue.d.ts +15 -0
  35. package/dist/components/LuTabPanel.vue.d.ts +16 -0
  36. package/dist/components/LuTable.vue.d.ts +15 -0
  37. package/dist/components/LuTableBody.vue.d.ts +15 -0
  38. package/dist/components/LuTableCell.vue.d.ts +15 -0
  39. package/dist/components/LuTableHead.vue.d.ts +15 -0
  40. package/dist/components/LuTableHeadCell.vue.d.ts +15 -0
  41. package/dist/components/LuTableRow.vue.d.ts +15 -0
  42. package/dist/components/LuTabs.vue.d.ts +20 -0
  43. package/dist/components/LuTag.vue.d.ts +20 -0
  44. package/dist/components/LuText.vue.d.ts +16 -0
  45. package/dist/components/LuTextarea.vue.d.ts +14 -0
  46. package/dist/components/LuThemeSelect.vue.d.ts +2 -0
  47. package/dist/components/LuThemeSwitch.vue.d.ts +2 -0
  48. package/dist/components/LuToggleButton.vue.d.ts +16 -0
  49. package/dist/components/LuToggleGroup.vue.d.ts +20 -0
  50. package/dist/components/LuTooltip.vue.d.ts +19 -0
  51. package/dist/components/__tests__/LuForm.test.d.ts +1 -0
  52. package/dist/components/index.js +47 -0
  53. package/{src/composables/index.ts → dist/composables/index.d.ts} +0 -1
  54. package/dist/composables/index.js +30 -0
  55. package/dist/composables/useRail.d.ts +6 -0
  56. package/dist/composables/useSplit.d.ts +4 -0
  57. package/dist/composables/useTheme.d.ts +7 -0
  58. package/dist/context-0gENwESP.js +62 -0
  59. package/dist/context.d.ts +8 -0
  60. package/{src/index.ts → dist/index.d.ts} +0 -1
  61. package/dist/index.js +94 -0
  62. package/dist/layout/LuDock.vue.d.ts +9 -0
  63. package/dist/layout/LuDockItem.vue.d.ts +16 -0
  64. package/dist/layout/LuFill.vue.d.ts +21 -0
  65. package/dist/layout/LuFixed.vue.d.ts +21 -0
  66. package/dist/layout/LuGrid.vue.d.ts +26 -0
  67. package/dist/layout/LuOverlay.vue.d.ts +15 -0
  68. package/dist/layout/LuScroll.vue.d.ts +15 -0
  69. package/dist/layout/LuSplit.vue.d.ts +16 -0
  70. package/dist/layout/LuSplitPane.vue.d.ts +18 -0
  71. package/dist/layout/LuSplitResizer.vue.d.ts +5 -0
  72. package/dist/layout/LuStack.vue.d.ts +23 -0
  73. package/{src/layout/index.ts → dist/layout/index.d.ts} +1 -14
  74. package/dist/layout/index.js +14 -0
  75. package/dist/plugin.d.ts +6 -0
  76. package/dist/shell/desktop/LuDesktopRailBar.vue.d.ts +17 -0
  77. package/dist/shell/desktop/LuDesktopRailItem.vue.d.ts +18 -0
  78. package/dist/shell/desktop/LuDesktopShell.vue.d.ts +23 -0
  79. package/dist/shell/desktop/LuDesktopSidebar.vue.d.ts +21 -0
  80. package/dist/shell/desktop/LuDesktopStatusBar.vue.d.ts +15 -0
  81. package/dist/shell/desktop/LuDesktopTopBar.vue.d.ts +15 -0
  82. package/dist/shell/embedded/LuEmbeddedShell.vue.d.ts +19 -0
  83. package/dist/shell/embedded/LuEmbeddedStatusBar.vue.d.ts +17 -0
  84. package/dist/shell/embedded/LuEmbeddedTopBar.vue.d.ts +19 -0
  85. package/{src/shell/index.ts → dist/shell/index.d.ts} +0 -1
  86. package/dist/shell/index.js +15 -0
  87. package/dist/shell/mobile/LuMobileHeader.vue.d.ts +19 -0
  88. package/dist/shell/mobile/LuMobileNavBar.vue.d.ts +15 -0
  89. package/dist/shell/mobile/LuMobileShell.vue.d.ts +21 -0
  90. package/dist/skins/default.d.ts +2 -0
  91. package/{src/skins/default.ts → dist/skins/index.js} +119 -129
  92. package/dist/tailwind.d.ts +1 -0
  93. package/dist/tailwind.js +13 -0
  94. package/dist/types.d.ts +15 -0
  95. package/dist/useTheme-Cd4wVaLs.js +21 -0
  96. package/dist/utils.d.ts +21 -0
  97. package/package.json +94 -69
  98. package/src/lumora.css +16 -16
  99. package/src/components/LuAlert.vue +0 -33
  100. package/src/components/LuAvatar.vue +0 -22
  101. package/src/components/LuBadge.vue +0 -15
  102. package/src/components/LuBreadcrumb.vue +0 -63
  103. package/src/components/LuButton.vue +0 -58
  104. package/src/components/LuCard.vue +0 -27
  105. package/src/components/LuCheckbox.vue +0 -94
  106. package/src/components/LuCodeBlock.vue +0 -168
  107. package/src/components/LuCollapsible.vue +0 -34
  108. package/src/components/LuDivider.vue +0 -18
  109. package/src/components/LuForm.types.ts +0 -24
  110. package/src/components/LuForm.vue +0 -121
  111. package/src/components/LuIcon.vue +0 -39
  112. package/src/components/LuInput.vue +0 -82
  113. package/src/components/LuLink.vue +0 -47
  114. package/src/components/LuMenu.vue +0 -86
  115. package/src/components/LuMenuItem.vue +0 -37
  116. package/src/components/LuModal.vue +0 -115
  117. package/src/components/LuPageHeader.vue +0 -24
  118. package/src/components/LuPagination.vue +0 -118
  119. package/src/components/LuProgressBar.vue +0 -21
  120. package/src/components/LuRadio.vue +0 -55
  121. package/src/components/LuRadioGroup.types.ts +0 -10
  122. package/src/components/LuRadioGroup.vue +0 -66
  123. package/src/components/LuSelect.vue +0 -67
  124. package/src/components/LuSkeleton.vue +0 -15
  125. package/src/components/LuSpinner.vue +0 -36
  126. package/src/components/LuSwitch.vue +0 -76
  127. package/src/components/LuTab.vue +0 -26
  128. package/src/components/LuTabList.vue +0 -15
  129. package/src/components/LuTabPanel.vue +0 -19
  130. package/src/components/LuTable.vue +0 -15
  131. package/src/components/LuTableBody.vue +0 -15
  132. package/src/components/LuTableCell.vue +0 -15
  133. package/src/components/LuTableHead.vue +0 -15
  134. package/src/components/LuTableHeadCell.vue +0 -15
  135. package/src/components/LuTableRow.vue +0 -15
  136. package/src/components/LuTabs.vue +0 -30
  137. package/src/components/LuTag.vue +0 -35
  138. package/src/components/LuText.vue +0 -18
  139. package/src/components/LuTextarea.vue +0 -62
  140. package/src/components/LuThemeSelect.vue +0 -26
  141. package/src/components/LuThemeSwitch.vue +0 -22
  142. package/src/components/LuToggleButton.vue +0 -35
  143. package/src/components/LuToggleGroup.vue +0 -27
  144. package/src/components/LuTooltip.vue +0 -36
  145. package/src/components/__tests__/LuForm.test.ts +0 -206
  146. package/src/composables/useRail.ts +0 -24
  147. package/src/composables/useSplit.ts +0 -17
  148. package/src/composables/useTheme.ts +0 -36
  149. package/src/context.ts +0 -39
  150. package/src/layout/LuDock.vue +0 -56
  151. package/src/layout/LuDockItem.vue +0 -20
  152. package/src/layout/LuFill.vue +0 -27
  153. package/src/layout/LuFixed.vue +0 -27
  154. package/src/layout/LuGrid.vue +0 -45
  155. package/src/layout/LuOverlay.vue +0 -17
  156. package/src/layout/LuScroll.vue +0 -19
  157. package/src/layout/LuSplit.vue +0 -23
  158. package/src/layout/LuSplitPane.vue +0 -32
  159. package/src/layout/LuSplitResizer.vue +0 -19
  160. package/src/layout/LuStack.vue +0 -29
  161. package/src/plugin.ts +0 -28
  162. package/src/shell/desktop/LuDesktopRailBar.vue +0 -23
  163. package/src/shell/desktop/LuDesktopRailItem.vue +0 -23
  164. package/src/shell/desktop/LuDesktopShell.vue +0 -25
  165. package/src/shell/desktop/LuDesktopSidebar.vue +0 -36
  166. package/src/shell/desktop/LuDesktopStatusBar.vue +0 -15
  167. package/src/shell/desktop/LuDesktopTopBar.vue +0 -15
  168. package/src/shell/embedded/LuEmbeddedShell.vue +0 -20
  169. package/src/shell/embedded/LuEmbeddedStatusBar.vue +0 -16
  170. package/src/shell/embedded/LuEmbeddedTopBar.vue +0 -17
  171. package/src/shell/mobile/LuMobileHeader.vue +0 -17
  172. package/src/shell/mobile/LuMobileNavBar.vue +0 -15
  173. package/src/shell/mobile/LuMobileShell.vue +0 -21
  174. package/src/tailwind.ts +0 -25
  175. package/src/types.ts +0 -18
  176. package/src/utils.ts +0 -95
  177. package/tsconfig.json +0 -10
  178. /package/{src/components/index.ts → dist/components/index.d.ts} +0 -0
  179. /package/{src/skins/index.ts → dist/skins/index.d.ts} +0 -0
@@ -1,168 +0,0 @@
1
- <template>
2
- <div v-bind="$attrs" :class="resolvedSkin.container">
3
- <div v-if="title || description" :class="resolvedSkin.header">
4
- <h3 v-if="title" :class="resolvedSkin.title">{{ title }}</h3>
5
- <p v-if="description" :class="resolvedSkin.description">{{ description }}</p>
6
- </div>
7
-
8
- <template v-if="variant === 'preview'">
9
- <div :class="resolvedSkin.card">
10
- <template v-if="layout === 'tabbed'">
11
- <LuTabs v-model="activeTab" variant="default">
12
- <LuTabList variant="card-header">
13
- <LuTab value="preview">Preview</LuTab>
14
- <LuTab value="code">Code</LuTab>
15
- </LuTabList>
16
- </LuTabs>
17
-
18
- <div v-if="activeTab === 'preview'" :class="resolvedSkin.previewArea">
19
- <slot name="preview" />
20
- </div>
21
-
22
- <div v-if="activeTab === 'code'" :class="resolvedSkin.codeArea">
23
- <div :class="resolvedSkin.codeHeader">
24
- <div :class="resolvedSkin.badge">{{ lang }}</div>
25
- <button @click="copyCode" :class="resolvedSkin.copyButton" aria-label="Copy code">
26
- <LuIcon :name="copied ? 'check' : 'copy'" class="w-4 h-4" />
27
- </button>
28
- </div>
29
- <div :class="resolvedSkin.codeContent" v-html="html"></div>
30
- </div>
31
- </template>
32
-
33
- <template v-else-if="layout === 'split'">
34
- <div :class="resolvedSkin.splitContainer">
35
- <div :class="resolvedSkin.previewArea">
36
- <slot name="preview" />
37
- </div>
38
- <div :class="resolvedSkin.splitCodeArea">
39
- <div :class="resolvedSkin.codeHeader">
40
- <div :class="resolvedSkin.badge">{{ lang }}</div>
41
- <button @click="copyCode" :class="resolvedSkin.copyButton" aria-label="Copy code">
42
- <LuIcon :name="copied ? 'check' : 'copy'" class="w-4 h-4" />
43
- </button>
44
- </div>
45
- <div :class="resolvedSkin.codeContent" v-html="html"></div>
46
- </div>
47
- </div>
48
- </template>
49
- </div>
50
- </template>
51
-
52
- <template v-else>
53
- <div :class="resolvedSkin.card">
54
- <div :class="resolvedSkin.codeArea">
55
- <div :class="resolvedSkin.codeHeader">
56
- <div :class="resolvedSkin.badge">{{ lang }}</div>
57
- <button @click="copyCode" :class="resolvedSkin.copyButton" aria-label="Copy code">
58
- <LuIcon :name="copied ? 'check' : 'copy'" class="w-4 h-4" />
59
- </button>
60
- </div>
61
- <div :class="resolvedSkin.codeContent" v-html="html"></div>
62
- </div>
63
- </div>
64
- </template>
65
- </div>
66
- </template>
67
-
68
- <script lang="ts">
69
- import { createHighlighter, type Highlighter } from 'shiki';
70
-
71
- let globalHighlighter: Highlighter | null = null;
72
- let highlighterPromise: Promise<Highlighter> | null = null;
73
-
74
- async function getHighlighter() {
75
- if (globalHighlighter) return globalHighlighter;
76
- if (!highlighterPromise) {
77
- highlighterPromise = createHighlighter({
78
- themes: ['one-dark-pro'],
79
- langs: ['bash', 'vue', 'ts', 'html', 'css', 'json']
80
- });
81
- }
82
- globalHighlighter = await highlighterPromise;
83
- return globalHighlighter;
84
- }
85
- </script>
86
-
87
- <script setup lang="ts">
88
- import { ref, watch, onMounted, computed } from "vue";
89
- import { useLumoraConfig } from "../context";
90
- import LuTabs from "./LuTabs.vue";
91
- import LuTabList from "./LuTabList.vue";
92
- import LuTab from "./LuTab.vue";
93
- import LuIcon from "./LuIcon.vue";
94
-
95
- const props = withDefaults(defineProps<{
96
- code: string;
97
- lang?: string;
98
- variant?: "default" | "preview";
99
- layout?: "tabbed" | "split";
100
- title?: string;
101
- description?: string;
102
- }>(), {
103
- lang: "vue",
104
- variant: "default",
105
- layout: "tabbed"
106
- });
107
-
108
- const { resolveSkin } = useLumoraConfig();
109
- const resolvedSkin = computed(() => {
110
- return {
111
- container: resolveSkin("LuCodeBlock", "container"),
112
- header: resolveSkin("LuCodeBlock", "header"),
113
- title: resolveSkin("LuCodeBlock", "title"),
114
- description: resolveSkin("LuCodeBlock", "description"),
115
- card: resolveSkin("LuCodeBlock", "card"),
116
- previewArea: resolveSkin("LuCodeBlock", "previewArea"),
117
- codeArea: resolveSkin("LuCodeBlock", "codeArea"),
118
- splitCodeArea: resolveSkin("LuCodeBlock", "splitCodeArea"),
119
- splitContainer: resolveSkin("LuCodeBlock", "splitContainer"),
120
- codeHeader: resolveSkin("LuCodeBlock", "codeHeader"),
121
- badge: resolveSkin("LuCodeBlock", "badge"),
122
- copyButton: resolveSkin("LuCodeBlock", "copyButton"),
123
- codeContent: resolveSkin("LuCodeBlock", "codeContent"),
124
- };
125
- });
126
-
127
- const html = ref('');
128
- const activeTab = ref<'preview' | 'code'>('preview');
129
- const copied = ref(false);
130
-
131
- const highlight = async () => {
132
- if (!props.code) {
133
- html.value = '';
134
- return;
135
- }
136
-
137
- try {
138
- const highlighter = await getHighlighter();
139
-
140
- html.value = highlighter.codeToHtml(props.code, {
141
- lang: props.lang || 'vue',
142
- theme: 'one-dark-pro'
143
- });
144
- } catch (e) {
145
- console.error('Failed to highlight code', e);
146
- // fallback to plain pre
147
- html.value = `<pre><code>${props.code.replace(/</g, '&lt;').replace(/>/g, '&gt;')}</code></pre>`;
148
- }
149
- };
150
-
151
- onMounted(() => {
152
- highlight();
153
- });
154
-
155
- watch(() => [props.code, props.lang], () => {
156
- highlight();
157
- });
158
-
159
- const copyCode = async () => {
160
- try {
161
- await navigator.clipboard.writeText(props.code);
162
- copied.value = true;
163
- setTimeout(() => copied.value = false, 2000);
164
- } catch (err) {
165
- console.error('Failed to copy', err);
166
- }
167
- };
168
- </script>
@@ -1,34 +0,0 @@
1
- <template>
2
- <div v-bind="$attrs" :class="wrapperSkin">
3
- <button :class="triggerSkin" @click="toggle">
4
- <slot name="trigger" :isOpen="isOpen" />
5
- </button>
6
- <div v-show="isOpen" :class="contentSkin">
7
- <slot name="content" />
8
- </div>
9
- </div>
10
- </template>
11
-
12
- <script setup lang="ts">
13
- import { computed, ref, watch } from "vue";
14
- import { useLumoraConfig } from "../context";
15
-
16
- const props = defineProps<{ variant?: string; modelValue?: boolean; defaultOpen?: boolean }>();
17
- const emit = defineEmits<{ (e: "update:modelValue", val: boolean): void }>();
18
-
19
- const isOpen = ref(props.modelValue ?? props.defaultOpen ?? false);
20
-
21
- watch(() => props.modelValue, (val) => {
22
- if (val !== undefined) isOpen.value = val;
23
- });
24
-
25
- const toggle = () => {
26
- isOpen.value = !isOpen.value;
27
- emit("update:modelValue", isOpen.value);
28
- };
29
-
30
- const { resolveSkin } = useLumoraConfig();
31
- const wrapperSkin = computed(() => resolveSkin("LuCollapsible", props.variant));
32
- const triggerSkin = computed(() => resolveSkin("LuCollapsibleTrigger", props.variant));
33
- const contentSkin = computed(() => resolveSkin("LuCollapsibleContent", props.variant));
34
- </script>
@@ -1,18 +0,0 @@
1
- <template>
2
- <div v-bind="$attrs" :class="resolvedSkin"></div>
3
- </template>
4
-
5
- <script setup lang="ts">
6
- import { computed } from "vue";
7
- import { useLumoraConfig } from "../context";
8
-
9
- const props = withDefaults(defineProps<{
10
- variant?: string;
11
- orientation?: "horizontal" | "vertical";
12
- }>(), {
13
- orientation: "horizontal"
14
- });
15
-
16
- const { resolveSkin } = useLumoraConfig();
17
- const resolvedSkin = computed(() => resolveSkin("LuDivider", props.variant || props.orientation));
18
- </script>
@@ -1,24 +0,0 @@
1
- import type { InjectionKey, Ref } from "vue";
2
-
3
- export type LuFormValidator = (value: unknown) => string | null | Promise<string | null>;
4
-
5
- export type LuFormRules = Record<string, LuFormValidator | LuFormValidator[]>;
6
-
7
- export type LuFormErrors = Record<string, string>;
8
-
9
- export interface LuFormFieldRegistration {
10
- name: string;
11
- getValue: () => unknown;
12
- setValue: (v: unknown) => void;
13
- setError: (msg: string | null) => void;
14
- }
15
-
16
- export const LuFormContextKey = Symbol("LuFormContext") as InjectionKey<LuFormContext>;
17
-
18
- export interface LuFormContext {
19
- register(field: LuFormFieldRegistration): void;
20
- unregister(name: string): void;
21
- getError(name: string): string | null;
22
- validateOn: Readonly<Ref<"submit" | "blur" | "both">>;
23
- disabled: Readonly<Ref<boolean>>;
24
- }
@@ -1,121 +0,0 @@
1
- <template>
2
- <form @submit.prevent="handleSubmit" @reset.prevent="handleReset">
3
- <slot />
4
- <slot
5
- name="errors"
6
- :errors="errors"
7
- :has-errors="hasErrors"
8
- />
9
- <slot
10
- name="actions"
11
- :submit="handleSubmit"
12
- :reset="handleReset"
13
- :pending="pending"
14
- />
15
- </form>
16
- </template>
17
-
18
- <script setup lang="ts">
19
- import { ref, computed, provide, readonly } from "vue";
20
- import type { LuFormRules, LuFormErrors, LuFormFieldRegistration, LuFormContext } from "./LuForm.types";
21
- import { LuFormContextKey } from "./LuForm.types";
22
-
23
- const props = withDefaults(defineProps<{
24
- rules?: LuFormRules;
25
- validateOn?: "submit" | "blur" | "both";
26
- resetOnSubmit?: boolean;
27
- disabled?: boolean;
28
- }>(), {
29
- rules: () => ({}),
30
- validateOn: "submit",
31
- resetOnSubmit: false,
32
- disabled: false,
33
- });
34
-
35
- const emit = defineEmits<{
36
- (e: "submit", values: Record<string, unknown>): void;
37
- (e: "reset"): void;
38
- (e: "error", errors: LuFormErrors): void;
39
- }>();
40
-
41
- const fields = new Map<string, LuFormFieldRegistration>();
42
- const errors = ref<LuFormErrors>({});
43
- const pending = ref(false);
44
- const hasErrors = computed(() => Object.keys(errors.value).length > 0);
45
-
46
- async function validate(): Promise<boolean> {
47
- const nextErrors: LuFormErrors = {};
48
-
49
- for (const [name, field] of fields) {
50
- const rule = props.rules?.[name];
51
- if (!rule) continue;
52
-
53
- const validators = Array.isArray(rule) ? rule : [rule];
54
- const value = field.getValue();
55
-
56
- for (const validator of validators) {
57
- const result = await validator(value);
58
- if (result) {
59
- nextErrors[name] = result;
60
- field.setError(result);
61
- break;
62
- } else {
63
- field.setError(null);
64
- }
65
- }
66
- }
67
-
68
- errors.value = nextErrors;
69
- return Object.keys(nextErrors).length === 0;
70
- }
71
-
72
- async function handleSubmit() {
73
- pending.value = true;
74
- try {
75
- const valid = await validate();
76
- if (!valid) {
77
- emit("error", errors.value);
78
- return;
79
- }
80
- const values: Record<string, unknown> = {};
81
- for (const [name, field] of fields) {
82
- values[name] = field.getValue();
83
- }
84
- emit("submit", values);
85
- if (props.resetOnSubmit) handleReset();
86
- } finally {
87
- pending.value = false;
88
- }
89
- }
90
-
91
- function handleReset() {
92
- errors.value = {};
93
- for (const field of fields.values()) {
94
- field.setValue(undefined);
95
- field.setError(null);
96
- }
97
- emit("reset");
98
- }
99
-
100
- const context: LuFormContext = {
101
- register(field) { fields.set(field.name, field); },
102
- unregister(name) { fields.delete(name); },
103
- getError(name) { return errors.value[name] ?? null; },
104
- validateOn: computed(() => props.validateOn),
105
- disabled: computed(() => props.disabled),
106
- };
107
-
108
- provide(LuFormContextKey, context);
109
-
110
- defineExpose({
111
- submit: handleSubmit,
112
- reset: handleReset,
113
- errors: readonly(errors),
114
- pending: readonly(pending),
115
- values: computed(() => {
116
- const v: Record<string, unknown> = {};
117
- for (const [name, field] of fields) v[name] = field.getValue();
118
- return v;
119
- }),
120
- });
121
- </script>
@@ -1,39 +0,0 @@
1
- <template>
2
- <span v-bind="$attrs" :class="resolvedSkin" aria-hidden="true" :style="sizeStyle">
3
- <slot>
4
- <component
5
- v-if="resolvedIcon"
6
- :is="resolvedIcon"
7
- :size="size"
8
- :stroke-width="strokeWidth"
9
- />
10
- </slot>
11
- </span>
12
- </template>
13
-
14
- <script setup lang="ts">
15
- import { computed, useSlots } from "vue";
16
- import { useLumoraConfig } from "../context";
17
-
18
- const props = defineProps<{
19
- variant?: string;
20
- name?: string;
21
- size?: number;
22
- strokeWidth?: number;
23
- }>();
24
-
25
- const slots = useSlots();
26
- const { resolveSkin, resolveIcon } = useLumoraConfig();
27
-
28
- const resolvedSkin = computed(() => resolveSkin("LuIcon", props.variant));
29
-
30
- const resolvedIcon = computed(() => {
31
- if (slots.default) return null;
32
- if (!props.name) return null;
33
- return resolveIcon(props.name, props.size);
34
- });
35
-
36
- const sizeStyle = computed(() =>
37
- props.size ? { width: `${props.size}px`, height: `${props.size}px` } : undefined
38
- );
39
- </script>
@@ -1,82 +0,0 @@
1
- <template>
2
- <div class="relative w-full" v-if="$slots.prepend || $slots.append">
3
- <div v-if="$slots.prepend" :class="prependSkin">
4
- <slot name="prepend" />
5
- </div>
6
- <input
7
- v-bind="$attrs"
8
- :class="[resolvedSkin, $slots.prepend && 'pl-9', $slots.append && 'pr-9']"
9
- :value="modelValue"
10
- :name="name"
11
- :disabled="formContext?.disabled.value"
12
- @input="onInput"
13
- @blur="onBlur"
14
- />
15
- <div v-if="$slots.append" :class="appendSkin">
16
- <slot name="append" />
17
- </div>
18
- </div>
19
- <input
20
- v-else
21
- v-bind="$attrs"
22
- :class="resolvedSkin"
23
- :value="modelValue"
24
- :name="name"
25
- :disabled="formContext?.disabled.value"
26
- @input="onInput"
27
- @blur="onBlur"
28
- />
29
- </template>
30
-
31
- <script setup lang="ts">
32
- import { computed, inject, onMounted, onUnmounted, ref } from "vue";
33
- import { useLumoraConfig } from "../context";
34
- import { LuFormContextKey } from "./LuForm.types";
35
-
36
- const props = defineProps<{
37
- modelValue?: string | number;
38
- variant?: string;
39
- name?: string;
40
- error?: string | null;
41
- }>();
42
-
43
- const emit = defineEmits<{
44
- (e: "update:modelValue", value: string): void;
45
- (e: "blur"): void;
46
- }>();
47
-
48
- const { resolveSkin } = useLumoraConfig();
49
- const resolvedSkin = computed(() => resolveSkin("LuInput", props.variant));
50
- const prependSkin = computed(() => resolveSkin("LuInputPrepend", props.variant));
51
- const appendSkin = computed(() => resolveSkin("LuInputAppend", props.variant));
52
-
53
- const formContext = inject(LuFormContextKey, null);
54
- const internalValue = ref<string | number | undefined>(props.modelValue);
55
-
56
- const onInput = (event: Event) => {
57
- const value = (event.target as HTMLInputElement).value;
58
- internalValue.value = value;
59
- emit("update:modelValue", value);
60
- };
61
-
62
- const onBlur = () => {
63
- if (props.name && formContext && (formContext.validateOn.value === "blur" || formContext.validateOn.value === "both")) {
64
- // trigger single-field validation — handled by parent LuForm
65
- }
66
- emit("blur");
67
- };
68
-
69
- onMounted(() => {
70
- if (!props.name || !formContext) return;
71
- formContext.register({
72
- name: props.name,
73
- getValue: () => internalValue.value,
74
- setValue: (v) => { internalValue.value = v as string; },
75
- setError: (_msg) => { /* error display handled via formContext.getError in template if desired */ },
76
- });
77
- });
78
-
79
- onUnmounted(() => {
80
- if (props.name && formContext) formContext.unregister(props.name);
81
- });
82
- </script>
@@ -1,47 +0,0 @@
1
- <template>
2
- <component
3
- :is="componentType"
4
- v-bind="bindingProps"
5
- :class="resolvedSkin"
6
- @click="emit('click', $event)"
7
- >
8
- <slot />
9
- </component>
10
- </template>
11
-
12
- <script setup lang="ts">
13
- import { computed, resolveComponent } from "vue";
14
- import { useLumoraConfig } from "../context";
15
-
16
- const props = defineProps<{
17
- variant?: string;
18
- as?: string;
19
- to?: any;
20
- href?: string;
21
- target?: string;
22
- }>();
23
-
24
- const emit = defineEmits<{
25
- (e: "click", event: MouseEvent): void;
26
- }>();
27
-
28
- const componentType = computed(() => {
29
- if (props.as) return props.as;
30
- if (props.to) {
31
- // Attempt to resolve RouterLink if available, otherwise fallback to 'a'
32
- const routerLink = resolveComponent("RouterLink");
33
- return typeof routerLink === 'string' ? 'a' : routerLink;
34
- }
35
- return "a";
36
- });
37
-
38
- const bindingProps = computed(() => {
39
- const p: any = { ...props };
40
- delete p.variant;
41
- delete p.as;
42
- return p;
43
- });
44
-
45
- const { resolveSkin } = useLumoraConfig();
46
- const resolvedSkin = computed(() => resolveSkin("LuLink", props.variant));
47
- </script>
@@ -1,86 +0,0 @@
1
- <template>
2
- <div :class="resolvedSkin" ref="dropdownRef">
3
- <div @click="toggle" :class="resolvedTriggerSkin" aria-haspopup="true" :aria-expanded="isOpen">
4
- <slot name="trigger">
5
- <LuButton variant="default">Options <LuIcon name="chevron-down" class="ml-2 h-4 w-4" /></LuButton>
6
- </slot>
7
- </div>
8
- <transition
9
- enter-active-class="transition ease-out duration-100"
10
- enter-from-class="transform opacity-0 scale-95"
11
- enter-to-class="transform opacity-100 scale-100"
12
- leave-active-class="transition ease-in duration-75"
13
- leave-from-class="transform opacity-100 scale-100"
14
- leave-to-class="transform opacity-0 scale-95"
15
- >
16
- <div v-if="isOpen" :class="[resolvedContentSkin, alignClass]">
17
- <div :class="resolvedGroupSkin" role="menu" aria-orientation="vertical" aria-labelledby="options-menu">
18
- <slot />
19
- </div>
20
- </div>
21
- </transition>
22
- </div>
23
- </template>
24
-
25
- <script setup lang="ts">
26
- import { computed, ref, onMounted, onBeforeUnmount } from "vue";
27
- import { useLumoraConfig } from "../context";
28
- import LuButton from "./LuButton.vue";
29
- import LuIcon from "./LuIcon.vue";
30
-
31
- const props = withDefaults(defineProps<{
32
- variant?: string;
33
- align?: 'left' | 'right';
34
- }>(), {
35
- align: 'left'
36
- });
37
-
38
- const emit = defineEmits<{
39
- (e: "open"): void;
40
- (e: "close"): void;
41
- }>();
42
-
43
- const isOpen = ref(false);
44
- const dropdownRef = ref<HTMLElement | null>(null);
45
-
46
- const { resolveSkin } = useLumoraConfig();
47
-
48
- const resolvedSkin = computed(() => resolveSkin("LuMenu", props.variant));
49
- const resolvedTriggerSkin = computed(() => resolveSkin("LuMenuTrigger", props.variant));
50
- const resolvedContentSkin = computed(() => resolveSkin("LuMenuContent", props.variant));
51
- const resolvedGroupSkin = computed(() => resolveSkin("LuMenuGroup", props.variant));
52
-
53
- const alignClass = computed(() => {
54
- return props.align === 'right' ? 'right-0 origin-top-right' : 'left-0 origin-top-left';
55
- });
56
-
57
- const toggle = () => {
58
- isOpen.value = !isOpen.value;
59
- if (isOpen.value) {
60
- emit("open");
61
- } else {
62
- emit("close");
63
- }
64
- };
65
-
66
- const close = () => {
67
- if (isOpen.value) {
68
- isOpen.value = false;
69
- emit("close");
70
- }
71
- };
72
-
73
- const handleClickOutside = (event: MouseEvent) => {
74
- if (dropdownRef.value && !dropdownRef.value.contains(event.target as Node)) {
75
- close();
76
- }
77
- };
78
-
79
- onMounted(() => {
80
- document.addEventListener('click', handleClickOutside);
81
- });
82
-
83
- onBeforeUnmount(() => {
84
- document.removeEventListener('click', handleClickOutside);
85
- });
86
- </script>
@@ -1,37 +0,0 @@
1
- <template>
2
- <button
3
- type="button"
4
- :class="resolvedSkin"
5
- role="menuitem"
6
- :disabled="disabled"
7
- :data-disabled="disabled ? '' : undefined"
8
- @click="onClick"
9
- >
10
- <slot />
11
- </button>
12
- </template>
13
-
14
- <script setup lang="ts">
15
- import { computed } from "vue";
16
- import { useLumoraConfig } from "../context";
17
-
18
- const props = defineProps<{
19
- variant?: string;
20
- disabled?: boolean;
21
- }>();
22
-
23
- const emit = defineEmits<{
24
- (e: "click", event: MouseEvent): void;
25
- }>();
26
-
27
- const { resolveSkin } = useLumoraConfig();
28
- const resolvedSkin = computed(() => resolveSkin("LuMenuItem", props.variant));
29
-
30
- const onClick = (event: MouseEvent) => {
31
- if (props.disabled) {
32
- event.preventDefault();
33
- return;
34
- }
35
- emit("click", event);
36
- };
37
- </script>