@soave/ui 0.1.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 (149) hide show
  1. package/dist/build.config.d.ts +2 -0
  2. package/dist/build.config.mjs +14 -0
  3. package/dist/components/ui/Alert.vue +39 -0
  4. package/dist/components/ui/AlertDescription.vue +12 -0
  5. package/dist/components/ui/AlertTitle.vue +12 -0
  6. package/dist/components/ui/Button.vue +59 -0
  7. package/dist/components/ui/Card.vue +15 -0
  8. package/dist/components/ui/CardContent.vue +12 -0
  9. package/dist/components/ui/CardDescription.vue +12 -0
  10. package/dist/components/ui/CardFooter.vue +12 -0
  11. package/dist/components/ui/CardHeader.vue +12 -0
  12. package/dist/components/ui/CardTitle.vue +12 -0
  13. package/dist/components/ui/Checkbox.vue +73 -0
  14. package/dist/components/ui/Dialog.vue +93 -0
  15. package/dist/components/ui/DialogDescription.vue +12 -0
  16. package/dist/components/ui/DialogFooter.vue +12 -0
  17. package/dist/components/ui/DialogHeader.vue +12 -0
  18. package/dist/components/ui/DialogTitle.vue +12 -0
  19. package/dist/components/ui/DropdownMenu.vue +33 -0
  20. package/dist/components/ui/DropdownMenuContent.vue +66 -0
  21. package/dist/components/ui/DropdownMenuItem.vue +77 -0
  22. package/dist/components/ui/DropdownMenuLabel.vue +20 -0
  23. package/dist/components/ui/DropdownMenuSeparator.vue +16 -0
  24. package/dist/components/ui/DropdownMenuTrigger.vue +38 -0
  25. package/dist/components/ui/FileInput.vue +153 -0
  26. package/dist/components/ui/FormError.vue +20 -0
  27. package/dist/components/ui/FormField.vue +12 -0
  28. package/dist/components/ui/FormInput.vue +46 -0
  29. package/dist/components/ui/FormLabel.vue +19 -0
  30. package/dist/components/ui/FormTextarea.vue +39 -0
  31. package/dist/components/ui/Input.vue +49 -0
  32. package/dist/components/ui/Popover.vue +36 -0
  33. package/dist/components/ui/PopoverContent.vue +62 -0
  34. package/dist/components/ui/PopoverTrigger.vue +36 -0
  35. package/dist/components/ui/RadioGroup.vue +42 -0
  36. package/dist/components/ui/RadioItem.vue +41 -0
  37. package/dist/components/ui/Select.vue +55 -0
  38. package/dist/components/ui/SelectContent.vue +29 -0
  39. package/dist/components/ui/SelectItem.vue +51 -0
  40. package/dist/components/ui/SelectTrigger.vue +38 -0
  41. package/dist/components/ui/SelectValue.vue +16 -0
  42. package/dist/components/ui/Sheet.vue +140 -0
  43. package/dist/components/ui/SheetDescription.vue +15 -0
  44. package/dist/components/ui/SheetFooter.vue +15 -0
  45. package/dist/components/ui/SheetHeader.vue +15 -0
  46. package/dist/components/ui/SheetTitle.vue +15 -0
  47. package/dist/components/ui/Switch.vue +43 -0
  48. package/dist/components/ui/Textarea.vue +50 -0
  49. package/dist/components/ui/Toast.vue +107 -0
  50. package/dist/components/ui/Toaster.vue +80 -0
  51. package/dist/components/ui/Tooltip.vue +42 -0
  52. package/dist/components/ui/TooltipContent.vue +68 -0
  53. package/dist/components/ui/TooltipTrigger.vue +39 -0
  54. package/dist/components/ui/UIProvider.vue +19 -0
  55. package/dist/components/ui/index.d.ts +52 -0
  56. package/dist/components/ui/index.mjs +52 -0
  57. package/dist/composables/index.d.ts +17 -0
  58. package/dist/composables/index.mjs +17 -0
  59. package/dist/composables/useButton.d.ts +8 -0
  60. package/dist/composables/useButton.mjs +49 -0
  61. package/dist/composables/useCard.d.ts +8 -0
  62. package/dist/composables/useCard.mjs +24 -0
  63. package/dist/composables/useCheckbox.d.ts +7 -0
  64. package/dist/composables/useCheckbox.mjs +51 -0
  65. package/dist/composables/useDialog.d.ts +6 -0
  66. package/dist/composables/useDialog.mjs +19 -0
  67. package/dist/composables/useDropdown.d.ts +24 -0
  68. package/dist/composables/useDropdown.mjs +170 -0
  69. package/dist/composables/useFileInput.d.ts +6 -0
  70. package/dist/composables/useFileInput.mjs +152 -0
  71. package/dist/composables/useForm.d.ts +7 -0
  72. package/dist/composables/useForm.mjs +159 -0
  73. package/dist/composables/useInput.d.ts +8 -0
  74. package/dist/composables/useInput.mjs +52 -0
  75. package/dist/composables/usePopover.d.ts +20 -0
  76. package/dist/composables/usePopover.mjs +113 -0
  77. package/dist/composables/useRadio.d.ts +7 -0
  78. package/dist/composables/useRadio.mjs +55 -0
  79. package/dist/composables/useSelect.d.ts +17 -0
  80. package/dist/composables/useSelect.mjs +71 -0
  81. package/dist/composables/useSwitch.d.ts +7 -0
  82. package/dist/composables/useSwitch.mjs +50 -0
  83. package/dist/composables/useTextarea.d.ts +7 -0
  84. package/dist/composables/useTextarea.mjs +50 -0
  85. package/dist/composables/useTheme.d.ts +15 -0
  86. package/dist/composables/useTheme.mjs +89 -0
  87. package/dist/composables/useToast.d.ts +11 -0
  88. package/dist/composables/useToast.mjs +64 -0
  89. package/dist/composables/useTooltip.d.ts +23 -0
  90. package/dist/composables/useTooltip.mjs +125 -0
  91. package/dist/composables/useUIConfig.d.ts +28 -0
  92. package/dist/composables/useUIConfig.mjs +36 -0
  93. package/dist/constants/errors.d.ts +22 -0
  94. package/dist/constants/errors.mjs +18 -0
  95. package/dist/constants/index.d.ts +2 -0
  96. package/dist/constants/index.mjs +2 -0
  97. package/dist/constants/logs.d.ts +17 -0
  98. package/dist/constants/logs.mjs +17 -0
  99. package/dist/index.d.ts +5 -0
  100. package/dist/index.mjs +5 -0
  101. package/dist/types/alert.d.ts +15 -0
  102. package/dist/types/alert.mjs +0 -0
  103. package/dist/types/button.d.ts +20 -0
  104. package/dist/types/button.mjs +0 -0
  105. package/dist/types/card.d.ts +23 -0
  106. package/dist/types/card.mjs +0 -0
  107. package/dist/types/checkbox.d.ts +19 -0
  108. package/dist/types/checkbox.mjs +0 -0
  109. package/dist/types/config.d.ts +30 -0
  110. package/dist/types/config.mjs +15 -0
  111. package/dist/types/dialog.d.ts +29 -0
  112. package/dist/types/dialog.mjs +0 -0
  113. package/dist/types/dropdown.d.ts +27 -0
  114. package/dist/types/dropdown.mjs +0 -0
  115. package/dist/types/file-input.d.ts +35 -0
  116. package/dist/types/file-input.mjs +0 -0
  117. package/dist/types/form.d.ts +70 -0
  118. package/dist/types/form.mjs +0 -0
  119. package/dist/types/index.d.ts +20 -0
  120. package/dist/types/index.mjs +20 -0
  121. package/dist/types/input.d.ts +27 -0
  122. package/dist/types/input.mjs +0 -0
  123. package/dist/types/popover.d.ts +15 -0
  124. package/dist/types/popover.mjs +0 -0
  125. package/dist/types/radio.d.ts +29 -0
  126. package/dist/types/radio.mjs +1 -0
  127. package/dist/types/select.d.ts +36 -0
  128. package/dist/types/select.mjs +1 -0
  129. package/dist/types/sheet.d.ts +11 -0
  130. package/dist/types/sheet.mjs +0 -0
  131. package/dist/types/switch.d.ts +17 -0
  132. package/dist/types/switch.mjs +0 -0
  133. package/dist/types/textarea.d.ts +25 -0
  134. package/dist/types/textarea.mjs +0 -0
  135. package/dist/types/theme.d.ts +43 -0
  136. package/dist/types/theme.mjs +42 -0
  137. package/dist/types/toast.d.ts +38 -0
  138. package/dist/types/toast.mjs +0 -0
  139. package/dist/types/tooltip.d.ts +25 -0
  140. package/dist/types/tooltip.mjs +0 -0
  141. package/dist/types/utils.d.ts +12 -0
  142. package/dist/types/utils.mjs +0 -0
  143. package/dist/utils/cn.d.ts +6 -0
  144. package/dist/utils/cn.mjs +5 -0
  145. package/dist/utils/deepMerge.d.ts +6 -0
  146. package/dist/utils/deepMerge.mjs +18 -0
  147. package/dist/utils/index.d.ts +2 -0
  148. package/dist/utils/index.mjs +2 -0
  149. package/package.json +53 -0
