@soave/ui 0.3.1 → 0.4.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.
Files changed (214) hide show
  1. package/dist/adapters/css-variables.d.ts +2 -1
  2. package/dist/adapters/css-variables.d.ts.map +1 -0
  3. package/dist/adapters/headless.d.ts +2 -1
  4. package/dist/adapters/headless.d.ts.map +1 -0
  5. package/dist/adapters/index.d.ts +6 -5
  6. package/dist/adapters/index.d.ts.map +1 -0
  7. package/dist/adapters/tailwind.d.ts +2 -1
  8. package/dist/adapters/tailwind.d.ts.map +1 -0
  9. package/dist/adapters/types.d.ts +2 -1
  10. package/dist/adapters/types.d.ts.map +1 -0
  11. package/dist/adapters.d.ts +2 -0
  12. package/dist/{adapters/css-variables.mjs → adapters.mjs} +33 -2
  13. package/dist/adapters.mjs.map +1 -0
  14. package/dist/components/Button.vue.d.ts +35 -0
  15. package/dist/components/Button.vue.d.ts.map +1 -0
  16. package/dist/components/Card.vue.d.ts +29 -0
  17. package/dist/components/Card.vue.d.ts.map +1 -0
  18. package/dist/components/Checkbox.vue.d.ts +39 -0
  19. package/dist/components/Checkbox.vue.d.ts.map +1 -0
  20. package/dist/components/Dialog.vue.d.ts +46 -0
  21. package/dist/components/Dialog.vue.d.ts.map +1 -0
  22. package/dist/components/Input.vue.d.ts +24 -0
  23. package/dist/components/Input.vue.d.ts.map +1 -0
  24. package/dist/components/RadioGroup.vue.d.ts +32 -0
  25. package/dist/components/RadioGroup.vue.d.ts.map +1 -0
  26. package/dist/components/RadioItem.vue.d.ts +40 -0
  27. package/dist/components/RadioItem.vue.d.ts.map +1 -0
  28. package/dist/components/Select.vue.d.ts +50 -0
  29. package/dist/components/Select.vue.d.ts.map +1 -0
  30. package/dist/components/SelectContent.vue.d.ts +25 -0
  31. package/dist/components/SelectContent.vue.d.ts.map +1 -0
  32. package/dist/components/SelectItem.vue.d.ts +36 -0
  33. package/dist/components/SelectItem.vue.d.ts.map +1 -0
  34. package/dist/components/SelectTrigger.vue.d.ts +36 -0
  35. package/dist/components/SelectTrigger.vue.d.ts.map +1 -0
  36. package/dist/components/Switch.vue.d.ts +46 -0
  37. package/dist/components/Switch.vue.d.ts.map +1 -0
  38. package/dist/components/Textarea.vue.d.ts +41 -0
  39. package/dist/components/Textarea.vue.d.ts.map +1 -0
  40. package/dist/components/index.d.ts +14 -13
  41. package/dist/components/index.d.ts.map +1 -0
  42. package/dist/composables/index.d.ts +18 -17
  43. package/dist/composables/index.d.ts.map +1 -0
  44. package/dist/composables/useButton.d.ts +3 -2
  45. package/dist/composables/useButton.d.ts.map +1 -0
  46. package/dist/composables/useCard.d.ts +3 -2
  47. package/dist/composables/useCard.d.ts.map +1 -0
  48. package/dist/composables/useCheckbox.d.ts +3 -2
  49. package/dist/composables/useCheckbox.d.ts.map +1 -0
  50. package/dist/composables/useDialog.d.ts +2 -1
  51. package/dist/composables/useDialog.d.ts.map +1 -0
  52. package/dist/composables/useDropdown.d.ts +3 -2
  53. package/dist/composables/useDropdown.d.ts.map +1 -0
  54. package/dist/composables/useFileInput.d.ts +3 -2
  55. package/dist/composables/useFileInput.d.ts.map +1 -0
  56. package/dist/composables/useForm.d.ts +3 -2
  57. package/dist/composables/useForm.d.ts.map +1 -0
  58. package/dist/composables/useInput.d.ts +3 -2
  59. package/dist/composables/useInput.d.ts.map +1 -0
  60. package/dist/composables/usePopover.d.ts +3 -2
  61. package/dist/composables/usePopover.d.ts.map +1 -0
  62. package/dist/composables/useRadio.d.ts +3 -2
  63. package/dist/composables/useRadio.d.ts.map +1 -0
  64. package/dist/composables/useSelect.d.ts +3 -2
  65. package/dist/composables/useSelect.d.ts.map +1 -0
  66. package/dist/composables/useSwitch.d.ts +3 -2
  67. package/dist/composables/useSwitch.d.ts.map +1 -0
  68. package/dist/composables/useTextarea.d.ts +3 -2
  69. package/dist/composables/useTextarea.d.ts.map +1 -0
  70. package/dist/composables/useTheme.d.ts +3 -2
  71. package/dist/composables/useTheme.d.ts.map +1 -0
  72. package/dist/composables/useToast.d.ts +2 -1
  73. package/dist/composables/useToast.d.ts.map +1 -0
  74. package/dist/composables/useTooltip.d.ts +3 -2
  75. package/dist/composables/useTooltip.d.ts.map +1 -0
  76. package/dist/composables/useUIConfig.d.ts +5 -4
  77. package/dist/composables/useUIConfig.d.ts.map +1 -0
  78. package/dist/composables.d.ts +2 -0
  79. package/dist/composables.mjs +30 -0
  80. package/dist/composables.mjs.map +1 -0
  81. package/dist/constants/errors.d.ts +1 -0
  82. package/dist/constants/errors.d.ts.map +1 -0
  83. package/dist/constants/index.d.ts +3 -2
  84. package/dist/constants/index.d.ts.map +1 -0
  85. package/dist/constants/logs.d.ts +1 -0
  86. package/dist/constants/logs.d.ts.map +1 -0
  87. package/dist/index.d.ts +7 -6
  88. package/dist/index.d.ts.map +1 -0
  89. package/dist/index.mjs +760 -6
  90. package/dist/index.mjs.map +1 -0
  91. package/dist/{adapters/tailwind.mjs → tailwind-B-R7fPT1.js} +16 -7
  92. package/dist/tailwind-B-R7fPT1.js.map +1 -0
  93. package/dist/types/alert.d.ts +2 -1
  94. package/dist/types/alert.d.ts.map +1 -0
  95. package/dist/types/button.d.ts +2 -1
  96. package/dist/types/button.d.ts.map +1 -0
  97. package/dist/types/card.d.ts +2 -1
  98. package/dist/types/card.d.ts.map +1 -0
  99. package/dist/types/checkbox.d.ts +2 -1
  100. package/dist/types/checkbox.d.ts.map +1 -0
  101. package/dist/types/composables.d.ts +10 -1
  102. package/dist/types/composables.d.ts.map +1 -0
  103. package/dist/types/config.d.ts +6 -5
  104. package/dist/types/config.d.ts.map +1 -0
  105. package/dist/types/dialog.d.ts +2 -1
  106. package/dist/types/dialog.d.ts.map +1 -0
  107. package/dist/types/dropdown.d.ts +3 -2
  108. package/dist/types/dropdown.d.ts.map +1 -0
  109. package/dist/types/file-input.d.ts +2 -1
  110. package/dist/types/file-input.d.ts.map +1 -0
  111. package/dist/types/form.d.ts +2 -1
  112. package/dist/types/form.d.ts.map +1 -0
  113. package/dist/types/index.d.ts +22 -21
  114. package/dist/types/index.d.ts.map +1 -0
  115. package/dist/types/input.d.ts +2 -1
  116. package/dist/types/input.d.ts.map +1 -0
  117. package/dist/types/popover.d.ts +3 -2
  118. package/dist/types/popover.d.ts.map +1 -0
  119. package/dist/types/radio.d.ts +2 -1
  120. package/dist/types/radio.d.ts.map +1 -0
  121. package/dist/types/select.d.ts +2 -1
  122. package/dist/types/select.d.ts.map +1 -0
  123. package/dist/types/sheet.d.ts +2 -1
  124. package/dist/types/sheet.d.ts.map +1 -0
  125. package/dist/types/switch.d.ts +2 -1
  126. package/dist/types/switch.d.ts.map +1 -0
  127. package/dist/types/textarea.d.ts +2 -1
  128. package/dist/types/textarea.d.ts.map +1 -0
  129. package/dist/types/theme.d.ts +1 -0
  130. package/dist/types/theme.d.ts.map +1 -0
  131. package/dist/types/toast.d.ts +1 -0
  132. package/dist/types/toast.d.ts.map +1 -0
  133. package/dist/types/tooltip.d.ts +3 -2
  134. package/dist/types/tooltip.d.ts.map +1 -0
  135. package/dist/types/utils.d.ts +1 -0
  136. package/dist/types/utils.d.ts.map +1 -0
  137. package/dist/useTheme-C2uPqAtQ.js +1175 -0
  138. package/dist/useTheme-C2uPqAtQ.js.map +1 -0
  139. package/dist/utils/cn.d.ts +2 -1
  140. package/dist/utils/cn.d.ts.map +1 -0
  141. package/dist/utils/deepMerge.d.ts +2 -1
  142. package/dist/utils/deepMerge.d.ts.map +1 -0
  143. package/dist/utils/index.d.ts +3 -2
  144. package/dist/utils/index.d.ts.map +1 -0
  145. package/package.json +6 -4
  146. package/dist/adapters/headless.mjs +0 -7
  147. package/dist/adapters/index.mjs +0 -11
  148. package/dist/adapters/types.mjs +0 -10
  149. package/dist/build.config.d.ts +0 -2
  150. package/dist/build.config.mjs +0 -14
  151. package/dist/components/Button.vue +0 -36
  152. package/dist/components/Card.vue +0 -23
  153. package/dist/components/Checkbox.vue +0 -44
  154. package/dist/components/Dialog.vue +0 -99
  155. package/dist/components/Input.vue +0 -48
  156. package/dist/components/RadioGroup.vue +0 -35
  157. package/dist/components/RadioItem.vue +0 -55
  158. package/dist/components/Select.vue +0 -95
  159. package/dist/components/SelectContent.vue +0 -40
  160. package/dist/components/SelectItem.vue +0 -44
  161. package/dist/components/SelectTrigger.vue +0 -61
  162. package/dist/components/Switch.vue +0 -43
  163. package/dist/components/Textarea.vue +0 -55
  164. package/dist/components/index.mjs +0 -13
  165. package/dist/composables/index.mjs +0 -17
  166. package/dist/composables/useButton.mjs +0 -22
  167. package/dist/composables/useCard.mjs +0 -11
  168. package/dist/composables/useCheckbox.mjs +0 -18
  169. package/dist/composables/useDialog.mjs +0 -19
  170. package/dist/composables/useDropdown.mjs +0 -170
  171. package/dist/composables/useFileInput.mjs +0 -137
  172. package/dist/composables/useForm.mjs +0 -159
  173. package/dist/composables/useInput.mjs +0 -31
  174. package/dist/composables/usePopover.mjs +0 -113
  175. package/dist/composables/useRadio.mjs +0 -23
  176. package/dist/composables/useSelect.mjs +0 -42
  177. package/dist/composables/useSwitch.mjs +0 -17
  178. package/dist/composables/useTextarea.mjs +0 -29
  179. package/dist/composables/useTheme.mjs +0 -89
  180. package/dist/composables/useToast.mjs +0 -64
  181. package/dist/composables/useTooltip.mjs +0 -125
  182. package/dist/composables/useUIConfig.mjs +0 -53
  183. package/dist/constants/errors.mjs +0 -18
  184. package/dist/constants/index.mjs +0 -2
  185. package/dist/constants/logs.mjs +0 -17
  186. package/dist/env.d.ts +0 -11
  187. package/dist/styles/css-variables.css +0 -1
  188. package/dist/styles/index.d.ts +0 -1
  189. package/dist/styles/index.mjs +0 -1
  190. package/dist/types/alert.mjs +0 -0
  191. package/dist/types/button.mjs +0 -0
  192. package/dist/types/card.mjs +0 -0
  193. package/dist/types/checkbox.mjs +0 -0
  194. package/dist/types/composables.mjs +0 -0
  195. package/dist/types/config.mjs +0 -15
  196. package/dist/types/dialog.mjs +0 -1
  197. package/dist/types/dropdown.mjs +0 -1
  198. package/dist/types/file-input.mjs +0 -0
  199. package/dist/types/form.mjs +0 -0
  200. package/dist/types/index.mjs +0 -21
  201. package/dist/types/input.mjs +0 -0
  202. package/dist/types/popover.mjs +0 -1
  203. package/dist/types/radio.mjs +0 -1
  204. package/dist/types/select.mjs +0 -1
  205. package/dist/types/sheet.mjs +0 -1
  206. package/dist/types/switch.mjs +0 -0
  207. package/dist/types/textarea.mjs +0 -0
  208. package/dist/types/theme.mjs +0 -42
  209. package/dist/types/toast.mjs +0 -0
  210. package/dist/types/tooltip.mjs +0 -1
  211. package/dist/types/utils.mjs +0 -0
  212. package/dist/utils/cn.mjs +0 -5
  213. package/dist/utils/deepMerge.mjs +0 -18
  214. package/dist/utils/index.mjs +0 -2
