@proj-airi/ui 0.8.1-beta.2 → 0.8.1-beta.4
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/package.json +4 -3
- package/src/components/animations/index.ts +9 -0
- package/src/components/{Form/Checkbox/Checkbox.vue → form/checkbox/checkbox.vue} +1 -0
- package/src/components/form/checkbox/index.ts +1 -0
- package/src/components/form/combobox/index.ts +1 -0
- package/src/components/{Form/Field/FieldCheckbox.vue → form/field/field-checkbox.vue} +1 -1
- package/src/components/{Form/Field/FieldInput.vue → form/field/field-input.vue} +3 -3
- package/src/components/{Form/Field/FieldKeyValues.vue → form/field/field-key-values.vue} +2 -2
- package/src/components/{Form/Field/FieldRange.vue → form/field/field-range.vue} +1 -1
- package/src/components/{Form/Field/FieldSelect.vue → form/field/field-select.vue} +1 -1
- package/src/components/{Form/Field/FieldTextArea.vue → form/field/field-text-area.vue} +2 -2
- package/src/components/{Form/Field/FieldValues.vue → form/field/field-values.vue} +1 -1
- package/src/components/form/field/index.ts +7 -0
- package/src/components/form/index.ts +9 -0
- package/src/components/form/input/index.ts +4 -0
- package/src/components/{Form/Input/InputFile.vue → form/input/input-file.vue} +1 -1
- package/src/components/{Form/Input/InputKeyValue.vue → form/input/input-key-value.vue} +1 -1
- package/src/components/form/input/input.vue +71 -0
- package/src/components/form/radio/index.ts +1 -0
- package/src/components/form/range/index.ts +3 -0
- package/src/components/form/select/index.ts +2 -0
- package/src/components/{Form/Select/Select.vue → form/select/select.vue} +1 -1
- package/src/components/form/select-tab/index.ts +1 -0
- package/src/components/{Form/SelectTab/SelectTab.vue → form/select-tab/select-tab.vue} +1 -0
- package/src/components/form/textarea/index.ts +2 -0
- package/src/components/{Form/Textarea/Textarea.vue → form/textarea/textarea.vue} +1 -1
- package/src/components/layouts/collapsible.vue +39 -0
- package/src/components/layouts/index.ts +3 -0
- package/src/components/layouts/screen.vue +72 -0
- package/src/components/layouts/skeleton.vue +75 -0
- package/src/components/misc/button.vue +174 -0
- package/src/components/misc/callout.vue +77 -0
- package/src/components/{Misc/DoubleCheckButton.vue → misc/double-check-button.vue} +1 -1
- package/src/components/misc/index.ts +4 -0
- package/src/components/misc/progress.vue +44 -0
- package/src/fallback.css +4 -0
- package/src/index.ts +4 -3
- package/src/components/Animations/index.ts +0 -3
- package/src/components/Form/Checkbox/index.ts +0 -1
- package/src/components/Form/Combobox/index.ts +0 -1
- package/src/components/Form/Field/index.ts +0 -7
- package/src/components/Form/Input/Input.vue +0 -23
- package/src/components/Form/Input/index.ts +0 -4
- package/src/components/Form/Radio/index.ts +0 -1
- package/src/components/Form/Range/index.ts +0 -3
- package/src/components/Form/Select/index.ts +0 -2
- package/src/components/Form/SelectTab/index.ts +0 -1
- package/src/components/Form/Textarea/index.ts +0 -2
- package/src/components/Form/index.ts +0 -9
- package/src/components/Misc/Button.vue +0 -118
- package/src/components/Misc/index.ts +0 -2
- /package/src/components/{Animations/BidirectionalTransition.vue → animations/transition-bidirectional.vue} +0 -0
- /package/src/components/{Animations/TransitionHorizontal.vue → animations/transition-horizontal.vue} +0 -0
- /package/src/components/{Animations/TransitionVertical.vue → animations/transition-vertical.vue} +0 -0
- /package/src/components/{Form/Combobox/Combobox.vue → form/combobox/combobox.vue} +0 -0
- /package/src/components/{Form/Input/BasicInputFile.vue → form/input/basic-input-file.vue} +0 -0
- /package/src/components/{Form/Radio/Radio.vue → form/radio/radio.vue} +0 -0
- /package/src/components/{Form/Range/ColorHueRange.vue → form/range/color-hue-range.vue} +0 -0
- /package/src/components/{Form/Range/Range.vue → form/range/range.vue} +0 -0
- /package/src/components/{Form/Range/RoundRange.vue → form/range/round-range.vue} +0 -0
- /package/src/components/{Form/Select/Option.vue → form/select/option.vue} +0 -0
- /package/src/components/{Form/Textarea/Basic.vue → form/textarea/basic-text-area.vue} +0 -0
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@proj-airi/ui",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.8.1-beta.
|
|
4
|
+
"version": "0.8.1-beta.4",
|
|
5
5
|
"description": "A collection of UI components that used by Project AIRI",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Moeru AI Project AIRI Team",
|
|
@@ -16,8 +16,9 @@
|
|
|
16
16
|
},
|
|
17
17
|
"exports": {
|
|
18
18
|
".": "./src/index.ts",
|
|
19
|
-
"./components/animations": "./src/components/
|
|
20
|
-
"./components/form": "./src/components/
|
|
19
|
+
"./components/animations": "./src/components/animations/index.ts",
|
|
20
|
+
"./components/form": "./src/components/form/index.ts",
|
|
21
|
+
"./components/layouts": "./src/components/layouts/index.ts",
|
|
21
22
|
"./*": "./*"
|
|
22
23
|
},
|
|
23
24
|
"dependencies": {
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { default as TransitionBidirectional } from './transition-bidirectional.vue'
|
|
2
|
+
export {
|
|
3
|
+
/**
|
|
4
|
+
* @deprecated Use TransitionBidirectional instead
|
|
5
|
+
*/
|
|
6
|
+
default as BidirectionalTransition,
|
|
7
|
+
} from './transition-bidirectional.vue'
|
|
8
|
+
export { default as TransitionHorizontal } from './transition-horizontal.vue'
|
|
9
|
+
export { default as TransitionVertical } from './transition-vertical.vue'
|
|
@@ -11,6 +11,7 @@ const modelValue = defineModel<boolean>({ required: true })
|
|
|
11
11
|
'duration-250 ease-in-out',
|
|
12
12
|
'focus-within:outline-none',
|
|
13
13
|
'flex',
|
|
14
|
+
'is-interacting',
|
|
14
15
|
'border-neutral-300 dark:border-neutral-700 data-[state=checked]:border-primary-200 data-[state=unchecked]:border-neutral-300 focus-within:border-neutral-800',
|
|
15
16
|
'data-[state=checked]:bg-primary-400 data-[state=unchecked]:bg-neutral-300 data-[state=checked]:dark:bg-primary-400/80 dark:data-[state=unchecked]:bg-neutral-800',
|
|
16
17
|
'relative h-7 w-12.5 rounded-full',
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as Checkbox } from './checkbox.vue'
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as Combobox } from './combobox.vue'
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import Input from '../
|
|
2
|
+
import { Input } from '../input'
|
|
3
3
|
|
|
4
4
|
const props = withDefaults(defineProps<{
|
|
5
5
|
label?: string
|
|
@@ -24,9 +24,9 @@ const modelValue = defineModel<string>({ required: false })
|
|
|
24
24
|
<slot name="label">
|
|
25
25
|
{{ props.label }}
|
|
26
26
|
</slot>
|
|
27
|
-
<span v-if="props.required
|
|
27
|
+
<span v-if="props.required" class="text-red-500">*</span>
|
|
28
28
|
</div>
|
|
29
|
-
<div class="text-xs text-neutral-500 dark:text-neutral-400" text-
|
|
29
|
+
<div class="text-xs text-neutral-500 dark:text-neutral-400" text-wrap>
|
|
30
30
|
<slot name="description">
|
|
31
31
|
{{ props.description }}
|
|
32
32
|
</slot>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { ref, watch } from 'vue'
|
|
3
3
|
|
|
4
|
-
import InputKeyValue from '../
|
|
4
|
+
import { InputKeyValue } from '../input'
|
|
5
5
|
|
|
6
6
|
const props = defineProps<{
|
|
7
7
|
label?: string
|
|
@@ -37,7 +37,7 @@ watch([inputKey, inputValue], () => {
|
|
|
37
37
|
</slot>
|
|
38
38
|
<span v-if="props.required !== false" class="text-red-500">*</span>
|
|
39
39
|
</div>
|
|
40
|
-
<div class="text-xs text-neutral-500 dark:text-neutral-400"
|
|
40
|
+
<div class="text-xs text-neutral-500 dark:text-neutral-400">
|
|
41
41
|
<slot name="description">
|
|
42
42
|
{{ props.description }}
|
|
43
43
|
</slot>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import Textarea from '../
|
|
2
|
+
import { Textarea } from '../textarea'
|
|
3
3
|
|
|
4
4
|
const props = withDefaults(defineProps<{
|
|
5
5
|
label?: string
|
|
@@ -25,7 +25,7 @@ const modelValue = defineModel<string>({ required: false })
|
|
|
25
25
|
</slot>
|
|
26
26
|
<span v-if="props.required !== false" class="text-red-500">*</span>
|
|
27
27
|
</div>
|
|
28
|
-
<div class="text-xs text-neutral-500 dark:text-neutral-400"
|
|
28
|
+
<div class="text-xs text-neutral-500 dark:text-neutral-400">
|
|
29
29
|
<slot name="description">
|
|
30
30
|
{{ props.description }}
|
|
31
31
|
</slot>
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { default as FieldCheckbox } from './field-checkbox.vue'
|
|
2
|
+
export { default as FieldInput } from './field-input.vue'
|
|
3
|
+
export { default as FieldKeyValues } from './field-key-values.vue'
|
|
4
|
+
export { default as FieldRange } from './field-range.vue'
|
|
5
|
+
export { default as FieldSelect } from './field-select.vue'
|
|
6
|
+
export { default as FieldTextArea } from './field-text-area.vue'
|
|
7
|
+
export { default as FieldValues } from './field-values.vue'
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
// Define button variants for better type safety and maintainability
|
|
3
|
+
type InputVariant = 'primary' | 'secondary' | 'primary-dimmed'
|
|
4
|
+
|
|
5
|
+
type InputTheme = 'default'
|
|
6
|
+
|
|
7
|
+
// Define size options for better flexibility
|
|
8
|
+
type InputSize = 'sm' | 'md' | 'lg'
|
|
9
|
+
|
|
10
|
+
const props = withDefaults(defineProps<{
|
|
11
|
+
type?: string
|
|
12
|
+
variant?: InputVariant // Button style variant
|
|
13
|
+
size?: InputSize // Button size variant
|
|
14
|
+
theme?: InputTheme // Button theme
|
|
15
|
+
}>(), {
|
|
16
|
+
variant: 'primary',
|
|
17
|
+
size: 'md',
|
|
18
|
+
theme: 'default',
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
const modelValue = defineModel<string>({ required: false })
|
|
22
|
+
|
|
23
|
+
const variantClasses: Record<InputVariant, Record<InputTheme, {
|
|
24
|
+
default: string[]
|
|
25
|
+
}>> = {
|
|
26
|
+
'primary': {
|
|
27
|
+
default: {
|
|
28
|
+
default: [
|
|
29
|
+
'w-full rounded-lg px-2 py-1 text-nowrap text-sm outline-none',
|
|
30
|
+
'bg-neutral-50 dark:bg-neutral-950 focus:bg-neutral-50 dark:focus:bg-neutral-900',
|
|
31
|
+
'focus:border-primary-300 dark:focus:border-primary-400/50 border-2 border-solid border-neutral-100 dark:border-neutral-900',
|
|
32
|
+
'text-disabled:neutral-400 dark:text-disabled:neutral-600',
|
|
33
|
+
'shadow-sm',
|
|
34
|
+
],
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
'secondary': {
|
|
38
|
+
default: {
|
|
39
|
+
default: [
|
|
40
|
+
'w-full rounded-lg px-2 py-1 text-nowrap text-sm outline-none',
|
|
41
|
+
'bg-neutral-50 dark:bg-neutral-950 focus:bg-neutral-50 dark:focus:bg-neutral-900',
|
|
42
|
+
'focus:border-primary-300 dark:focus:border-primary-400/50 border-2 border-solid border-neutral-100 dark:border-neutral-900',
|
|
43
|
+
'text-disabled:neutral-400 dark:text-disabled:neutral-600',
|
|
44
|
+
'shadow-sm',
|
|
45
|
+
],
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
'primary-dimmed': {
|
|
49
|
+
default: {
|
|
50
|
+
default: [
|
|
51
|
+
'w-full rounded-lg px-2 py-1 text-nowrap text-sm outline-none',
|
|
52
|
+
'bg-neutral-100 dark:bg-neutral-800 focus:bg-neutral-50 dark:focus:bg-neutral-950',
|
|
53
|
+
'focus:border-primary-500/30 dark:focus:border-primary-400/50 border-2 border-solid border-neutral-500/5 dark:border-neutral-700/40',
|
|
54
|
+
'text-disabled:neutral-400 dark:text-disabled:neutral-600',
|
|
55
|
+
],
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
}
|
|
59
|
+
</script>
|
|
60
|
+
|
|
61
|
+
<template>
|
|
62
|
+
<input
|
|
63
|
+
v-model="modelValue"
|
|
64
|
+
:type="props.type || 'text'"
|
|
65
|
+
:class="[
|
|
66
|
+
'transition-all duration-200 ease-in-out',
|
|
67
|
+
'cursor-disabled:not-allowed',
|
|
68
|
+
...variantClasses[props.variant][props.theme].default,
|
|
69
|
+
]"
|
|
70
|
+
>
|
|
71
|
+
</template>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as Radio } from './radio.vue'
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as SelectTab } from './select-tab.vue'
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
<script lang="ts" setup>
|
|
2
|
+
import { watchEffect } from 'vue'
|
|
3
|
+
|
|
4
|
+
import { TransitionVertical } from '../animations'
|
|
5
|
+
|
|
6
|
+
const props = defineProps<{
|
|
7
|
+
default?: boolean
|
|
8
|
+
label?: string
|
|
9
|
+
}>()
|
|
10
|
+
const visible = defineModel<boolean>({ default: false })
|
|
11
|
+
watchEffect(() => {
|
|
12
|
+
if (props.default != null) {
|
|
13
|
+
visible.value = !!props.default
|
|
14
|
+
}
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
function setVisible(value: boolean) {
|
|
18
|
+
visible.value = value
|
|
19
|
+
return value
|
|
20
|
+
}
|
|
21
|
+
</script>
|
|
22
|
+
|
|
23
|
+
<template>
|
|
24
|
+
<div>
|
|
25
|
+
<slot name="trigger" v-bind="{ visible, setVisible }">
|
|
26
|
+
<button
|
|
27
|
+
:class="['sticky top-0 z-10 flex items-center justify-between px2 py1 text-sm backdrop-blur-xl']"
|
|
28
|
+
@click="visible = !visible"
|
|
29
|
+
>
|
|
30
|
+
<span>
|
|
31
|
+
{{ props.label ?? 'Collapsable' }}
|
|
32
|
+
</span> <span op50>{{ visible ? '▲' : '▼' }}</span>
|
|
33
|
+
</button>
|
|
34
|
+
</slot>
|
|
35
|
+
<TransitionVertical>
|
|
36
|
+
<slot v-if="visible" v-bind="{ visible, setVisible }" />
|
|
37
|
+
</TransitionVertical>
|
|
38
|
+
</div>
|
|
39
|
+
</template>
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { breakpointsTailwind, useBreakpoints, useElementBounding, useWindowSize } from '@vueuse/core'
|
|
3
|
+
import { computed, onMounted, ref, watch } from 'vue'
|
|
4
|
+
|
|
5
|
+
const containerRef = ref<HTMLDivElement>()
|
|
6
|
+
|
|
7
|
+
const breakpoints = useBreakpoints(breakpointsTailwind)
|
|
8
|
+
const { width, height } = useWindowSize()
|
|
9
|
+
const containerElementBounding = useElementBounding(containerRef, { immediate: true, windowResize: true, reset: true })
|
|
10
|
+
|
|
11
|
+
const isMobile = computed(() => breakpoints.between('sm', 'md').value || breakpoints.smaller('sm').value)
|
|
12
|
+
const isTablet = computed(() => breakpoints.between('md', 'lg').value)
|
|
13
|
+
const isDesktop = computed(() => breakpoints.greaterOrEqual('lg').value)
|
|
14
|
+
|
|
15
|
+
const canvasWidth = computed(() => {
|
|
16
|
+
if (isDesktop.value)
|
|
17
|
+
return containerElementBounding.width.value
|
|
18
|
+
else if (isMobile.value)
|
|
19
|
+
return (width.value - 16) // padding
|
|
20
|
+
else if (isTablet.value)
|
|
21
|
+
return (width.value - 16) // padding
|
|
22
|
+
else
|
|
23
|
+
return containerElementBounding.width.value
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
const canvasHeight = ref(0)
|
|
27
|
+
|
|
28
|
+
watch([width, height, containerRef], () => {
|
|
29
|
+
const bounding = containerRef.value?.parentElement?.getBoundingClientRect()
|
|
30
|
+
|
|
31
|
+
if (isDesktop.value) {
|
|
32
|
+
canvasHeight.value = bounding?.height || 0
|
|
33
|
+
}
|
|
34
|
+
else if (isMobile.value) {
|
|
35
|
+
canvasHeight.value = bounding?.height || 0
|
|
36
|
+
}
|
|
37
|
+
else if (isTablet.value) {
|
|
38
|
+
canvasHeight.value = bounding?.height || 0
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
canvasHeight.value = 600
|
|
42
|
+
}
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
watch([containerElementBounding.width, containerElementBounding.height], () => {
|
|
46
|
+
if (isDesktop.value) {
|
|
47
|
+
canvasHeight.value = containerElementBounding.height.value
|
|
48
|
+
}
|
|
49
|
+
else if (isMobile.value) {
|
|
50
|
+
canvasHeight.value = containerElementBounding.height.value
|
|
51
|
+
}
|
|
52
|
+
else if (isTablet.value) {
|
|
53
|
+
canvasHeight.value = containerElementBounding.height.value
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
canvasHeight.value = 600
|
|
57
|
+
}
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
onMounted(async () => {
|
|
61
|
+
if (!containerRef.value)
|
|
62
|
+
return
|
|
63
|
+
|
|
64
|
+
containerElementBounding.update()
|
|
65
|
+
})
|
|
66
|
+
</script>
|
|
67
|
+
|
|
68
|
+
<template>
|
|
69
|
+
<div ref="containerRef" h-full w-full>
|
|
70
|
+
<slot :width="canvasWidth" :height="canvasHeight" />
|
|
71
|
+
</div>
|
|
72
|
+
</template>
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
const props = withDefaults(defineProps<{
|
|
3
|
+
animation?: 'pulse' | 'wave' | 'none'
|
|
4
|
+
}>(), {
|
|
5
|
+
animation: 'pulse',
|
|
6
|
+
})
|
|
7
|
+
</script>
|
|
8
|
+
|
|
9
|
+
<template>
|
|
10
|
+
<div
|
|
11
|
+
class="skeleton"
|
|
12
|
+
:class="props.animation !== 'none' ? `skeleton-${props.animation}` : ''"
|
|
13
|
+
bg="neutral-200 dark:neutral-800"
|
|
14
|
+
overflow="hidden"
|
|
15
|
+
>
|
|
16
|
+
<slot />
|
|
17
|
+
</div>
|
|
18
|
+
</template>
|
|
19
|
+
|
|
20
|
+
<style scoped>
|
|
21
|
+
.skeleton {
|
|
22
|
+
position: relative;
|
|
23
|
+
transition: all 0.2s ease-in-out;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/* Pulse animation */
|
|
27
|
+
.skeleton-pulse {
|
|
28
|
+
animation: skeleton-pulse 2s ease-in-out 0.5s infinite;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
@keyframes skeleton-pulse {
|
|
32
|
+
0% {
|
|
33
|
+
opacity: 1;
|
|
34
|
+
}
|
|
35
|
+
50% {
|
|
36
|
+
opacity: 0.5;
|
|
37
|
+
}
|
|
38
|
+
100% {
|
|
39
|
+
opacity: 1;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/* Wave animation */
|
|
44
|
+
.skeleton-wave::after {
|
|
45
|
+
content: '';
|
|
46
|
+
position: absolute;
|
|
47
|
+
top: 0;
|
|
48
|
+
right: 0;
|
|
49
|
+
bottom: 0;
|
|
50
|
+
left: 0;
|
|
51
|
+
transform: translateX(-100%);
|
|
52
|
+
background: linear-gradient(90deg, transparent, rgb(255, 255, 255), transparent);
|
|
53
|
+
animation: skeleton-wave 2s ease-in-out infinite;
|
|
54
|
+
border-radius: inherit;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.dark .skeleton-wave::after {
|
|
58
|
+
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.1), transparent);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
@keyframes skeleton-wave {
|
|
62
|
+
0% {
|
|
63
|
+
transform: translateX(-100%);
|
|
64
|
+
opacity: 0;
|
|
65
|
+
}
|
|
66
|
+
60% {
|
|
67
|
+
transform: translateX(100%);
|
|
68
|
+
opacity: 1;
|
|
69
|
+
}
|
|
70
|
+
100% {
|
|
71
|
+
transform: translateX(100%);
|
|
72
|
+
opacity: 0;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
</style>
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { computed } from 'vue'
|
|
3
|
+
|
|
4
|
+
import { TransitionBidirectional } from '../animations'
|
|
5
|
+
|
|
6
|
+
// Define button variants for better type safety and maintainability
|
|
7
|
+
type ButtonVariant = 'primary' | 'secondary' | 'secondary-muted' | 'danger' | 'caution' | 'pure' | 'ghost'
|
|
8
|
+
|
|
9
|
+
type ButtonTheme = 'default'
|
|
10
|
+
|
|
11
|
+
// Define size options for better flexibility
|
|
12
|
+
type ButtonSize = 'sm' | 'md' | 'lg'
|
|
13
|
+
|
|
14
|
+
interface ButtonProps {
|
|
15
|
+
toggled?: boolean // Optional toggled state for toggle buttons
|
|
16
|
+
icon?: string // Icon class name
|
|
17
|
+
label?: string // Button text label
|
|
18
|
+
disabled?: boolean // Disabled state
|
|
19
|
+
loading?: boolean // Loading state
|
|
20
|
+
variant?: ButtonVariant // Button style variant
|
|
21
|
+
size?: ButtonSize // Button size variant
|
|
22
|
+
theme?: ButtonTheme // Button theme
|
|
23
|
+
block?: boolean // Full width button
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const props = withDefaults(defineProps<ButtonProps>(), {
|
|
27
|
+
toggled: false,
|
|
28
|
+
variant: 'primary',
|
|
29
|
+
disabled: false,
|
|
30
|
+
loading: false,
|
|
31
|
+
size: 'md',
|
|
32
|
+
theme: 'default',
|
|
33
|
+
block: false,
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
const isDisabled = computed(() => props.disabled || props.loading)
|
|
37
|
+
|
|
38
|
+
// Extract variant styles for better organization
|
|
39
|
+
const variantClasses: Record<ButtonVariant, Record<ButtonTheme, {
|
|
40
|
+
default: string[]
|
|
41
|
+
nonToggled?: string
|
|
42
|
+
toggled?: string
|
|
43
|
+
}>> = {
|
|
44
|
+
'primary': {
|
|
45
|
+
default: {
|
|
46
|
+
default: [
|
|
47
|
+
'rounded-lg',
|
|
48
|
+
'backdrop-blur-md',
|
|
49
|
+
'bg-primary-500/15 hover:bg-primary-500/20 active:bg-primary-500/30 dark:bg-primary-700/30 dark:hover:bg-primary-700/40 dark:active:bg-primary-700/30',
|
|
50
|
+
'focus:ring-primary-300/60 dark:focus:ring-primary-600/30',
|
|
51
|
+
'border-2 border-solid border-primary-500/5 dark:border-primary-900/40',
|
|
52
|
+
'text-primary-950 dark:text-primary-100',
|
|
53
|
+
'focus:ring-2',
|
|
54
|
+
],
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
'secondary': {
|
|
58
|
+
default: {
|
|
59
|
+
default: [
|
|
60
|
+
'rounded-lg',
|
|
61
|
+
'backdrop-blur-md',
|
|
62
|
+
'bg-neutral-100/55 hover:bg-neutral-400/20 active:bg-neutral-400/30 dark:bg-neutral-700/60 dark:hover:bg-neutral-700/80 dark:active:bg-neutral-700/60',
|
|
63
|
+
'focus:ring-neutral-300/30 dark:focus:ring-neutral-600/60 dark:focus:ring-neutral-600/30',
|
|
64
|
+
'border-2 border-solid border-neutral-300/30 dark:border-neutral-700/30',
|
|
65
|
+
'text-neutral-950 dark:text-neutral-100',
|
|
66
|
+
'focus:ring-2',
|
|
67
|
+
],
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
'secondary-muted': {
|
|
71
|
+
default: {
|
|
72
|
+
default: [
|
|
73
|
+
'rounded-lg',
|
|
74
|
+
'backdrop-blur-md',
|
|
75
|
+
'hover:bg-neutral-50/50 active:bg-neutral-50/90 hover:dark:bg-neutral-800/50 active:dark:bg-neutral-800/90',
|
|
76
|
+
'border-2 border-solid border-neutral-100/60 dark:border-neutral-800/30',
|
|
77
|
+
'focus:ring-2 focus:ring-neutral-300/30 dark:focus:ring-neutral-600/60 dark:focus:ring-neutral-600/30',
|
|
78
|
+
],
|
|
79
|
+
nonToggled: 'bg-neutral-50/70 dark:bg-neutral-800/70 text-neutral-500 dark:text-neutral-400',
|
|
80
|
+
toggled: 'bg-white/90 dark:bg-neutral-500/70 ring-neutral-300/30 dark:ring-neutral-600/60 ring-2 dark:ring-neutral-600/30 text-primary-500 dark:text-primary-100',
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
'danger': {
|
|
84
|
+
default: {
|
|
85
|
+
default: [
|
|
86
|
+
'rounded-lg',
|
|
87
|
+
'backdrop-blur-md',
|
|
88
|
+
'bg-red-500/15 hover:bg-red-500/20 active:bg-red-500/30 dark:bg-red-700/30 dark:hover:bg-red-700/40 dark:active:bg-red-700/30',
|
|
89
|
+
'focus:ring-2 focus:ring-red-300/30 dark:focus:ring-red-600/60 dark:focus:ring-red-600/30',
|
|
90
|
+
'border-2 border-solid border-red-200/30 dark:border-red-900/30',
|
|
91
|
+
'text-red-950 dark:text-red-100',
|
|
92
|
+
],
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
'caution': {
|
|
96
|
+
default: {
|
|
97
|
+
default: [
|
|
98
|
+
'rounded-lg',
|
|
99
|
+
'backdrop-blur-md',
|
|
100
|
+
'bg-amber-400/20 hover:bg-amber-400/25 active:bg-amber-400/35 dark:bg-amber-500/20 dark:hover:bg-amber-500/30 dark:active:bg-amber-500/35',
|
|
101
|
+
'focus:ring-2 focus:ring-amber-300/40 dark:focus:ring-amber-400/40',
|
|
102
|
+
'border-2 border-solid border-amber-300/40 dark:border-amber-500/40',
|
|
103
|
+
'text-amber-900 dark:text-amber-50',
|
|
104
|
+
],
|
|
105
|
+
},
|
|
106
|
+
},
|
|
107
|
+
'pure': {
|
|
108
|
+
default: {
|
|
109
|
+
default: [
|
|
110
|
+
'bg-transparent',
|
|
111
|
+
'text-neutral-900 dark:text-neutral-50',
|
|
112
|
+
'!px-0 !py-0',
|
|
113
|
+
],
|
|
114
|
+
},
|
|
115
|
+
},
|
|
116
|
+
'ghost': {
|
|
117
|
+
default: {
|
|
118
|
+
default: [
|
|
119
|
+
'bg-transparent',
|
|
120
|
+
'hover:bg-neutral-100/50 dark:hover:bg-neutral-800/50',
|
|
121
|
+
'text-neutral-500 dark:text-neutral-400',
|
|
122
|
+
'focus:ring-2 focus:ring-neutral-300/30 dark:focus:ring-neutral-600/30',
|
|
123
|
+
],
|
|
124
|
+
},
|
|
125
|
+
},
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Extract size styles for better organization
|
|
129
|
+
const sizeClasses: Record<ButtonSize, string> = {
|
|
130
|
+
sm: 'px-3 py-1.5 text-xs',
|
|
131
|
+
md: 'px-4 py-2 text-sm',
|
|
132
|
+
lg: 'px-6 py-3 text-base',
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Base classes that are always applied
|
|
136
|
+
const baseClasses = computed(() => {
|
|
137
|
+
const variant = variantClasses[props.variant] || variantClasses.primary
|
|
138
|
+
const theme = variant[props.theme] || variant.default
|
|
139
|
+
|
|
140
|
+
return [
|
|
141
|
+
'rounded-lg font-medium outline-none',
|
|
142
|
+
'transition-all duration-200 ease-in-out',
|
|
143
|
+
'disabled:cursor-not-allowed disabled:opacity-50',
|
|
144
|
+
'backdrop-blur-md',
|
|
145
|
+
props.block ? 'w-full' : '',
|
|
146
|
+
sizeClasses[props.size],
|
|
147
|
+
theme.default,
|
|
148
|
+
props.toggled ? theme.toggled || '' : theme.nonToggled || '',
|
|
149
|
+
{ 'opacity-50 cursor-not-allowed': isDisabled.value },
|
|
150
|
+
'focus:ring-2',
|
|
151
|
+
]
|
|
152
|
+
})
|
|
153
|
+
</script>
|
|
154
|
+
|
|
155
|
+
<template>
|
|
156
|
+
<button
|
|
157
|
+
:disabled="isDisabled"
|
|
158
|
+
:class="baseClasses"
|
|
159
|
+
>
|
|
160
|
+
<div class="flex flex-row items-center justify-center gap-2">
|
|
161
|
+
<TransitionBidirectional
|
|
162
|
+
from-class="opacity-0 mr-0! w-0!"
|
|
163
|
+
active-class="transition-[width,margin] ease-in-out overflow-hidden transition-100"
|
|
164
|
+
>
|
|
165
|
+
<div v-if="loading || icon" class="w-4">
|
|
166
|
+
<div v-if="loading" class="i-svg-spinners:ring-resize h-4 w-4" />
|
|
167
|
+
<div v-else-if="icon" class="h-4 w-4" :class="icon" />
|
|
168
|
+
</div>
|
|
169
|
+
</TransitionBidirectional>
|
|
170
|
+
<span v-if="label">{{ label }}</span>
|
|
171
|
+
<slot v-else />
|
|
172
|
+
</div>
|
|
173
|
+
</button>
|
|
174
|
+
</template>
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
type ThemeVariant = 'primary' | 'violet' | 'lime' | 'orange'
|
|
3
|
+
|
|
4
|
+
const props = withDefaults(defineProps<{
|
|
5
|
+
theme?: ThemeVariant
|
|
6
|
+
label?: string
|
|
7
|
+
}>(), {
|
|
8
|
+
theme: 'primary',
|
|
9
|
+
})
|
|
10
|
+
|
|
11
|
+
const themeClasses: Record<ThemeVariant, {
|
|
12
|
+
container: string[]
|
|
13
|
+
label?: string[]
|
|
14
|
+
}> = {
|
|
15
|
+
primary: {
|
|
16
|
+
container: [
|
|
17
|
+
'text-neutral-900/80 dark:text-neutral-100/80',
|
|
18
|
+
'bg-primary-50/80 dark:bg-primary-900/50 backdrop-blur-md',
|
|
19
|
+
`before:bg-primary-500/30 before:content-[''] before:dark:bg-primary-200/20`,
|
|
20
|
+
],
|
|
21
|
+
label: [
|
|
22
|
+
'text-primary-500 dark:text-primary-200 font-semibold',
|
|
23
|
+
],
|
|
24
|
+
},
|
|
25
|
+
lime: {
|
|
26
|
+
container: [
|
|
27
|
+
'text-neutral-900/80 dark:text-neutral-100/80',
|
|
28
|
+
'bg-lime-50/80 dark:bg-lime-900/50 backdrop-blur-md',
|
|
29
|
+
`before:bg-lime-500/30 before:content-[''] before:dark:bg-lime-200/20`,
|
|
30
|
+
],
|
|
31
|
+
label: [
|
|
32
|
+
'text-lime-500 dark:text-lime-200 font-semibold',
|
|
33
|
+
],
|
|
34
|
+
},
|
|
35
|
+
violet: {
|
|
36
|
+
container: [
|
|
37
|
+
'text-neutral-900/80 dark:text-neutral-100/80',
|
|
38
|
+
'bg-violet-50/80 dark:bg-violet-900/50 backdrop-blur-md',
|
|
39
|
+
`before:bg-violet-500/30 before:content-[''] before:dark:bg-violet-200/20`,
|
|
40
|
+
],
|
|
41
|
+
label: [
|
|
42
|
+
'text-violet-500 dark:text-violet-200 font-semibold',
|
|
43
|
+
],
|
|
44
|
+
},
|
|
45
|
+
orange: {
|
|
46
|
+
container: [
|
|
47
|
+
'text-neutral-900/80 dark:text-neutral-100/80',
|
|
48
|
+
'bg-orange-100/60 dark:bg-orange-900/50 backdrop-blur-md',
|
|
49
|
+
`before:bg-orange-500/30 before:content-[''] before:dark:bg-orange-200/20`,
|
|
50
|
+
],
|
|
51
|
+
label: [
|
|
52
|
+
'text-orange-500 dark:text-orange-200 font-semibold',
|
|
53
|
+
],
|
|
54
|
+
},
|
|
55
|
+
}
|
|
56
|
+
</script>
|
|
57
|
+
|
|
58
|
+
<template>
|
|
59
|
+
<div
|
|
60
|
+
relative
|
|
61
|
+
flex flex-col gap-1
|
|
62
|
+
rounded-lg
|
|
63
|
+
py="2.5" pl="5" pr-3
|
|
64
|
+
:class="[
|
|
65
|
+
...themeClasses[props.theme || 'violet'].container,
|
|
66
|
+
// eslint-disable-next-line vue/prefer-separate-static-class
|
|
67
|
+
'before-position-absolute before:left-2 before:right-0 before:h-[calc(100%-1rem)] before:top-50% before:translate-y--50% before:w-1 before:rounded-full',
|
|
68
|
+
]"
|
|
69
|
+
>
|
|
70
|
+
<div text="font-semibold" :class="[...(themeClasses[props.theme || 'violet'].label || [])]">
|
|
71
|
+
<slot name="label">
|
|
72
|
+
{{ props.label || 'Callout' }}
|
|
73
|
+
</slot>
|
|
74
|
+
</div>
|
|
75
|
+
<slot />
|
|
76
|
+
</div>
|
|
77
|
+
</template>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { computed, ref, watch } from 'vue'
|
|
3
3
|
|
|
4
|
-
import Button from './
|
|
4
|
+
import Button from './button.vue'
|
|
5
5
|
|
|
6
6
|
type ButtonVariant = 'primary' | 'secondary' | 'secondary-muted' | 'danger' | 'caution'
|
|
7
7
|
type ButtonSize = 'sm' | 'md' | 'lg'
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
defineProps<{
|
|
3
|
+
progress: number
|
|
4
|
+
barClass?: string
|
|
5
|
+
}>()
|
|
6
|
+
</script>
|
|
7
|
+
|
|
8
|
+
<template>
|
|
9
|
+
<div relative overflow-hidden rounded-md>
|
|
10
|
+
<div
|
|
11
|
+
:class="[barClass ? barClass : 'bg-primary-300 dark:bg-primary-300/50']"
|
|
12
|
+
absolute h-4 min-w-2 rounded-md will-change-width
|
|
13
|
+
:style="{ width: `${progress}%` }"
|
|
14
|
+
transition="width duration-500 ease-in-out"
|
|
15
|
+
>
|
|
16
|
+
<div
|
|
17
|
+
v-if="progress < 100"
|
|
18
|
+
absolute inset-0 origin-left rounded-md bg-white
|
|
19
|
+
class="progress-shine-animation"
|
|
20
|
+
/>
|
|
21
|
+
</div>
|
|
22
|
+
<div
|
|
23
|
+
bg="neutral-100 dark:neutral-900" h-4 w-full rounded-md
|
|
24
|
+
/>
|
|
25
|
+
</div>
|
|
26
|
+
</template>
|
|
27
|
+
|
|
28
|
+
<style scoped>
|
|
29
|
+
.progress-shine-animation {
|
|
30
|
+
animation: progress-shine 2s cubic-bezier(0.35, 0.08, 0.04, 0.99) infinite;
|
|
31
|
+
will-change: transform, opacity;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
@keyframes progress-shine {
|
|
35
|
+
0% {
|
|
36
|
+
opacity: 0.4;
|
|
37
|
+
transform: scale(0, 1);
|
|
38
|
+
}
|
|
39
|
+
100% {
|
|
40
|
+
opacity: 0;
|
|
41
|
+
transform: scale(1, 1);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
</style>
|
package/src/fallback.css
CHANGED
package/src/index.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import './fallback.css'
|
|
2
2
|
|
|
3
|
-
export * from './components/
|
|
4
|
-
export * from './components/
|
|
5
|
-
export * from './components/
|
|
3
|
+
export * from './components/animations'
|
|
4
|
+
export * from './components/form'
|
|
5
|
+
export * from './components/layouts'
|
|
6
|
+
export * from './components/misc'
|
|
6
7
|
export * from './composables/use-deferred-mount'
|
|
7
8
|
export * from './composables/use-theme'
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { default as Checkbox } from './Checkbox.vue'
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { default as Combobox } from './Combobox.vue'
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
export { default as FieldCheckbox } from './FieldCheckbox.vue'
|
|
2
|
-
export { default as FieldInput } from './FieldInput.vue'
|
|
3
|
-
export { default as FieldKeyValues } from './FieldKeyValues.vue'
|
|
4
|
-
export { default as FieldRange } from './FieldRange.vue'
|
|
5
|
-
export { default as FieldSelect } from './FieldSelect.vue'
|
|
6
|
-
export { default as FieldTextArea } from './FieldTextArea.vue'
|
|
7
|
-
export { default as FieldValues } from './FieldValues.vue'
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
const props = defineProps<{
|
|
3
|
-
type?: string
|
|
4
|
-
}>()
|
|
5
|
-
|
|
6
|
-
const modelValue = defineModel<string>({ required: false })
|
|
7
|
-
</script>
|
|
8
|
-
|
|
9
|
-
<template>
|
|
10
|
-
<input
|
|
11
|
-
v-model="modelValue"
|
|
12
|
-
:type="props.type || 'text'"
|
|
13
|
-
:class="[
|
|
14
|
-
'focus:border-primary-300 dark:focus:border-primary-400/50 border-2 border-solid border-neutral-100 dark:border-neutral-900',
|
|
15
|
-
'transition-all duration-200 ease-in-out',
|
|
16
|
-
'text-disabled:neutral-400 dark:text-disabled:neutral-600',
|
|
17
|
-
'cursor-disabled:not-allowed',
|
|
18
|
-
'w-full rounded-lg px-2 py-1 text-nowrap text-sm outline-none',
|
|
19
|
-
'shadow-sm',
|
|
20
|
-
'bg-neutral-50 dark:bg-neutral-950 focus:bg-neutral-50 dark:focus:bg-neutral-900',
|
|
21
|
-
]"
|
|
22
|
-
>
|
|
23
|
-
</template>
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { default as Radio } from './Radio.vue'
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { default as SelectTab } from './SelectTab.vue'
|
|
@@ -1,118 +0,0 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
import { BidirectionalTransition } from '@proj-airi/ui'
|
|
3
|
-
import { computed } from 'vue'
|
|
4
|
-
|
|
5
|
-
// Define button variants for better type safety and maintainability
|
|
6
|
-
type ButtonVariant = 'primary' | 'secondary' | 'secondary-muted' | 'danger' | 'caution' | 'pure'
|
|
7
|
-
|
|
8
|
-
type ButtonTheme = 'default'
|
|
9
|
-
|
|
10
|
-
// Define size options for better flexibility
|
|
11
|
-
type ButtonSize = 'sm' | 'md' | 'lg'
|
|
12
|
-
|
|
13
|
-
interface ButtonProps {
|
|
14
|
-
toggled?: boolean // Optional toggled state for toggle buttons
|
|
15
|
-
icon?: string // Icon class name
|
|
16
|
-
label?: string // Button text label
|
|
17
|
-
disabled?: boolean // Disabled state
|
|
18
|
-
loading?: boolean // Loading state
|
|
19
|
-
variant?: ButtonVariant // Button style variant
|
|
20
|
-
size?: ButtonSize // Button size variant
|
|
21
|
-
theme?: ButtonTheme // Button theme
|
|
22
|
-
block?: boolean // Full width button
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const props = withDefaults(defineProps<ButtonProps>(), {
|
|
26
|
-
toggled: false,
|
|
27
|
-
variant: 'primary',
|
|
28
|
-
disabled: false,
|
|
29
|
-
loading: false,
|
|
30
|
-
size: 'md',
|
|
31
|
-
theme: 'default',
|
|
32
|
-
block: false,
|
|
33
|
-
})
|
|
34
|
-
|
|
35
|
-
const isDisabled = computed(() => props.disabled || props.loading)
|
|
36
|
-
|
|
37
|
-
// Extract variant styles for better organization
|
|
38
|
-
const variantClasses: Record<ButtonVariant, Record<ButtonTheme, {
|
|
39
|
-
default: string
|
|
40
|
-
nonToggled?: string
|
|
41
|
-
toggled?: string
|
|
42
|
-
}>> = {
|
|
43
|
-
'primary': {
|
|
44
|
-
default: {
|
|
45
|
-
default: 'bg-primary-500/15 hover:bg-primary-500/20 active:bg-primary-500/30 dark:bg-primary-700/30 dark:hover:bg-primary-700/40 dark:active:bg-primary-700/30 focus:ring-primary-300/60 dark:focus:ring-primary-600/30 border-2 border-solid border-primary-500/5 dark:border-primary-900/40 text-primary-950 dark:text-primary-100',
|
|
46
|
-
},
|
|
47
|
-
},
|
|
48
|
-
'secondary': {
|
|
49
|
-
default: {
|
|
50
|
-
default: 'bg-neutral-100/55 hover:bg-neutral-400/20 active:bg-neutral-400/30 dark:bg-neutral-700/60 dark:hover:bg-neutral-700/80 dark:active:bg-neutral-700/60 focus:ring-neutral-300/30 dark:focus:ring-neutral-600/60 dark:focus:ring-neutral-600/30 border-2 border-solid border-neutral-300/30 dark:border-neutral-700/30 text-neutral-950 dark:text-neutral-100',
|
|
51
|
-
},
|
|
52
|
-
},
|
|
53
|
-
'secondary-muted': {
|
|
54
|
-
default: {
|
|
55
|
-
default: 'hover:bg-neutral-50/50 active:bg-neutral-50/90 hover:dark:bg-neutral-800/50 active:dark:bg-neutral-800/90 border-2 border-solid border-neutral-100/60 dark:border-neutral-800/30 focus:ring-neutral-300/30 dark:focus:ring-neutral-600/60 dark:focus:ring-neutral-600/30',
|
|
56
|
-
nonToggled: 'bg-neutral-50/70 dark:bg-neutral-800/70 text-neutral-500 dark:text-neutral-400',
|
|
57
|
-
toggled: 'bg-white/90 dark:bg-neutral-500/70 ring-neutral-300/30 dark:ring-neutral-600/60 ring-2 dark:ring-neutral-600/30 text-primary-500 dark:text-primary-100',
|
|
58
|
-
},
|
|
59
|
-
},
|
|
60
|
-
'danger': {
|
|
61
|
-
default: {
|
|
62
|
-
default: 'bg-red-500/15 hover:bg-red-500/20 active:bg-red-500/30 dark:bg-red-700/30 dark:hover:bg-red-700/40 dark:active:bg-red-700/30 focus:ring-red-300/30 dark:focus:ring-red-600/60 dark:focus:ring-red-600/30 border-2 border-solid border-red-200/30 dark:border-red-900/30 text-red-950 dark:text-red-100',
|
|
63
|
-
},
|
|
64
|
-
},
|
|
65
|
-
'caution': {
|
|
66
|
-
default: {
|
|
67
|
-
default: 'bg-amber-400/20 hover:bg-amber-400/25 active:bg-amber-400/35 dark:bg-amber-500/20 dark:hover:bg-amber-500/30 dark:active:bg-amber-500/35 focus:ring-amber-300/40 dark:focus:ring-amber-400/40 border-2 border-solid border-amber-300/40 dark:border-amber-500/40 text-amber-900 dark:text-amber-50',
|
|
68
|
-
},
|
|
69
|
-
},
|
|
70
|
-
'pure': {
|
|
71
|
-
default: {
|
|
72
|
-
default: 'bg-white hover:bg-neutral-50 active:bg-neutral-100 dark:bg-neutral-900 dark:hover:bg-neutral-800 dark:active:bg-neutral-700 border-2 border-solid border-neutral-100 dark:border-neutral-800 focus:ring-neutral-200/40 dark:focus:ring-neutral-600/40 text-neutral-900 dark:text-neutral-50',
|
|
73
|
-
},
|
|
74
|
-
},
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// Extract size styles for better organization
|
|
78
|
-
const sizeClasses: Record<ButtonSize, string> = {
|
|
79
|
-
sm: 'px-3 py-1.5 text-xs',
|
|
80
|
-
md: 'px-4 py-2 text-sm',
|
|
81
|
-
lg: 'px-6 py-3 text-base',
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
// Base classes that are always applied
|
|
85
|
-
const baseClasses = computed(() => [
|
|
86
|
-
'rounded-lg font-medium outline-none',
|
|
87
|
-
'transition-all duration-200 ease-in-out',
|
|
88
|
-
'disabled:cursor-not-allowed disabled:opacity-50',
|
|
89
|
-
'backdrop-blur-md',
|
|
90
|
-
props.block ? 'w-full' : '',
|
|
91
|
-
sizeClasses[props.size],
|
|
92
|
-
variantClasses[props.variant][props.theme].default,
|
|
93
|
-
props.toggled ? variantClasses[props.variant][props.theme].toggled || '' : variantClasses[props.variant][props.theme].nonToggled || '',
|
|
94
|
-
{ 'opacity-50 cursor-not-allowed': isDisabled.value },
|
|
95
|
-
'focus:ring-2',
|
|
96
|
-
])
|
|
97
|
-
</script>
|
|
98
|
-
|
|
99
|
-
<template>
|
|
100
|
-
<button
|
|
101
|
-
:disabled="isDisabled"
|
|
102
|
-
:class="baseClasses"
|
|
103
|
-
>
|
|
104
|
-
<div class="flex flex-row items-center justify-center gap-2">
|
|
105
|
-
<BidirectionalTransition
|
|
106
|
-
from-class="opacity-0 mr-0! w-0!"
|
|
107
|
-
active-class="transition-[width,margin] ease-in-out overflow-hidden transition-100"
|
|
108
|
-
>
|
|
109
|
-
<div v-if="loading || icon" class="w-4">
|
|
110
|
-
<div v-if="loading" class="i-svg-spinners:ring-resize h-4 w-4" />
|
|
111
|
-
<div v-else-if="icon" class="h-4 w-4" :class="icon" />
|
|
112
|
-
</div>
|
|
113
|
-
</BidirectionalTransition>
|
|
114
|
-
<span v-if="label">{{ label }}</span>
|
|
115
|
-
<slot v-else />
|
|
116
|
-
</div>
|
|
117
|
-
</button>
|
|
118
|
-
</template>
|
|
File without changes
|
/package/src/components/{Animations/TransitionHorizontal.vue → animations/transition-horizontal.vue}
RENAMED
|
File without changes
|
/package/src/components/{Animations/TransitionVertical.vue → animations/transition-vertical.vue}
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|