@@ -0,0 +1,50 @@
1
+ import { computed } from "vue";
2
+ import { cn } from "../utils/cn.mjs";
3
+ export const useSwitch = (props, checked) => {
4
+ const is_disabled = computed(() => props.value.disabled ?? false);
5
+ const track_size_classes = computed(() => {
6
+ const size_map = {
7
+ sm: "h-5 w-9",
8
+ md: "h-6 w-11",
9
+ lg: "h-7 w-14"
10
+ };
11
+ return size_map[props.value.size ?? "md"];
12
+ });
13
+ const track_classes = computed(
14
+ () => cn(
15
+ "peer inline-flex shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent",
16
+ "transition-colors",
17
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background",
18
+ "disabled:cursor-not-allowed disabled:opacity-50",
19
+ "data-[state=checked]:bg-primary data-[state=unchecked]:bg-input",
20
+ track_size_classes.value
21
+ )
22
+ );
23
+ const thumb_size_classes = computed(() => {
24
+ const size_map = {
25
+ sm: "h-4 w-4 data-[state=checked]:translate-x-4",
26
+ md: "h-5 w-5 data-[state=checked]:translate-x-5",
27
+ lg: "h-6 w-6 data-[state=checked]:translate-x-7"
28
+ };
29
+ return size_map[props.value.size ?? "md"];
30
+ });
31
+ const thumb_classes = computed(
32
+ () => cn(
33
+ "pointer-events-none block rounded-full bg-background shadow-lg ring-0",
34
+ "transition-transform",
35
+ "data-[state=unchecked]:translate-x-0",
36
+ thumb_size_classes.value
37
+ )
38
+ );
39
+ const aria_attributes = computed(() => ({
40
+ role: "switch",
41
+ "aria-checked": checked.value,
42
+ "aria-disabled": is_disabled.value || void 0
43
+ }));
44
+ return {
45
+ track_classes,
46
+ thumb_classes,
47
+ is_disabled,
48
+ aria_attributes
49
+ };
50
+ };
@@ -0,0 +1,7 @@
1
+ import { type Ref } from "vue";
2
+ import type { TextareaProps, TextareaReturn } from "../types/textarea";
3
+ /**
4
+ * Textareaコンポーネントのロジックを提供するComposable
5
+ * error, disabled, readonly, resizeに基づいてクラスとaria属性を生成
6
+ */
7
+ export declare const useTextarea: (props: Ref<TextareaProps>) => TextareaReturn;
@@ -0,0 +1,50 @@
1
+ import { computed, ref } from "vue";
2
+ import { cn } from "../utils/cn.mjs";
3
+ export const useTextarea = (props) => {
4
+ const is_focused = ref(false);
5
+ const has_error = computed(() => !!props.value.error);
6
+ const is_disabled = computed(() => props.value.disabled ?? false);
7
+ const is_readonly = computed(() => props.value.readonly ?? false);
8
+ const resize_classes = computed(() => {
9
+ const resize_map = {
10
+ none: "resize-none",
11
+ vertical: "resize-y",
12
+ horizontal: "resize-x",
13
+ both: "resize"
14
+ };
15
+ return resize_map[props.value.resize ?? "vertical"];
16
+ });
17
+ const base_classes = computed(
18
+ () => cn(
19
+ "flex min-h-[80px] w-full rounded-md border border-input bg-background px-3 py-2",
20
+ "text-sm ring-offset-background",
21
+ "placeholder:text-muted-foreground",
22
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
23
+ "disabled:cursor-not-allowed disabled:opacity-50",
24
+ resize_classes.value,
25
+ has_error.value && "border-destructive focus-visible:ring-destructive",
26
+ is_readonly.value && "bg-muted"
27
+ )
28
+ );
29
+ const aria_attributes = computed(() => ({
30
+ "aria-invalid": has_error.value || void 0,
31
+ "aria-describedby": props.value.error_id,
32
+ "aria-readonly": is_readonly.value || void 0
33
+ }));
34
+ const handleFocus = () => {
35
+ is_focused.value = true;
36
+ };
37
+ const handleBlur = () => {
38
+ is_focused.value = false;
39
+ };
40
+ return {
41
+ base_classes,
42
+ is_focused,
43
+ has_error,
44
+ is_disabled,
45
+ is_readonly,
46
+ aria_attributes,
47
+ handleFocus,
48
+ handleBlur
49
+ };
50
+ };
@@ -0,0 +1,15 @@
1
+ import { type Ref, type ComputedRef } from "vue";
2
+ import type { ThemeMode, ThemeColors } from "../types/theme";
3
+ export interface UseThemeOptions {
4
+ default_mode?: ThemeMode;
5
+ storage_key?: string;
6
+ attribute?: string;
7
+ }
8
+ export interface UseThemeReturn {
9
+ mode: Ref<ThemeMode>;
10
+ resolved_mode: ComputedRef<"light" | "dark">;
11
+ setMode: (mode: ThemeMode) => void;
12
+ toggleMode: () => void;
13
+ }
14
+ export declare function useTheme(options?: UseThemeOptions): UseThemeReturn;
15
+ export declare function generateThemeCSS(light: ThemeColors, dark: ThemeColors): string;
@@ -0,0 +1,89 @@
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
+ }
@@ -0,0 +1,11 @@
1
+ import type { ToastProps, ToastReturn } from "../types/toast";
2
+ export declare function useToast(): ToastReturn;
3
+ export declare const toast: {
4
+ default: (props: Omit<ToastProps, "variant">) => string;
5
+ success: (props: Omit<ToastProps, "variant">) => string;
6
+ error: (props: Omit<ToastProps, "variant">) => string;
7
+ warning: (props: Omit<ToastProps, "variant">) => string;
8
+ info: (props: Omit<ToastProps, "variant">) => string;
9
+ dismiss: (id: string) => void;
10
+ dismissAll: () => void;
11
+ };
@@ -0,0 +1,64 @@
1
+ import { ref } from "vue";
2
+ const toasts = ref([]);
3
+ let toast_counter = 0;
4
+ const generateId = () => {
5
+ toast_counter++;
6
+ return `toast-${toast_counter}-${Date.now()}`;
7
+ };
8
+ export function useToast() {
9
+ const add = (props) => {
10
+ const id = props.id ?? generateId();
11
+ const toast2 = {
12
+ id,
13
+ title: props.title,
14
+ description: props.description,
15
+ variant: props.variant ?? "default",
16
+ duration: props.duration ?? 5e3,
17
+ dismissible: props.dismissible ?? true,
18
+ action: props.action,
19
+ created_at: Date.now()
20
+ };
21
+ toasts.value = [...toasts.value, toast2];
22
+ if (toast2.duration > 0) {
23
+ setTimeout(() => {
24
+ dismiss(id);
25
+ }, toast2.duration);
26
+ }
27
+ return id;
28
+ };
29
+ const dismiss = (id) => {
30
+ toasts.value = toasts.value.filter((t) => t.id !== id);
31
+ };
32
+ const dismissAll = () => {
33
+ toasts.value = [];
34
+ };
35
+ const update = (id, props) => {
36
+ toasts.value = toasts.value.map((toast2) => {
37
+ if (toast2.id === id) {
38
+ return {
39
+ ...toast2,
40
+ ...props,
41
+ id: toast2.id,
42
+ created_at: toast2.created_at
43
+ };
44
+ }
45
+ return toast2;
46
+ });
47
+ };
48
+ return {
49
+ toasts: toasts.value,
50
+ add,
51
+ dismiss,
52
+ dismissAll,
53
+ update
54
+ };
55
+ }
56
+ export const toast = {
57
+ default: (props) => useToast().add({ ...props, variant: "default" }),
58
+ success: (props) => useToast().add({ ...props, variant: "success" }),
59
+ error: (props) => useToast().add({ ...props, variant: "error" }),
60
+ warning: (props) => useToast().add({ ...props, variant: "warning" }),
61
+ info: (props) => useToast().add({ ...props, variant: "info" }),
62
+ dismiss: (id) => useToast().dismiss(id),
63
+ dismissAll: () => useToast().dismissAll()
64
+ };
@@ -0,0 +1,23 @@
1
+ import { type Ref, type ComputedRef } from "vue";
2
+ import type { TooltipSide, TooltipAlign } from "../types/tooltip";
3
+ export interface UseTooltipProps {
4
+ side?: TooltipSide;
5
+ align?: TooltipAlign;
6
+ delay_duration?: number;
7
+ skip_delay_duration?: number;
8
+ disabled?: boolean;
9
+ }
10
+ export interface UseTooltipReturn {
11
+ is_open: Ref<boolean>;
12
+ trigger_ref: Ref<HTMLElement | null>;
13
+ content_ref: Ref<HTMLElement | null>;
14
+ tooltip_id: string;
15
+ position_styles: ComputedRef<Record<string, string>>;
16
+ show: () => void;
17
+ hide: () => void;
18
+ handleMouseEnter: () => void;
19
+ handleMouseLeave: () => void;
20
+ handleFocus: () => void;
21
+ handleBlur: () => void;
22
+ }
23
+ export declare function useTooltip(props: Ref<UseTooltipProps>): UseTooltipReturn;
@@ -0,0 +1,125 @@
1
+ import { ref, computed, onUnmounted } from "vue";
2
+ let tooltip_counter = 0;
3
+ export function useTooltip(props) {
4
+ const is_open = ref(false);
5
+ const trigger_ref = ref(null);
6
+ const content_ref = ref(null);
7
+ const tooltip_id = `tooltip-${++tooltip_counter}`;
8
+ let show_timeout = null;
9
+ let hide_timeout = null;
10
+ const clearTimeouts = () => {
11
+ if (show_timeout) {
12
+ clearTimeout(show_timeout);
13
+ show_timeout = null;
14
+ }
15
+ if (hide_timeout) {
16
+ clearTimeout(hide_timeout);
17
+ hide_timeout = null;
18
+ }
19
+ };
20
+ const show = () => {
21
+ if (props.value.disabled) return;
22
+ clearTimeouts();
23
+ is_open.value = true;
24
+ };
25
+ const hide = () => {
26
+ clearTimeouts();
27
+ is_open.value = false;
28
+ };
29
+ const handleMouseEnter = () => {
30
+ if (props.value.disabled) return;
31
+ clearTimeouts();
32
+ const delay = props.value.delay_duration ?? 200;
33
+ show_timeout = setTimeout(() => {
34
+ is_open.value = true;
35
+ }, delay);
36
+ };
37
+ const handleMouseLeave = () => {
38
+ clearTimeouts();
39
+ const skip_delay = props.value.skip_delay_duration ?? 100;
40
+ hide_timeout = setTimeout(() => {
41
+ is_open.value = false;
42
+ }, skip_delay);
43
+ };
44
+ const handleFocus = () => {
45
+ if (props.value.disabled) return;
46
+ show();
47
+ };
48
+ const handleBlur = () => {
49
+ hide();
50
+ };
51
+ const position_styles = computed(() => {
52
+ if (!trigger_ref.value || !is_open.value) {
53
+ return {};
54
+ }
55
+ const side = props.value.side ?? "top";
56
+ const align = props.value.align ?? "center";
57
+ const offset = 8;
58
+ const styles = {
59
+ position: "absolute",
60
+ zIndex: "50"
61
+ };
62
+ switch (side) {
63
+ case "top":
64
+ styles.bottom = "100%";
65
+ styles.marginBottom = `${offset}px`;
66
+ break;
67
+ case "bottom":
68
+ styles.top = "100%";
69
+ styles.marginTop = `${offset}px`;
70
+ break;
71
+ case "left":
72
+ styles.right = "100%";
73
+ styles.marginRight = `${offset}px`;
74
+ break;
75
+ case "right":
76
+ styles.left = "100%";
77
+ styles.marginLeft = `${offset}px`;
78
+ break;
79
+ }
80
+ if (side === "top" || side === "bottom") {
81
+ switch (align) {
82
+ case "start":
83
+ styles.left = "0";
84
+ break;
85
+ case "center":
86
+ styles.left = "50%";
87
+ styles.transform = "translateX(-50%)";
88
+ break;
89
+ case "end":
90
+ styles.right = "0";
91
+ break;
92
+ }
93
+ } else {
94
+ switch (align) {
95
+ case "start":
96
+ styles.top = "0";
97
+ break;
98
+ case "center":
99
+ styles.top = "50%";
100
+ styles.transform = "translateY(-50%)";
101
+ break;
102
+ case "end":
103
+ styles.bottom = "0";
104
+ break;
105
+ }
106
+ }
107
+ return styles;
108
+ });
109
+ onUnmounted(() => {
110
+ clearTimeouts();
111
+ });
112
+ return {
113
+ is_open,
114
+ trigger_ref,
115
+ content_ref,
116
+ tooltip_id,
117
+ position_styles,
118
+ show,
119
+ hide,
120
+ handleMouseEnter,
121
+ handleMouseLeave,
122
+ handleFocus,
123
+ handleBlur
124
+ };
125
+ }
@@ -0,0 +1,28 @@
1
+ import { type InjectionKey } from "vue";
2
+ import type { DeepPartial } from "../types/utils";
3
+ import type { UIConfig, ButtonConfig, InputConfig, CardConfig, AlertConfig } from "../types/config";
4
+ /**
5
+ * UIConfig用のInjectionKey
6
+ */
7
+ export declare const UI_CONFIG_KEY: InjectionKey<UIConfig>;
8
+ /**
9
+ * UIProviderで設定を提供する
10
+ * @param config - 部分的なUI設定(デフォルト値とマージされる)
11
+ * @returns マージされたUI設定
12
+ */
13
+ export declare const useUIProvider: (config?: DeepPartial<UIConfig>) => UIConfig;
14
+ /**
15
+ * コンポーネント固有の設定を取得する
16
+ * @param component - コンポーネント名
17
+ * @returns 読み取り専用の設定
18
+ * @throws Provider未設定時はデフォルト設定を使用
19
+ */
20
+ export declare function useUI(component: "button"): Readonly<ButtonConfig>;
21
+ export declare function useUI(component: "input"): Readonly<InputConfig>;
22
+ export declare function useUI(component: "card"): Readonly<CardConfig>;
23
+ export declare function useUI(component: "alert"): Readonly<AlertConfig>;
24
+ /**
25
+ * 全体のUI設定を取得する
26
+ * @returns 読み取り専用の全体設定
27
+ */
28
+ export declare const useUIConfig: () => Readonly<UIConfig>;
@@ -0,0 +1,36 @@
1
+ import { inject, provide, reactive, readonly } from "vue";
2
+ import { DEFAULT_UI_CONFIG } from "../types/config.mjs";
3
+ import { deepMerge } from "../utils/deepMerge.mjs";
4
+ import { COMPONENT_ERRORS } from "../constants/errors.mjs";
5
+ import { CONFIG_LOGS } from "../constants/logs.mjs";
6
+ export const UI_CONFIG_KEY = Symbol("ui-config");
7
+ export const useUIProvider = (config = {}) => {
8
+ const merged_config = reactive(
9
+ deepMerge(DEFAULT_UI_CONFIG, config)
10
+ );
11
+ provide(UI_CONFIG_KEY, merged_config);
12
+ if (import.meta.env.DEV) {
13
+ console.debug(CONFIG_LOGS.PROVIDER_INITIALIZED, merged_config);
14
+ }
15
+ return merged_config;
16
+ };
17
+ export function useUI(component) {
18
+ const config = inject(UI_CONFIG_KEY, null);
19
+ if (!config) {
20
+ if (import.meta.env.DEV) {
21
+ console.warn(COMPONENT_ERRORS.PROVIDER_NOT_FOUND);
22
+ }
23
+ return readonly(DEFAULT_UI_CONFIG[component]);
24
+ }
25
+ return readonly(config[component]);
26
+ }
27
+ export const useUIConfig = () => {
28
+ const config = inject(UI_CONFIG_KEY, null);
29
+ if (!config) {
30
+ if (import.meta.env.DEV) {
31
+ console.warn(COMPONENT_ERRORS.PROVIDER_NOT_FOUND);
32
+ }
33
+ return readonly(DEFAULT_UI_CONFIG);
34
+ }
35
+ return readonly(config);
36
+ };
@@ -0,0 +1,22 @@
1
+ export declare const FORM_ERRORS: {
2
+ readonly FIELD_NOT_FOUND: "指定されたフィールドがスキーマに存在しません";
3
+ readonly VALIDATION_FAILED: "バリデーションに失敗しました";
4
+ readonly SUBMIT_FAILED: "フォームの送信に失敗しました";
5
+ readonly SCHEMA_INVALID: "無効なスキーマが指定されました";
6
+ };
7
+ export declare const COMPONENT_ERRORS: {
8
+ readonly INVALID_VARIANT: "無効なvariantが指定されました";
9
+ readonly INVALID_SIZE: "無効なsizeが指定されました";
10
+ readonly PROVIDER_NOT_FOUND: "UIProviderが見つかりません。アプリケーションをUIProviderでラップしてください";
11
+ };
12
+ export declare const DIALOG_ERRORS: {
13
+ readonly ALREADY_OPEN: "ダイアログは既に開いています";
14
+ readonly ALREADY_CLOSED: "ダイアログは既に閉じています";
15
+ };
16
+ export declare const INPUT_ERRORS: {
17
+ readonly INVALID_TYPE: "無効なinputタイプが指定されました";
18
+ };
19
+ export type FormErrorKey = keyof typeof FORM_ERRORS;
20
+ export type ComponentErrorKey = keyof typeof COMPONENT_ERRORS;
21
+ export type DialogErrorKey = keyof typeof DIALOG_ERRORS;
22
+ export type InputErrorKey = keyof typeof INPUT_ERRORS;
@@ -0,0 +1,18 @@
1
+ export const FORM_ERRORS = {
2
+ FIELD_NOT_FOUND: "\u6307\u5B9A\u3055\u308C\u305F\u30D5\u30A3\u30FC\u30EB\u30C9\u304C\u30B9\u30AD\u30FC\u30DE\u306B\u5B58\u5728\u3057\u307E\u305B\u3093",
3
+ VALIDATION_FAILED: "\u30D0\u30EA\u30C7\u30FC\u30B7\u30E7\u30F3\u306B\u5931\u6557\u3057\u307E\u3057\u305F",
4
+ SUBMIT_FAILED: "\u30D5\u30A9\u30FC\u30E0\u306E\u9001\u4FE1\u306B\u5931\u6557\u3057\u307E\u3057\u305F",
5
+ SCHEMA_INVALID: "\u7121\u52B9\u306A\u30B9\u30AD\u30FC\u30DE\u304C\u6307\u5B9A\u3055\u308C\u307E\u3057\u305F"
6
+ };
7
+ export const COMPONENT_ERRORS = {
8
+ INVALID_VARIANT: "\u7121\u52B9\u306Avariant\u304C\u6307\u5B9A\u3055\u308C\u307E\u3057\u305F",
9
+ INVALID_SIZE: "\u7121\u52B9\u306Asize\u304C\u6307\u5B9A\u3055\u308C\u307E\u3057\u305F",
10
+ PROVIDER_NOT_FOUND: "UIProvider\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093\u3002\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u3092UIProvider\u3067\u30E9\u30C3\u30D7\u3057\u3066\u304F\u3060\u3055\u3044"
11
+ };
12
+ export const DIALOG_ERRORS = {
13
+ ALREADY_OPEN: "\u30C0\u30A4\u30A2\u30ED\u30B0\u306F\u65E2\u306B\u958B\u3044\u3066\u3044\u307E\u3059",
14
+ ALREADY_CLOSED: "\u30C0\u30A4\u30A2\u30ED\u30B0\u306F\u65E2\u306B\u9589\u3058\u3066\u3044\u307E\u3059"
15
+ };
16
+ export const INPUT_ERRORS = {
17
+ INVALID_TYPE: "\u7121\u52B9\u306Ainput\u30BF\u30A4\u30D7\u304C\u6307\u5B9A\u3055\u308C\u307E\u3057\u305F"
18
+ };
@@ -0,0 +1,2 @@
1
+ export * from "./errors";
2
+ export * from "./logs";
@@ -0,0 +1,2 @@
1
+ export * from "./errors.mjs";
2
+ export * from "./logs.mjs";
@@ -0,0 +1,17 @@
1
+ export declare const FORM_LOGS: {
2
+ readonly FIELD_VALIDATED: (field: string) => string;
3
+ readonly FORM_SUBMITTED: "フォームが送信されました";
4
+ readonly FORM_RESET: "フォームがリセットされました";
5
+ readonly VALIDATION_STARTED: "バリデーションを開始します";
6
+ };
7
+ export declare const COMPONENT_LOGS: {
8
+ readonly DIALOG_OPENED: "ダイアログが開かれました";
9
+ readonly DIALOG_CLOSED: "ダイアログが閉じられました";
10
+ readonly BUTTON_CLICKED: "ボタンがクリックされました";
11
+ readonly INPUT_FOCUSED: "入力フィールドにフォーカスが当たりました";
12
+ readonly INPUT_BLURRED: "入力フィールドからフォーカスが外れました";
13
+ };
14
+ export declare const CONFIG_LOGS: {
15
+ readonly PROVIDER_INITIALIZED: "UIProviderが初期化されました";
16
+ readonly CONFIG_UPDATED: "UI設定が更新されました";
17
+ };
@@ -0,0 +1,17 @@
1
+ export const FORM_LOGS = {
2
+ FIELD_VALIDATED: (field) => `\u30D5\u30A3\u30FC\u30EB\u30C9 "${field}" \u306E\u30D0\u30EA\u30C7\u30FC\u30B7\u30E7\u30F3\u304C\u5B8C\u4E86\u3057\u307E\u3057\u305F`,
3
+ FORM_SUBMITTED: "\u30D5\u30A9\u30FC\u30E0\u304C\u9001\u4FE1\u3055\u308C\u307E\u3057\u305F",
4
+ FORM_RESET: "\u30D5\u30A9\u30FC\u30E0\u304C\u30EA\u30BB\u30C3\u30C8\u3055\u308C\u307E\u3057\u305F",
5
+ VALIDATION_STARTED: "\u30D0\u30EA\u30C7\u30FC\u30B7\u30E7\u30F3\u3092\u958B\u59CB\u3057\u307E\u3059"
6
+ };
7
+ export const COMPONENT_LOGS = {
8
+ DIALOG_OPENED: "\u30C0\u30A4\u30A2\u30ED\u30B0\u304C\u958B\u304B\u308C\u307E\u3057\u305F",
9
+ DIALOG_CLOSED: "\u30C0\u30A4\u30A2\u30ED\u30B0\u304C\u9589\u3058\u3089\u308C\u307E\u3057\u305F",
10
+ BUTTON_CLICKED: "\u30DC\u30BF\u30F3\u304C\u30AF\u30EA\u30C3\u30AF\u3055\u308C\u307E\u3057\u305F",
11
+ INPUT_FOCUSED: "\u5165\u529B\u30D5\u30A3\u30FC\u30EB\u30C9\u306B\u30D5\u30A9\u30FC\u30AB\u30B9\u304C\u5F53\u305F\u308A\u307E\u3057\u305F",
12
+ INPUT_BLURRED: "\u5165\u529B\u30D5\u30A3\u30FC\u30EB\u30C9\u304B\u3089\u30D5\u30A9\u30FC\u30AB\u30B9\u304C\u5916\u308C\u307E\u3057\u305F"
13
+ };
14
+ export const CONFIG_LOGS = {
15
+ PROVIDER_INITIALIZED: "UIProvider\u304C\u521D\u671F\u5316\u3055\u308C\u307E\u3057\u305F",
16
+ CONFIG_UPDATED: "UI\u8A2D\u5B9A\u304C\u66F4\u65B0\u3055\u308C\u307E\u3057\u305F"
17
+ };
@@ -0,0 +1,5 @@
1
+ export * from "./composables";
2
+ export * from "./components/ui";
3
+ export * from "./types";
4
+ export * from "./utils";
5
+ export * from "./constants";
package/dist/index.mjs ADDED
@@ -0,0 +1,5 @@
1
+ export * from "./composables/index.mjs";
2
+ export * from "./components/ui/index.mjs";
3
+ export * from "./types/index.mjs";
4
+ export * from "./utils/index.mjs";
5
+ export * from "./constants/index.mjs";
@@ -0,0 +1,15 @@
1
+ import type { ComputedRef } from "vue";
2
+ export type AlertVariant = "default" | "info" | "success" | "warning" | "destructive";
3
+ export interface AlertProps {
4
+ variant?: AlertVariant;
5
+ }
6
+ export interface AlertReturn {
7
+ base_classes: ComputedRef<string>;
8
+ icon_name: ComputedRef<string>;
9
+ }
10
+ export interface AlertTitleProps {
11
+ class?: string;
12
+ }
13
+ export interface AlertDescriptionProps {
14
+ class?: string;
15
+ }
File without changes
@@ -0,0 +1,20 @@
1
+ import type { ComputedRef } from "vue";
2
+ export type ButtonVariant = "primary" | "secondary" | "ghost" | "outline" | "destructive";
3
+ export type ButtonSize = "sm" | "md" | "lg";
4
+ export interface ButtonProps {
5
+ variant?: ButtonVariant;
6
+ size?: ButtonSize;
7
+ disabled?: boolean;
8
+ loading?: boolean;
9
+ }
10
+ export interface ButtonAriaAttributes {
11
+ "aria-disabled"?: boolean;
12
+ "aria-busy"?: boolean;
13
+ role: "button";
14
+ }
15
+ export interface ButtonReturn {
16
+ base_classes: ComputedRef<string>;
17
+ is_disabled: ComputedRef<boolean>;
18
+ is_loading: ComputedRef<boolean>;
19
+ aria_attributes: ComputedRef<ButtonAriaAttributes>;
20
+ }
File without changes