@@ -1,137 +0,0 @@
1
- import { computed, ref, readonly } from "vue";
2
- const FILE_ERRORS = {
3
- MAX_SIZE_EXCEEDED: (max) => `\u30D5\u30A1\u30A4\u30EB\u30B5\u30A4\u30BA\u304C${formatFileSize(max)}\u3092\u8D85\u3048\u3066\u3044\u307E\u3059`,
4
- MAX_FILES_EXCEEDED: (max) => `\u6700\u5927${max}\u30D5\u30A1\u30A4\u30EB\u307E\u3067\u30A2\u30C3\u30D7\u30ED\u30FC\u30C9\u3067\u304D\u307E\u3059`,
5
- INVALID_TYPE: "\u8A31\u53EF\u3055\u308C\u3066\u3044\u306A\u3044\u30D5\u30A1\u30A4\u30EB\u5F62\u5F0F\u3067\u3059"
6
- };
7
- const formatFileSize = (bytes) => {
8
- if (bytes < 1024) return `${bytes}B`;
9
- if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}KB`;
10
- return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
11
- };
12
- const createPreviewUrl = (file) => {
13
- if (file.type.startsWith("image/")) {
14
- return URL.createObjectURL(file);
15
- }
16
- return null;
17
- };
18
- export const useFileInput = (props, input_ref) => {
19
- const is_dragging = ref(false);
20
- const files = ref([]);
21
- const error = ref(null);
22
- const state = computed(() => ({
23
- disabled: props.value.disabled ?? false,
24
- is_dragging: is_dragging.value,
25
- has_error: !!error.value,
26
- has_files: files.value.length > 0
27
- }));
28
- const aria_attributes = computed(() => ({
29
- "aria-disabled": state.value.disabled || void 0,
30
- "aria-invalid": state.value.has_error || void 0
31
- }));
32
- const validateFile = (file) => {
33
- const { accept, max_size } = props.value;
34
- if (max_size && file.size > max_size) {
35
- return FILE_ERRORS.MAX_SIZE_EXCEEDED(max_size);
36
- }
37
- if (accept) {
38
- const accepted_types = accept.split(",").map((t) => t.trim());
39
- const is_valid = accepted_types.some((type) => {
40
- if (type.startsWith(".")) {
41
- return file.name.toLowerCase().endsWith(type.toLowerCase());
42
- }
43
- if (type.endsWith("/*")) {
44
- return file.type.startsWith(type.replace("/*", "/"));
45
- }
46
- return file.type === type;
47
- });
48
- if (!is_valid) {
49
- return FILE_ERRORS.INVALID_TYPE;
50
- }
51
- }
52
- return null;
53
- };
54
- const handleFiles = (file_list) => {
55
- if (!file_list || state.value.disabled) return;
56
- error.value = null;
57
- const { multiple, max_files } = props.value;
58
- const new_files = [];
59
- const files_to_process = Array.from(file_list);
60
- for (const file of files_to_process) {
61
- const validation_error = validateFile(file);
62
- if (validation_error) {
63
- error.value = validation_error;
64
- return;
65
- }
66
- new_files.push({
67
- file,
68
- name: file.name,
69
- size: file.size,
70
- type: file.type,
71
- preview_url: createPreviewUrl(file)
72
- });
73
- }
74
- if (multiple) {
75
- const total = files.value.length + new_files.length;
76
- if (max_files && total > max_files) {
77
- error.value = FILE_ERRORS.MAX_FILES_EXCEEDED(max_files);
78
- return;
79
- }
80
- files.value.push(...new_files);
81
- } else {
82
- files.value.forEach((f) => {
83
- if (f.preview_url) URL.revokeObjectURL(f.preview_url);
84
- });
85
- files.value = new_files.slice(0, 1);
86
- }
87
- };
88
- const handleDragEnter = (event) => {
89
- event.preventDefault();
90
- if (!state.value.disabled) {
91
- is_dragging.value = true;
92
- }
93
- };
94
- const handleDragLeave = (event) => {
95
- event.preventDefault();
96
- is_dragging.value = false;
97
- };
98
- const handleDrop = (event) => {
99
- event.preventDefault();
100
- is_dragging.value = false;
101
- if (!state.value.disabled) {
102
- handleFiles(event.dataTransfer?.files ?? null);
103
- }
104
- };
105
- const removeFile = (index) => {
106
- const removed = files.value.splice(index, 1)[0];
107
- if (removed?.preview_url) {
108
- URL.revokeObjectURL(removed.preview_url);
109
- }
110
- };
111
- const clearFiles = () => {
112
- files.value.forEach((f) => {
113
- if (f.preview_url) URL.revokeObjectURL(f.preview_url);
114
- });
115
- files.value = [];
116
- error.value = null;
117
- };
118
- const openFilePicker = () => {
119
- if (!state.value.disabled && input_ref.value) {
120
- input_ref.value.click();
121
- }
122
- };
123
- return {
124
- state: readonly(state),
125
- is_dragging,
126
- files,
127
- error,
128
- aria_attributes: readonly(aria_attributes),
129
- handleFiles,
130
- handleDragEnter,
131
- handleDragLeave,
132
- handleDrop,
133
- removeFile,
134
- clearFiles,
135
- openFilePicker
136
- };
137
- };
@@ -1,159 +0,0 @@
1
- import { reactive, computed, readonly } from "vue";
2
- import { ZodError } from "zod";
3
- import { FORM_ERRORS } from "../constants/errors.mjs";
4
- export const useForm = (schema) => {
5
- const form_state = reactive({
6
- values: {},
7
- errors: {},
8
- touched: {},
9
- is_submitting: false,
10
- is_dirty: false
11
- });
12
- const is_valid = computed(() => {
13
- const result = schema.safeParse(form_state.values);
14
- return result.success;
15
- });
16
- const validateField = (field) => {
17
- form_state.touched[field] = true;
18
- form_state.is_dirty = true;
19
- try {
20
- const zod_object = schema;
21
- const field_schema = zod_object.shape[field];
22
- if (!field_schema) {
23
- throw new Error(FORM_ERRORS.FIELD_NOT_FOUND);
24
- }
25
- field_schema.parse(form_state.values[field]);
26
- form_state.errors[field] = void 0;
27
- } catch (error) {
28
- if (error instanceof ZodError) {
29
- form_state.errors[field] = error.errors[0].message;
30
- } else if (error instanceof Error) {
31
- form_state.errors[field] = error.message;
32
- }
33
- }
34
- };
35
- const validateAll = () => {
36
- try {
37
- schema.parse(form_state.values);
38
- form_state.errors = {};
39
- return true;
40
- } catch (error) {
41
- if (error instanceof ZodError) {
42
- form_state.errors = {};
43
- error.errors.forEach((err) => {
44
- const field = err.path[0];
45
- form_state.errors[field] = err.message;
46
- form_state.touched[field] = true;
47
- });
48
- }
49
- return false;
50
- }
51
- };
52
- const reset = () => {
53
- form_state.values = {};
54
- form_state.errors = {};
55
- form_state.touched = {};
56
- form_state.is_dirty = false;
57
- };
58
- const setValues = (values) => {
59
- Object.assign(form_state.values, values);
60
- form_state.is_dirty = true;
61
- };
62
- const setFieldValue = (field, value) => {
63
- form_state.values[field] = value;
64
- form_state.is_dirty = true;
65
- };
66
- const submit = async (on_submit) => {
67
- if (!validateAll()) {
68
- return;
69
- }
70
- form_state.is_submitting = true;
71
- try {
72
- const validated_data = schema.parse(form_state.values);
73
- await on_submit(validated_data);
74
- } catch (error) {
75
- if (error instanceof ZodError) {
76
- error.errors.forEach((err) => {
77
- const field = err.path[0];
78
- form_state.errors[field] = err.message;
79
- });
80
- }
81
- throw error;
82
- } finally {
83
- form_state.is_submitting = false;
84
- }
85
- };
86
- const getFieldArray = (field) => {
87
- const getArray = () => {
88
- const value = form_state.values[field];
89
- if (!Array.isArray(value)) {
90
- form_state.values[field] = [];
91
- return [];
92
- }
93
- return value;
94
- };
95
- const helpers = {
96
- get fields() {
97
- return getArray();
98
- },
99
- append: (value) => {
100
- const array = getArray();
101
- array.push(value);
102
- form_state.is_dirty = true;
103
- },
104
- prepend: (value) => {
105
- const array = getArray();
106
- array.unshift(value);
107
- form_state.is_dirty = true;
108
- },
109
- insert: (index, value) => {
110
- const array = getArray();
111
- array.splice(index, 0, value);
112
- form_state.is_dirty = true;
113
- },
114
- remove: (index) => {
115
- const array = getArray();
116
- array.splice(index, 1);
117
- form_state.is_dirty = true;
118
- },
119
- move: (from_index, to_index) => {
120
- const array = getArray();
121
- const item = array.splice(from_index, 1)[0];
122
- array.splice(to_index, 0, item);
123
- form_state.is_dirty = true;
124
- },
125
- swap: (index_a, index_b) => {
126
- const array = getArray();
127
- const temp = array[index_a];
128
- array[index_a] = array[index_b];
129
- array[index_b] = temp;
130
- form_state.is_dirty = true;
131
- },
132
- replace: (index, value) => {
133
- const array = getArray();
134
- array[index] = value;
135
- form_state.is_dirty = true;
136
- },
137
- clear: () => {
138
- form_state.values[field] = [];
139
- form_state.is_dirty = true;
140
- }
141
- };
142
- return helpers;
143
- };
144
- return {
145
- values: form_state.values,
146
- errors: readonly(form_state.errors),
147
- touched: readonly(form_state.touched),
148
- is_valid,
149
- is_submitting: computed(() => form_state.is_submitting),
150
- is_dirty: computed(() => form_state.is_dirty),
151
- validateField,
152
- validateAll,
153
- reset,
154
- setValues,
155
- setFieldValue,
156
- submit,
157
- getFieldArray
158
- };
159
- };
@@ -1,31 +0,0 @@
1
- import { computed, ref, readonly } from "vue";
2
- import { useUI } from "./useUIConfig.mjs";
3
- export const useInput = (props) => {
4
- const ui_config = useUI("input");
5
- const is_focused = ref(false);
6
- const state = computed(() => ({
7
- type: props.value.type ?? "text",
8
- size: props.value.size ?? ui_config.default_size,
9
- disabled: props.value.disabled ?? false,
10
- readonly: props.value.readonly ?? false,
11
- has_error: !!props.value.error
12
- }));
13
- const aria_attributes = computed(() => ({
14
- "aria-invalid": state.value.has_error || void 0,
15
- "aria-describedby": props.value.error_id,
16
- "aria-readonly": state.value.readonly || void 0
17
- }));
18
- const handleFocus = () => {
19
- is_focused.value = true;
20
- };
21
- const handleBlur = () => {
22
- is_focused.value = false;
23
- };
24
- return {
25
- state: readonly(state),
26
- is_focused,
27
- aria_attributes: readonly(aria_attributes),
28
- handleFocus,
29
- handleBlur
30
- };
31
- };
@@ -1,113 +0,0 @@
1
- import { ref, computed, onMounted, onUnmounted } from "vue";
2
- let popover_counter = 0;
3
- export function usePopover(props) {
4
- const is_open = ref(false);
5
- const trigger_ref = ref(null);
6
- const content_ref = ref(null);
7
- const popover_id = `popover-${++popover_counter}`;
8
- const open = () => {
9
- is_open.value = true;
10
- };
11
- const close = () => {
12
- is_open.value = false;
13
- };
14
- const toggle = () => {
15
- is_open.value = !is_open.value;
16
- };
17
- const handleTriggerClick = () => {
18
- toggle();
19
- };
20
- const handleKeyDown = (event) => {
21
- if (event.key === "Escape" && is_open.value) {
22
- close();
23
- trigger_ref.value?.focus();
24
- }
25
- };
26
- const handleClickOutside = (event) => {
27
- if (!is_open.value) return;
28
- const target = event.target;
29
- const trigger = trigger_ref.value;
30
- const content = content_ref.value;
31
- if (trigger && trigger.contains(target)) return;
32
- if (content && content.contains(target)) return;
33
- close();
34
- };
35
- const position_styles = computed(() => {
36
- if (!trigger_ref.value || !is_open.value) {
37
- return {};
38
- }
39
- const side = props.value.side ?? "bottom";
40
- const align = props.value.align ?? "center";
41
- const offset = 8;
42
- const styles = {
43
- position: "absolute",
44
- zIndex: "50"
45
- };
46
- switch (side) {
47
- case "top":
48
- styles.bottom = "100%";
49
- styles.marginBottom = `${offset}px`;
50
- break;
51
- case "bottom":
52
- styles.top = "100%";
53
- styles.marginTop = `${offset}px`;
54
- break;
55
- case "left":
56
- styles.right = "100%";
57
- styles.marginRight = `${offset}px`;
58
- break;
59
- case "right":
60
- styles.left = "100%";
61
- styles.marginLeft = `${offset}px`;
62
- break;
63
- }
64
- if (side === "top" || side === "bottom") {
65
- switch (align) {
66
- case "start":
67
- styles.left = "0";
68
- break;
69
- case "center":
70
- styles.left = "50%";
71
- styles.transform = "translateX(-50%)";
72
- break;
73
- case "end":
74
- styles.right = "0";
75
- break;
76
- }
77
- } else {
78
- switch (align) {
79
- case "start":
80
- styles.top = "0";
81
- break;
82
- case "center":
83
- styles.top = "50%";
84
- styles.transform = "translateY(-50%)";
85
- break;
86
- case "end":
87
- styles.bottom = "0";
88
- break;
89
- }
90
- }
91
- return styles;
92
- });
93
- onMounted(() => {
94
- document.addEventListener("mousedown", handleClickOutside);
95
- document.addEventListener("keydown", handleKeyDown);
96
- });
97
- onUnmounted(() => {
98
- document.removeEventListener("mousedown", handleClickOutside);
99
- document.removeEventListener("keydown", handleKeyDown);
100
- });
101
- return {
102
- is_open,
103
- trigger_ref,
104
- content_ref,
105
- popover_id,
106
- position_styles,
107
- open,
108
- close,
109
- toggle,
110
- handleTriggerClick,
111
- handleKeyDown
112
- };
113
- }
@@ -1,23 +0,0 @@
1
- import { computed, inject, readonly } from "vue";
2
- import { RADIO_GROUP_KEY } from "../types/radio.mjs";
3
- import { COMPONENT_ERRORS } from "../constants/errors.mjs";
4
- export const useRadioItem = (props) => {
5
- const context = inject(RADIO_GROUP_KEY, null);
6
- if (!context) {
7
- throw new Error(COMPONENT_ERRORS.PROVIDER_NOT_FOUND);
8
- }
9
- const state = computed(() => ({
10
- size: props.value.size ?? "md",
11
- disabled: props.value.disabled ?? context.disabled.value,
12
- checked: context.model_value.value === props.value.value
13
- }));
14
- const aria_attributes = computed(() => ({
15
- role: "radio",
16
- "aria-checked": state.value.checked,
17
- "aria-disabled": state.value.disabled || void 0
18
- }));
19
- return {
20
- state: readonly(state),
21
- aria_attributes: readonly(aria_attributes)
22
- };
23
- };
@@ -1,42 +0,0 @@
1
- import { computed, inject, readonly } from "vue";
2
- import { SELECT_KEY } from "../types/select.mjs";
3
- import { COMPONENT_ERRORS } from "../constants/errors.mjs";
4
- export const useSelectTrigger = () => {
5
- const context = inject(SELECT_KEY, null);
6
- if (!context) {
7
- throw new Error(COMPONENT_ERRORS.PROVIDER_NOT_FOUND);
8
- }
9
- const state = computed(() => ({
10
- size: context.size.value,
11
- disabled: context.disabled.value,
12
- is_open: context.is_open.value
13
- }));
14
- return {
15
- state: readonly(state)
16
- };
17
- };
18
- export const useSelectContent = () => {
19
- const context = inject(SELECT_KEY, null);
20
- if (!context) {
21
- throw new Error(COMPONENT_ERRORS.PROVIDER_NOT_FOUND);
22
- }
23
- const state = computed(() => ({
24
- is_open: context.is_open.value
25
- }));
26
- return {
27
- state: readonly(state)
28
- };
29
- };
30
- export const useSelectItem = (props) => {
31
- const context = inject(SELECT_KEY, null);
32
- if (!context) {
33
- throw new Error(COMPONENT_ERRORS.PROVIDER_NOT_FOUND);
34
- }
35
- const state = computed(() => ({
36
- selected: context.model_value.value === props.value.value,
37
- disabled: props.value.disabled ?? false
38
- }));
39
- return {
40
- state: readonly(state)
41
- };
42
- };
@@ -1,17 +0,0 @@
1
- import { computed, readonly } from "vue";
2
- export const useSwitch = (props, checked) => {
3
- const state = computed(() => ({
4
- size: props.value.size ?? "md",
5
- disabled: props.value.disabled ?? false,
6
- checked: checked.value
7
- }));
8
- const aria_attributes = computed(() => ({
9
- role: "switch",
10
- "aria-checked": state.value.checked,
11
- "aria-disabled": state.value.disabled || void 0
12
- }));
13
- return {
14
- state: readonly(state),
15
- aria_attributes: readonly(aria_attributes)
16
- };
17
- };
@@ -1,29 +0,0 @@
1
- import { computed, ref, readonly } from "vue";
2
- export const useTextarea = (props) => {
3
- const is_focused = ref(false);
4
- const state = computed(() => ({
5
- size: props.value.size ?? "md",
6
- disabled: props.value.disabled ?? false,
7
- readonly: props.value.readonly ?? false,
8
- has_error: !!props.value.error,
9
- resize: props.value.resize ?? "vertical"
10
- }));
11
- const aria_attributes = computed(() => ({
12
- "aria-invalid": state.value.has_error || void 0,
13
- "aria-describedby": props.value.error_id,
14
- "aria-readonly": state.value.readonly || void 0
15
- }));
16
- const handleFocus = () => {
17
- is_focused.value = true;
18
- };
19
- const handleBlur = () => {
20
- is_focused.value = false;
21
- };
22
- return {
23
- state: readonly(state),
24
- is_focused,
25
- aria_attributes: readonly(aria_attributes),
26
- handleFocus,
27
- handleBlur
28
- };
29
- };
@@ -1,89 +0,0 @@
1
- import { ref, computed, watch, onMounted, onUnmounted } from "vue";
2
- const DEFAULT_OPTIONS = {
3
- default_mode: "system",
4
- storage_key: "soave-ui-theme",
5
- attribute: "data-theme"
6
- };
7
- export function useTheme(options = {}) {
8
- const merged_options = { ...DEFAULT_OPTIONS, ...options };
9
- const mode = ref(merged_options.default_mode);
10
- let media_query = null;
11
- const getSystemTheme = () => {
12
- if (typeof window === "undefined") return "light";
13
- return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
14
- };
15
- const resolved_mode = computed(() => {
16
- if (mode.value === "system") {
17
- return getSystemTheme();
18
- }
19
- return mode.value;
20
- });
21
- const applyTheme = (theme) => {
22
- if (typeof document === "undefined") return;
23
- const root = document.documentElement;
24
- root.setAttribute(merged_options.attribute, theme);
25
- if (theme === "dark") {
26
- root.classList.add("dark");
27
- } else {
28
- root.classList.remove("dark");
29
- }
30
- };
31
- const setMode = (new_mode) => {
32
- mode.value = new_mode;
33
- if (typeof localStorage !== "undefined") {
34
- localStorage.setItem(merged_options.storage_key, new_mode);
35
- }
36
- applyTheme(resolved_mode.value);
37
- };
38
- const toggleMode = () => {
39
- const next_mode = resolved_mode.value === "light" ? "dark" : "light";
40
- setMode(next_mode);
41
- };
42
- const handleSystemThemeChange = (event) => {
43
- if (mode.value === "system") {
44
- applyTheme(event.matches ? "dark" : "light");
45
- }
46
- };
47
- onMounted(() => {
48
- if (typeof localStorage !== "undefined") {
49
- const stored = localStorage.getItem(merged_options.storage_key);
50
- if (stored && ["light", "dark", "system"].includes(stored)) {
51
- mode.value = stored;
52
- }
53
- }
54
- if (typeof window !== "undefined") {
55
- media_query = window.matchMedia("(prefers-color-scheme: dark)");
56
- media_query.addEventListener("change", handleSystemThemeChange);
57
- }
58
- applyTheme(resolved_mode.value);
59
- });
60
- onUnmounted(() => {
61
- if (media_query) {
62
- media_query.removeEventListener("change", handleSystemThemeChange);
63
- }
64
- });
65
- watch(mode, () => {
66
- applyTheme(resolved_mode.value);
67
- });
68
- return {
69
- mode,
70
- resolved_mode,
71
- setMode,
72
- toggleMode
73
- };
74
- }
75
- export function generateThemeCSS(light, dark) {
76
- const formatColor = (key, value) => {
77
- const css_key = key.replace(/_/g, "-");
78
- return ` --${css_key}: ${value};`;
79
- };
80
- const lightCSS = Object.entries(light).map(([key, value]) => formatColor(key, value)).join("\n");
81
- const darkCSS = Object.entries(dark).map(([key, value]) => formatColor(key, value)).join("\n");
82
- return `:root {
83
- ${lightCSS}
84
- }
85
-
86
- .dark {
87
- ${darkCSS}
88
- }`;
89
- }