@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.
- package/dist/build.config.d.ts +2 -0
- package/dist/build.config.mjs +14 -0
- package/dist/components/ui/Alert.vue +39 -0
- package/dist/components/ui/AlertDescription.vue +12 -0
- package/dist/components/ui/AlertTitle.vue +12 -0
- package/dist/components/ui/Button.vue +59 -0
- package/dist/components/ui/Card.vue +15 -0
- package/dist/components/ui/CardContent.vue +12 -0
- package/dist/components/ui/CardDescription.vue +12 -0
- package/dist/components/ui/CardFooter.vue +12 -0
- package/dist/components/ui/CardHeader.vue +12 -0
- package/dist/components/ui/CardTitle.vue +12 -0
- package/dist/components/ui/Checkbox.vue +73 -0
- package/dist/components/ui/Dialog.vue +93 -0
- package/dist/components/ui/DialogDescription.vue +12 -0
- package/dist/components/ui/DialogFooter.vue +12 -0
- package/dist/components/ui/DialogHeader.vue +12 -0
- package/dist/components/ui/DialogTitle.vue +12 -0
- package/dist/components/ui/DropdownMenu.vue +33 -0
- package/dist/components/ui/DropdownMenuContent.vue +66 -0
- package/dist/components/ui/DropdownMenuItem.vue +77 -0
- package/dist/components/ui/DropdownMenuLabel.vue +20 -0
- package/dist/components/ui/DropdownMenuSeparator.vue +16 -0
- package/dist/components/ui/DropdownMenuTrigger.vue +38 -0
- package/dist/components/ui/FileInput.vue +153 -0
- package/dist/components/ui/FormError.vue +20 -0
- package/dist/components/ui/FormField.vue +12 -0
- package/dist/components/ui/FormInput.vue +46 -0
- package/dist/components/ui/FormLabel.vue +19 -0
- package/dist/components/ui/FormTextarea.vue +39 -0
- package/dist/components/ui/Input.vue +49 -0
- package/dist/components/ui/Popover.vue +36 -0
- package/dist/components/ui/PopoverContent.vue +62 -0
- package/dist/components/ui/PopoverTrigger.vue +36 -0
- package/dist/components/ui/RadioGroup.vue +42 -0
- package/dist/components/ui/RadioItem.vue +41 -0
- package/dist/components/ui/Select.vue +55 -0
- package/dist/components/ui/SelectContent.vue +29 -0
- package/dist/components/ui/SelectItem.vue +51 -0
- package/dist/components/ui/SelectTrigger.vue +38 -0
- package/dist/components/ui/SelectValue.vue +16 -0
- package/dist/components/ui/Sheet.vue +140 -0
- package/dist/components/ui/SheetDescription.vue +15 -0
- package/dist/components/ui/SheetFooter.vue +15 -0
- package/dist/components/ui/SheetHeader.vue +15 -0
- package/dist/components/ui/SheetTitle.vue +15 -0
- package/dist/components/ui/Switch.vue +43 -0
- package/dist/components/ui/Textarea.vue +50 -0
- package/dist/components/ui/Toast.vue +107 -0
- package/dist/components/ui/Toaster.vue +80 -0
- package/dist/components/ui/Tooltip.vue +42 -0
- package/dist/components/ui/TooltipContent.vue +68 -0
- package/dist/components/ui/TooltipTrigger.vue +39 -0
- package/dist/components/ui/UIProvider.vue +19 -0
- package/dist/components/ui/index.d.ts +52 -0
- package/dist/components/ui/index.mjs +52 -0
- package/dist/composables/index.d.ts +17 -0
- package/dist/composables/index.mjs +17 -0
- package/dist/composables/useButton.d.ts +8 -0
- package/dist/composables/useButton.mjs +49 -0
- package/dist/composables/useCard.d.ts +8 -0
- package/dist/composables/useCard.mjs +24 -0
- package/dist/composables/useCheckbox.d.ts +7 -0
- package/dist/composables/useCheckbox.mjs +51 -0
- package/dist/composables/useDialog.d.ts +6 -0
- package/dist/composables/useDialog.mjs +19 -0
- package/dist/composables/useDropdown.d.ts +24 -0
- package/dist/composables/useDropdown.mjs +170 -0
- package/dist/composables/useFileInput.d.ts +6 -0
- package/dist/composables/useFileInput.mjs +152 -0
- package/dist/composables/useForm.d.ts +7 -0
- package/dist/composables/useForm.mjs +159 -0
- package/dist/composables/useInput.d.ts +8 -0
- package/dist/composables/useInput.mjs +52 -0
- package/dist/composables/usePopover.d.ts +20 -0
- package/dist/composables/usePopover.mjs +113 -0
- package/dist/composables/useRadio.d.ts +7 -0
- package/dist/composables/useRadio.mjs +55 -0
- package/dist/composables/useSelect.d.ts +17 -0
- package/dist/composables/useSelect.mjs +71 -0
- package/dist/composables/useSwitch.d.ts +7 -0
- package/dist/composables/useSwitch.mjs +50 -0
- package/dist/composables/useTextarea.d.ts +7 -0
- package/dist/composables/useTextarea.mjs +50 -0
- package/dist/composables/useTheme.d.ts +15 -0
- package/dist/composables/useTheme.mjs +89 -0
- package/dist/composables/useToast.d.ts +11 -0
- package/dist/composables/useToast.mjs +64 -0
- package/dist/composables/useTooltip.d.ts +23 -0
- package/dist/composables/useTooltip.mjs +125 -0
- package/dist/composables/useUIConfig.d.ts +28 -0
- package/dist/composables/useUIConfig.mjs +36 -0
- package/dist/constants/errors.d.ts +22 -0
- package/dist/constants/errors.mjs +18 -0
- package/dist/constants/index.d.ts +2 -0
- package/dist/constants/index.mjs +2 -0
- package/dist/constants/logs.d.ts +17 -0
- package/dist/constants/logs.mjs +17 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.mjs +5 -0
- package/dist/types/alert.d.ts +15 -0
- package/dist/types/alert.mjs +0 -0
- package/dist/types/button.d.ts +20 -0
- package/dist/types/button.mjs +0 -0
- package/dist/types/card.d.ts +23 -0
- package/dist/types/card.mjs +0 -0
- package/dist/types/checkbox.d.ts +19 -0
- package/dist/types/checkbox.mjs +0 -0
- package/dist/types/config.d.ts +30 -0
- package/dist/types/config.mjs +15 -0
- package/dist/types/dialog.d.ts +29 -0
- package/dist/types/dialog.mjs +0 -0
- package/dist/types/dropdown.d.ts +27 -0
- package/dist/types/dropdown.mjs +0 -0
- package/dist/types/file-input.d.ts +35 -0
- package/dist/types/file-input.mjs +0 -0
- package/dist/types/form.d.ts +70 -0
- package/dist/types/form.mjs +0 -0
- package/dist/types/index.d.ts +20 -0
- package/dist/types/index.mjs +20 -0
- package/dist/types/input.d.ts +27 -0
- package/dist/types/input.mjs +0 -0
- package/dist/types/popover.d.ts +15 -0
- package/dist/types/popover.mjs +0 -0
- package/dist/types/radio.d.ts +29 -0
- package/dist/types/radio.mjs +1 -0
- package/dist/types/select.d.ts +36 -0
- package/dist/types/select.mjs +1 -0
- package/dist/types/sheet.d.ts +11 -0
- package/dist/types/sheet.mjs +0 -0
- package/dist/types/switch.d.ts +17 -0
- package/dist/types/switch.mjs +0 -0
- package/dist/types/textarea.d.ts +25 -0
- package/dist/types/textarea.mjs +0 -0
- package/dist/types/theme.d.ts +43 -0
- package/dist/types/theme.mjs +42 -0
- package/dist/types/toast.d.ts +38 -0
- package/dist/types/toast.mjs +0 -0
- package/dist/types/tooltip.d.ts +25 -0
- package/dist/types/tooltip.mjs +0 -0
- package/dist/types/utils.d.ts +12 -0
- package/dist/types/utils.mjs +0 -0
- package/dist/utils/cn.d.ts +6 -0
- package/dist/utils/cn.mjs +5 -0
- package/dist/utils/deepMerge.d.ts +6 -0
- package/dist/utils/deepMerge.mjs +18 -0
- package/dist/utils/index.d.ts +2 -0
- package/dist/utils/index.mjs +2 -0
- package/package.json +53 -0
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { defineBuildConfig } from "unbuild";
|
|
2
|
+
export default defineBuildConfig({
|
|
3
|
+
entries: [
|
|
4
|
+
{
|
|
5
|
+
builder: "mkdist",
|
|
6
|
+
input: "./",
|
|
7
|
+
outDir: "./dist",
|
|
8
|
+
pattern: ["**/*.ts", "**/*.vue"],
|
|
9
|
+
declaration: true
|
|
10
|
+
}
|
|
11
|
+
],
|
|
12
|
+
clean: true,
|
|
13
|
+
externals: ["vue", "zod", "clsx", "tailwind-merge"]
|
|
14
|
+
});
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div
|
|
3
|
+
role="alert"
|
|
4
|
+
:class="cn(
|
|
5
|
+
'relative w-full rounded-lg border p-4',
|
|
6
|
+
variant_classes,
|
|
7
|
+
props.class
|
|
8
|
+
)"
|
|
9
|
+
>
|
|
10
|
+
<slot />
|
|
11
|
+
</div>
|
|
12
|
+
</template>
|
|
13
|
+
|
|
14
|
+
<script setup lang="ts">
|
|
15
|
+
import { computed } from "vue"
|
|
16
|
+
import { cn } from "../../utils/cn"
|
|
17
|
+
import { useUI } from "../../composables/useUIConfig"
|
|
18
|
+
import type { AlertVariant } from "../../types/alert"
|
|
19
|
+
|
|
20
|
+
interface Props {
|
|
21
|
+
variant?: AlertVariant
|
|
22
|
+
class?: string
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const props = defineProps<Props>()
|
|
26
|
+
|
|
27
|
+
const ui_config = useUI("alert")
|
|
28
|
+
|
|
29
|
+
const variant_classes = computed(() => {
|
|
30
|
+
const variant_map: Record<AlertVariant, string> = {
|
|
31
|
+
default: "bg-background text-foreground",
|
|
32
|
+
info: "border-blue-200 bg-blue-50 text-blue-900 [&>svg]:text-blue-600",
|
|
33
|
+
success: "border-green-200 bg-green-50 text-green-900 [&>svg]:text-green-600",
|
|
34
|
+
warning: "border-yellow-200 bg-yellow-50 text-yellow-900 [&>svg]:text-yellow-600",
|
|
35
|
+
destructive: "border-destructive/50 text-destructive bg-destructive/10 [&>svg]:text-destructive"
|
|
36
|
+
}
|
|
37
|
+
return variant_map[props.variant ?? ui_config.default_variant]
|
|
38
|
+
})
|
|
39
|
+
</script>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div :class="cn('text-sm [&_p]:leading-relaxed', props.class)">
|
|
3
|
+
<slot />
|
|
4
|
+
</div>
|
|
5
|
+
</template>
|
|
6
|
+
|
|
7
|
+
<script setup lang="ts">
|
|
8
|
+
import { cn } from "../../utils/cn"
|
|
9
|
+
import type { AlertDescriptionProps } from "../../types/alert"
|
|
10
|
+
|
|
11
|
+
const props = defineProps<AlertDescriptionProps>()
|
|
12
|
+
</script>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<h5 :class="cn('mb-1 font-medium leading-none tracking-tight', props.class)">
|
|
3
|
+
<slot />
|
|
4
|
+
</h5>
|
|
5
|
+
</template>
|
|
6
|
+
|
|
7
|
+
<script setup lang="ts">
|
|
8
|
+
import { cn } from "../../utils/cn"
|
|
9
|
+
import type { AlertTitleProps } from "../../types/alert"
|
|
10
|
+
|
|
11
|
+
const props = defineProps<AlertTitleProps>()
|
|
12
|
+
</script>
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<button
|
|
3
|
+
:class="composable.base_classes.value"
|
|
4
|
+
:disabled="composable.is_disabled.value"
|
|
5
|
+
v-bind="composable.aria_attributes.value"
|
|
6
|
+
@click="handleClick"
|
|
7
|
+
>
|
|
8
|
+
<slot name="icon-left" />
|
|
9
|
+
<span v-if="composable.is_loading.value" class="animate-spin">
|
|
10
|
+
<slot name="loading">
|
|
11
|
+
<svg
|
|
12
|
+
class="h-4 w-4"
|
|
13
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
14
|
+
fill="none"
|
|
15
|
+
viewBox="0 0 24 24"
|
|
16
|
+
>
|
|
17
|
+
<circle
|
|
18
|
+
class="opacity-25"
|
|
19
|
+
cx="12"
|
|
20
|
+
cy="12"
|
|
21
|
+
r="10"
|
|
22
|
+
stroke="currentColor"
|
|
23
|
+
stroke-width="4"
|
|
24
|
+
/>
|
|
25
|
+
<path
|
|
26
|
+
class="opacity-75"
|
|
27
|
+
fill="currentColor"
|
|
28
|
+
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
|
|
29
|
+
/>
|
|
30
|
+
</svg>
|
|
31
|
+
</slot>
|
|
32
|
+
</span>
|
|
33
|
+
<slot />
|
|
34
|
+
<slot name="icon-right" />
|
|
35
|
+
</button>
|
|
36
|
+
</template>
|
|
37
|
+
|
|
38
|
+
<script setup lang="ts">
|
|
39
|
+
import { toRef } from "vue"
|
|
40
|
+
import { useButton } from "../../composables/useButton"
|
|
41
|
+
import type { ButtonProps } from "../../types/button"
|
|
42
|
+
|
|
43
|
+
const props = withDefaults(defineProps<ButtonProps>(), {
|
|
44
|
+
disabled: false,
|
|
45
|
+
loading: false
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
const emit = defineEmits<{
|
|
49
|
+
click: [event: MouseEvent]
|
|
50
|
+
}>()
|
|
51
|
+
|
|
52
|
+
const composable = useButton(toRef(() => props))
|
|
53
|
+
|
|
54
|
+
const handleClick = (event: MouseEvent) => {
|
|
55
|
+
if (!composable.is_disabled.value && !composable.is_loading.value) {
|
|
56
|
+
emit("click", event)
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
</script>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div :class="composable.base_classes.value">
|
|
3
|
+
<slot />
|
|
4
|
+
</div>
|
|
5
|
+
</template>
|
|
6
|
+
|
|
7
|
+
<script setup lang="ts">
|
|
8
|
+
import { toRef } from "vue"
|
|
9
|
+
import { useCard } from "../../composables/useCard"
|
|
10
|
+
import type { CardProps } from "../../types/card"
|
|
11
|
+
|
|
12
|
+
const props = defineProps<CardProps>()
|
|
13
|
+
|
|
14
|
+
const composable = useCard(toRef(() => props))
|
|
15
|
+
</script>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div :class="cn('p-6 pt-0', props.class)">
|
|
3
|
+
<slot />
|
|
4
|
+
</div>
|
|
5
|
+
</template>
|
|
6
|
+
|
|
7
|
+
<script setup lang="ts">
|
|
8
|
+
import { cn } from "../../utils/cn"
|
|
9
|
+
import type { CardContentProps } from "../../types/card"
|
|
10
|
+
|
|
11
|
+
const props = defineProps<CardContentProps>()
|
|
12
|
+
</script>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<p :class="cn('text-sm text-muted-foreground', props.class)">
|
|
3
|
+
<slot />
|
|
4
|
+
</p>
|
|
5
|
+
</template>
|
|
6
|
+
|
|
7
|
+
<script setup lang="ts">
|
|
8
|
+
import { cn } from "../../utils/cn"
|
|
9
|
+
import type { CardDescriptionProps } from "../../types/card"
|
|
10
|
+
|
|
11
|
+
const props = defineProps<CardDescriptionProps>()
|
|
12
|
+
</script>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div :class="cn('flex items-center p-6 pt-0', props.class)">
|
|
3
|
+
<slot />
|
|
4
|
+
</div>
|
|
5
|
+
</template>
|
|
6
|
+
|
|
7
|
+
<script setup lang="ts">
|
|
8
|
+
import { cn } from "../../utils/cn"
|
|
9
|
+
import type { CardFooterProps } from "../../types/card"
|
|
10
|
+
|
|
11
|
+
const props = defineProps<CardFooterProps>()
|
|
12
|
+
</script>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div :class="cn('flex flex-col space-y-1.5 p-6', props.class)">
|
|
3
|
+
<slot />
|
|
4
|
+
</div>
|
|
5
|
+
</template>
|
|
6
|
+
|
|
7
|
+
<script setup lang="ts">
|
|
8
|
+
import { cn } from "../../utils/cn"
|
|
9
|
+
import type { CardHeaderProps } from "../../types/card"
|
|
10
|
+
|
|
11
|
+
const props = defineProps<CardHeaderProps>()
|
|
12
|
+
</script>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<h3 :class="cn('text-2xl font-semibold leading-none tracking-tight', props.class)">
|
|
3
|
+
<slot />
|
|
4
|
+
</h3>
|
|
5
|
+
</template>
|
|
6
|
+
|
|
7
|
+
<script setup lang="ts">
|
|
8
|
+
import { cn } from "../../utils/cn"
|
|
9
|
+
import type { CardTitleProps } from "../../types/card"
|
|
10
|
+
|
|
11
|
+
const props = defineProps<CardTitleProps>()
|
|
12
|
+
</script>
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<button
|
|
3
|
+
type="button"
|
|
4
|
+
:class="composable.base_classes.value"
|
|
5
|
+
:disabled="composable.is_disabled.value"
|
|
6
|
+
:data-state="dataState"
|
|
7
|
+
v-bind="composable.aria_attributes.value"
|
|
8
|
+
@click="handleClick"
|
|
9
|
+
>
|
|
10
|
+
<span
|
|
11
|
+
v-if="modelValue || composable.is_indeterminate.value"
|
|
12
|
+
:class="composable.indicator_classes.value"
|
|
13
|
+
>
|
|
14
|
+
<svg
|
|
15
|
+
v-if="composable.is_indeterminate.value"
|
|
16
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
17
|
+
viewBox="0 0 24 24"
|
|
18
|
+
fill="none"
|
|
19
|
+
stroke="currentColor"
|
|
20
|
+
stroke-width="3"
|
|
21
|
+
stroke-linecap="round"
|
|
22
|
+
>
|
|
23
|
+
<line x1="5" y1="12" x2="19" y2="12" />
|
|
24
|
+
</svg>
|
|
25
|
+
<svg
|
|
26
|
+
v-else
|
|
27
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
28
|
+
viewBox="0 0 24 24"
|
|
29
|
+
fill="none"
|
|
30
|
+
stroke="currentColor"
|
|
31
|
+
stroke-width="3"
|
|
32
|
+
stroke-linecap="round"
|
|
33
|
+
stroke-linejoin="round"
|
|
34
|
+
>
|
|
35
|
+
<polyline points="20 6 9 17 4 12" />
|
|
36
|
+
</svg>
|
|
37
|
+
</span>
|
|
38
|
+
</button>
|
|
39
|
+
</template>
|
|
40
|
+
|
|
41
|
+
<script setup lang="ts">
|
|
42
|
+
import { computed, toRef } from "vue"
|
|
43
|
+
import { useCheckbox } from "../../composables/useCheckbox"
|
|
44
|
+
import type { CheckboxProps } from "../../types/checkbox"
|
|
45
|
+
|
|
46
|
+
interface Props extends CheckboxProps {
|
|
47
|
+
modelValue?: boolean
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
51
|
+
modelValue: false,
|
|
52
|
+
disabled: false,
|
|
53
|
+
indeterminate: false
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
const emit = defineEmits<{
|
|
57
|
+
"update:modelValue": [value: boolean]
|
|
58
|
+
}>()
|
|
59
|
+
|
|
60
|
+
const checked = toRef(() => props.modelValue)
|
|
61
|
+
const composable = useCheckbox(toRef(() => props), checked)
|
|
62
|
+
|
|
63
|
+
const dataState = computed(() => {
|
|
64
|
+
if (props.indeterminate) return "indeterminate"
|
|
65
|
+
return props.modelValue ? "checked" : "unchecked"
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
const handleClick = () => {
|
|
69
|
+
if (!composable.is_disabled.value) {
|
|
70
|
+
emit("update:modelValue", !props.modelValue)
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
</script>
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<Teleport to="body">
|
|
3
|
+
<Transition name="dialog">
|
|
4
|
+
<div
|
|
5
|
+
v-if="is_open"
|
|
6
|
+
class="fixed inset-0 z-50 flex items-center justify-center"
|
|
7
|
+
>
|
|
8
|
+
<div
|
|
9
|
+
class="fixed inset-0 bg-black/80"
|
|
10
|
+
@click="handleOverlayClick"
|
|
11
|
+
/>
|
|
12
|
+
<div
|
|
13
|
+
class="relative z-50 grid w-full max-w-lg gap-4 border bg-background p-6 shadow-lg sm:rounded-lg"
|
|
14
|
+
role="dialog"
|
|
15
|
+
aria-modal="true"
|
|
16
|
+
>
|
|
17
|
+
<slot />
|
|
18
|
+
<button
|
|
19
|
+
v-if="show_close_button"
|
|
20
|
+
class="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none"
|
|
21
|
+
@click="close"
|
|
22
|
+
>
|
|
23
|
+
<svg
|
|
24
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
25
|
+
width="24"
|
|
26
|
+
height="24"
|
|
27
|
+
viewBox="0 0 24 24"
|
|
28
|
+
fill="none"
|
|
29
|
+
stroke="currentColor"
|
|
30
|
+
stroke-width="2"
|
|
31
|
+
stroke-linecap="round"
|
|
32
|
+
stroke-linejoin="round"
|
|
33
|
+
class="h-4 w-4"
|
|
34
|
+
>
|
|
35
|
+
<path d="M18 6 6 18" />
|
|
36
|
+
<path d="m6 6 12 12" />
|
|
37
|
+
</svg>
|
|
38
|
+
<span class="sr-only">閉じる</span>
|
|
39
|
+
</button>
|
|
40
|
+
</div>
|
|
41
|
+
</div>
|
|
42
|
+
</Transition>
|
|
43
|
+
</Teleport>
|
|
44
|
+
</template>
|
|
45
|
+
|
|
46
|
+
<script setup lang="ts">
|
|
47
|
+
import { computed, provide, type InjectionKey } from "vue"
|
|
48
|
+
|
|
49
|
+
interface Props {
|
|
50
|
+
open?: boolean
|
|
51
|
+
modal?: boolean
|
|
52
|
+
showCloseButton?: boolean
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
56
|
+
open: false,
|
|
57
|
+
modal: true,
|
|
58
|
+
showCloseButton: true
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
const emit = defineEmits<{
|
|
62
|
+
"update:open": [value: boolean]
|
|
63
|
+
}>()
|
|
64
|
+
|
|
65
|
+
const is_open = computed({
|
|
66
|
+
get: () => props.open,
|
|
67
|
+
set: (value) => emit("update:open", value)
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
const show_close_button = computed(() => props.showCloseButton)
|
|
71
|
+
|
|
72
|
+
const close = () => {
|
|
73
|
+
is_open.value = false
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const handleOverlayClick = () => {
|
|
77
|
+
if (props.modal) {
|
|
78
|
+
close()
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export interface DialogContext {
|
|
83
|
+
close: () => void
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export const DialogKey: InjectionKey<DialogContext> = Symbol("dialog")
|
|
87
|
+
|
|
88
|
+
provide(DialogKey, { close })
|
|
89
|
+
</script>
|
|
90
|
+
|
|
91
|
+
<style scoped>
|
|
92
|
+
.dialog-enter-active,.dialog-leave-active{transition:opacity .2s ease}.dialog-enter-from,.dialog-leave-to{opacity:0}
|
|
93
|
+
</style>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<p :class="cn('text-sm text-muted-foreground', props.class)">
|
|
3
|
+
<slot />
|
|
4
|
+
</p>
|
|
5
|
+
</template>
|
|
6
|
+
|
|
7
|
+
<script setup lang="ts">
|
|
8
|
+
import { cn } from "../../utils/cn"
|
|
9
|
+
import type { DialogDescriptionProps } from "../../types/dialog"
|
|
10
|
+
|
|
11
|
+
const props = defineProps<DialogDescriptionProps>()
|
|
12
|
+
</script>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div :class="cn('flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2', props.class)">
|
|
3
|
+
<slot />
|
|
4
|
+
</div>
|
|
5
|
+
</template>
|
|
6
|
+
|
|
7
|
+
<script setup lang="ts">
|
|
8
|
+
import { cn } from "../../utils/cn"
|
|
9
|
+
import type { DialogFooterProps } from "../../types/dialog"
|
|
10
|
+
|
|
11
|
+
const props = defineProps<DialogFooterProps>()
|
|
12
|
+
</script>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div :class="cn('flex flex-col space-y-1.5 text-center sm:text-left', props.class)">
|
|
3
|
+
<slot />
|
|
4
|
+
</div>
|
|
5
|
+
</template>
|
|
6
|
+
|
|
7
|
+
<script setup lang="ts">
|
|
8
|
+
import { cn } from "../../utils/cn"
|
|
9
|
+
import type { DialogHeaderProps } from "../../types/dialog"
|
|
10
|
+
|
|
11
|
+
const props = defineProps<DialogHeaderProps>()
|
|
12
|
+
</script>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<h2 :class="cn('text-lg font-semibold leading-none tracking-tight', props.class)">
|
|
3
|
+
<slot />
|
|
4
|
+
</h2>
|
|
5
|
+
</template>
|
|
6
|
+
|
|
7
|
+
<script setup lang="ts">
|
|
8
|
+
import { cn } from "../../utils/cn"
|
|
9
|
+
import type { DialogTitleProps } from "../../types/dialog"
|
|
10
|
+
|
|
11
|
+
const props = defineProps<DialogTitleProps>()
|
|
12
|
+
</script>
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="relative inline-block" :class="props.class">
|
|
3
|
+
<slot />
|
|
4
|
+
</div>
|
|
5
|
+
</template>
|
|
6
|
+
|
|
7
|
+
<script setup lang="ts">
|
|
8
|
+
import { provide, ref, type InjectionKey, type Ref } from "vue"
|
|
9
|
+
import { useDropdown, type UseDropdownReturn } from "../../composables/useDropdown"
|
|
10
|
+
import type { DropdownSide, DropdownAlign } from "../../types/dropdown"
|
|
11
|
+
|
|
12
|
+
export interface Props {
|
|
13
|
+
side?: DropdownSide
|
|
14
|
+
align?: DropdownAlign
|
|
15
|
+
class?: string
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
19
|
+
side: "bottom",
|
|
20
|
+
align: "start"
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
export const DROPDOWN_CONTEXT_KEY: InjectionKey<UseDropdownReturn> = Symbol("dropdown-context")
|
|
24
|
+
|
|
25
|
+
const dropdown_props = ref({
|
|
26
|
+
side: props.side,
|
|
27
|
+
align: props.align
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
const dropdown = useDropdown(dropdown_props as Ref<typeof dropdown_props.value>)
|
|
31
|
+
|
|
32
|
+
provide(DROPDOWN_CONTEXT_KEY, dropdown)
|
|
33
|
+
</script>
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<Transition name="dropdown">
|
|
3
|
+
<div
|
|
4
|
+
v-if="is_open"
|
|
5
|
+
ref="content_element"
|
|
6
|
+
:id="dropdown_id"
|
|
7
|
+
role="menu"
|
|
8
|
+
:aria-labelledby="`${dropdown_id}-trigger`"
|
|
9
|
+
:style="position_styles"
|
|
10
|
+
:class="cn(base_classes, props.class)"
|
|
11
|
+
tabindex="-1"
|
|
12
|
+
@keydown="handleContentKeyDown"
|
|
13
|
+
>
|
|
14
|
+
<slot />
|
|
15
|
+
</div>
|
|
16
|
+
</Transition>
|
|
17
|
+
</template>
|
|
18
|
+
|
|
19
|
+
<script setup lang="ts">
|
|
20
|
+
import { inject, ref, watchEffect, nextTick, watch } from "vue"
|
|
21
|
+
import { cn } from "../../utils/cn"
|
|
22
|
+
import { DROPDOWN_CONTEXT_KEY } from "./DropdownMenu.vue"
|
|
23
|
+
|
|
24
|
+
export interface Props {
|
|
25
|
+
class?: string
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const props = defineProps<Props>()
|
|
29
|
+
|
|
30
|
+
const context = inject(DROPDOWN_CONTEXT_KEY)
|
|
31
|
+
|
|
32
|
+
if (!context) {
|
|
33
|
+
throw new Error("DropdownMenuContent must be used within a DropdownMenu component")
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const {
|
|
37
|
+
is_open,
|
|
38
|
+
content_ref,
|
|
39
|
+
dropdown_id,
|
|
40
|
+
position_styles,
|
|
41
|
+
handleContentKeyDown
|
|
42
|
+
} = context
|
|
43
|
+
|
|
44
|
+
const content_element = ref<HTMLElement | null>(null)
|
|
45
|
+
|
|
46
|
+
watchEffect(() => {
|
|
47
|
+
content_ref.value = content_element.value
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
watch(is_open, async (open) => {
|
|
51
|
+
if (open) {
|
|
52
|
+
await nextTick()
|
|
53
|
+
content_element.value?.focus()
|
|
54
|
+
}
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
const base_classes = cn(
|
|
58
|
+
"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1",
|
|
59
|
+
"text-popover-foreground shadow-md outline-none",
|
|
60
|
+
"animate-in fade-in-0 zoom-in-95"
|
|
61
|
+
)
|
|
62
|
+
</script>
|
|
63
|
+
|
|
64
|
+
<style scoped>
|
|
65
|
+
.dropdown-enter-active,.dropdown-leave-active{transition:opacity .15s ease,transform .15s ease}.dropdown-enter-from,.dropdown-leave-to{opacity:0;transform:scale(.95)}
|
|
66
|
+
</style>
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div
|
|
3
|
+
role="menuitem"
|
|
4
|
+
:tabindex="disabled ? -1 : 0"
|
|
5
|
+
:class="cn(item_classes, props.class)"
|
|
6
|
+
:aria-disabled="disabled"
|
|
7
|
+
:data-highlighted="is_active ? '' : undefined"
|
|
8
|
+
:data-disabled="disabled ? '' : undefined"
|
|
9
|
+
@click="handleClick"
|
|
10
|
+
@mouseenter="handleMouseEnter"
|
|
11
|
+
@keydown.enter="handleClick"
|
|
12
|
+
@keydown.space.prevent="handleClick"
|
|
13
|
+
>
|
|
14
|
+
<slot />
|
|
15
|
+
</div>
|
|
16
|
+
</template>
|
|
17
|
+
|
|
18
|
+
<script setup lang="ts">
|
|
19
|
+
import { inject, computed, onMounted } from "vue"
|
|
20
|
+
import { cn } from "../../utils/cn"
|
|
21
|
+
import { DROPDOWN_CONTEXT_KEY } from "./DropdownMenu.vue"
|
|
22
|
+
|
|
23
|
+
export interface Props {
|
|
24
|
+
disabled?: boolean
|
|
25
|
+
destructive?: boolean
|
|
26
|
+
class?: string
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
30
|
+
disabled: false,
|
|
31
|
+
destructive: false
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
const emit = defineEmits<{
|
|
35
|
+
select: []
|
|
36
|
+
}>()
|
|
37
|
+
|
|
38
|
+
const context = inject(DROPDOWN_CONTEXT_KEY)
|
|
39
|
+
|
|
40
|
+
if (!context) {
|
|
41
|
+
throw new Error("DropdownMenuItem must be used within a DropdownMenu component")
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const { active_item_index, close, registerItem, setActiveItem } = context
|
|
45
|
+
|
|
46
|
+
let item_index = -1
|
|
47
|
+
|
|
48
|
+
onMounted(() => {
|
|
49
|
+
if (!props.disabled) {
|
|
50
|
+
item_index = registerItem()
|
|
51
|
+
}
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
const is_active = computed(() => {
|
|
55
|
+
return active_item_index.value === item_index && !props.disabled
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
const handleClick = (): void => {
|
|
59
|
+
if (props.disabled) return
|
|
60
|
+
emit("select")
|
|
61
|
+
close()
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const handleMouseEnter = (): void => {
|
|
65
|
+
if (!props.disabled && item_index >= 0) {
|
|
66
|
+
setActiveItem(item_index)
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const item_classes = computed(() => cn(
|
|
71
|
+
"relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none",
|
|
72
|
+
"transition-colors focus:bg-accent focus:text-accent-foreground",
|
|
73
|
+
"data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground",
|
|
74
|
+
"data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
75
|
+
props.destructive && "text-destructive focus:text-destructive"
|
|
76
|
+
))
|
|
77
|
+
</script>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div
|
|
3
|
+
:class="cn('px-2 py-1.5 text-sm font-semibold', inset && 'pl-8', props.class)"
|
|
4
|
+
>
|
|
5
|
+
<slot />
|
|
6
|
+
</div>
|
|
7
|
+
</template>
|
|
8
|
+
|
|
9
|
+
<script setup lang="ts">
|
|
10
|
+
import { cn } from "../../utils/cn"
|
|
11
|
+
|
|
12
|
+
export interface Props {
|
|
13
|
+
inset?: boolean
|
|
14
|
+
class?: string
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
18
|
+
inset: false
|
|
19
|
+
})
|
|
20
|
+
</script>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div
|
|
3
|
+
role="separator"
|
|
4
|
+
:class="cn('-mx-1 my-1 h-px bg-muted', props.class)"
|
|
5
|
+
/>
|
|
6
|
+
</template>
|
|
7
|
+
|
|
8
|
+
<script setup lang="ts">
|
|
9
|
+
import { cn } from "../../utils/cn"
|
|
10
|
+
|
|
11
|
+
export interface Props {
|
|
12
|
+
class?: string
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const props = defineProps<Props>()
|
|
16
|
+
</script>
|