@una-ui/nuxt 0.1.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +22 -0
- package/dist/module.cjs +5 -0
- package/dist/module.d.ts +31 -0
- package/dist/module.json +8 -0
- package/dist/module.mjs +93 -0
- package/dist/runtime/components/elements/Accordion.vue +201 -0
- package/dist/runtime/components/elements/Alert.vue +138 -0
- package/dist/runtime/components/elements/Avatar.vue +80 -0
- package/dist/runtime/components/elements/AvatarGroup.vue +27 -0
- package/dist/runtime/components/elements/Badge.vue +50 -0
- package/dist/runtime/components/elements/Button.vue +94 -0
- package/dist/runtime/components/elements/Icon.vue +9 -0
- package/dist/runtime/components/elements/Indicator.vue +60 -0
- package/dist/runtime/components/forms/FormGroup.vue +141 -0
- package/dist/runtime/components/forms/Input.vue +151 -0
- package/dist/runtime/components/forms/Switch.vue +117 -0
- package/dist/runtime/components/misc/ThemeSwitcher.vue +111 -0
- package/dist/runtime/components/slots/AvatarGroupDefault.d.ts +22 -0
- package/dist/runtime/components/slots/AvatarGroupDefault.mjs +44 -0
- package/dist/runtime/components/slots/FormGroupDefault.d.ts +25 -0
- package/dist/runtime/components/slots/FormGroupDefault.mjs +23 -0
- package/dist/runtime/composables/themes.d.ts +7 -0
- package/dist/runtime/composables/themes.mjs +119 -0
- package/dist/runtime/plugins/theme.client.d.ts +5 -0
- package/dist/runtime/plugins/theme.client.mjs +28 -0
- package/dist/runtime/plugins/theme.server.d.ts +2 -0
- package/dist/runtime/plugins/theme.server.mjs +24 -0
- package/dist/runtime/types/accordion.d.ts +112 -0
- package/dist/runtime/types/accordion.mjs +0 -0
- package/dist/runtime/types/alert.d.ts +55 -0
- package/dist/runtime/types/alert.mjs +0 -0
- package/dist/runtime/types/avatar-group.d.ts +26 -0
- package/dist/runtime/types/avatar-group.mjs +0 -0
- package/dist/runtime/types/avatar.d.ts +71 -0
- package/dist/runtime/types/avatar.mjs +0 -0
- package/dist/runtime/types/badge.d.ts +41 -0
- package/dist/runtime/types/badge.mjs +0 -0
- package/dist/runtime/types/button.d.ts +95 -0
- package/dist/runtime/types/button.mjs +0 -0
- package/dist/runtime/types/form-group.d.ts +83 -0
- package/dist/runtime/types/form-group.mjs +0 -0
- package/dist/runtime/types/icon.d.ts +9 -0
- package/dist/runtime/types/icon.mjs +0 -0
- package/dist/runtime/types/index.d.ts +11 -0
- package/dist/runtime/types/index.mjs +11 -0
- package/dist/runtime/types/indicator.d.ts +45 -0
- package/dist/runtime/types/indicator.mjs +0 -0
- package/dist/runtime/types/input.d.ts +91 -0
- package/dist/runtime/types/input.mjs +0 -0
- package/dist/runtime/types/switch.d.ts +69 -0
- package/dist/runtime/types/switch.mjs +0 -0
- package/dist/runtime/utils/index.d.ts +19 -0
- package/dist/runtime/utils/index.mjs +36 -0
- package/dist/types.d.ts +15 -0
- package/package.json +58 -0
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { computed } from 'vue'
|
|
3
|
+
import { createReusableTemplate } from '@vueuse/core'
|
|
4
|
+
import NIcon from '../elements/Icon.vue'
|
|
5
|
+
import type { NButtonProps } from '../../types'
|
|
6
|
+
|
|
7
|
+
// @ts-expect-error tsconfig
|
|
8
|
+
import { NuxtLink } from '#components'
|
|
9
|
+
|
|
10
|
+
defineOptions({
|
|
11
|
+
inheritAttrs: false,
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
const props = withDefaults(defineProps<NButtonProps>(), {
|
|
15
|
+
type: 'button',
|
|
16
|
+
loadingPlacement: 'leading',
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
const btnVariants = ['solid', 'outline', 'soft', 'ghost', 'link', 'text'] as const
|
|
20
|
+
const hasVariant = computed(() => btnVariants.some(btnVariants => props.btn?.includes(btnVariants)))
|
|
21
|
+
const isBaseVariant = computed(() => props.btn?.includes('~'))
|
|
22
|
+
|
|
23
|
+
const [DefineTemplate, ReuseTemplate] = createReusableTemplate()
|
|
24
|
+
</script>
|
|
25
|
+
|
|
26
|
+
<template>
|
|
27
|
+
<Component
|
|
28
|
+
:is="to ? NuxtLink : 'button'"
|
|
29
|
+
:to="to"
|
|
30
|
+
:type="to ? null : type"
|
|
31
|
+
class="btn"
|
|
32
|
+
:class="[
|
|
33
|
+
{ 'btn-default-variant': !hasVariant && !isBaseVariant },
|
|
34
|
+
{ 'btn-reverse': reverse },
|
|
35
|
+
una?.btn,
|
|
36
|
+
]"
|
|
37
|
+
:disabled="to ? null : disabled || loading"
|
|
38
|
+
:btn="btn"
|
|
39
|
+
v-bind="$attrs"
|
|
40
|
+
>
|
|
41
|
+
<DefineTemplate v-if="loading">
|
|
42
|
+
<slot name="loading">
|
|
43
|
+
<NIcon
|
|
44
|
+
:name="una?.btnLoadingIcon ?? 'btn-loading-icon'"
|
|
45
|
+
:class="una?.btnLoading"
|
|
46
|
+
btn="loading"
|
|
47
|
+
/>
|
|
48
|
+
</slot>
|
|
49
|
+
</DefineTemplate>
|
|
50
|
+
|
|
51
|
+
<ReuseTemplate v-if="loading && loadingPlacement === 'leading'" />
|
|
52
|
+
<slot
|
|
53
|
+
v-else
|
|
54
|
+
name="leading"
|
|
55
|
+
>
|
|
56
|
+
<NIcon
|
|
57
|
+
v-if="leading"
|
|
58
|
+
:name="leading"
|
|
59
|
+
:class="una?.btnLeading"
|
|
60
|
+
btn="leading"
|
|
61
|
+
/>
|
|
62
|
+
</slot>
|
|
63
|
+
|
|
64
|
+
<ReuseTemplate v-if="loading && loadingPlacement === 'label'" />
|
|
65
|
+
<slot v-else>
|
|
66
|
+
<NIcon
|
|
67
|
+
v-if="label && icon"
|
|
68
|
+
:name="label"
|
|
69
|
+
btn="icon-label"
|
|
70
|
+
:class="una?.btnIconLabel"
|
|
71
|
+
/>
|
|
72
|
+
<span
|
|
73
|
+
v-if="!icon"
|
|
74
|
+
btn="label"
|
|
75
|
+
:class="una?.btnLabel"
|
|
76
|
+
>
|
|
77
|
+
{{ label }}
|
|
78
|
+
</span>
|
|
79
|
+
</slot>
|
|
80
|
+
|
|
81
|
+
<ReuseTemplate v-if="loading && loadingPlacement === 'trailing'" />
|
|
82
|
+
<slot
|
|
83
|
+
v-else
|
|
84
|
+
name="trailing"
|
|
85
|
+
>
|
|
86
|
+
<NIcon
|
|
87
|
+
v-if="trailing"
|
|
88
|
+
:name="trailing"
|
|
89
|
+
:class="una?.btnLeading"
|
|
90
|
+
btn="trailing"
|
|
91
|
+
/>
|
|
92
|
+
</slot>
|
|
93
|
+
</Component>
|
|
94
|
+
</template>
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { computed } from 'vue'
|
|
3
|
+
import { createReusableTemplate } from '@vueuse/core'
|
|
4
|
+
import type { NIndicatorProps } from '../../types'
|
|
5
|
+
|
|
6
|
+
defineOptions({
|
|
7
|
+
inheritAttrs: false,
|
|
8
|
+
})
|
|
9
|
+
|
|
10
|
+
const props = withDefaults(defineProps<NIndicatorProps>(), {
|
|
11
|
+
visible: true,
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
const indicatorPlacements = ['top-left', 'top-right', 'bottom-left', 'bottom-right'] as const
|
|
15
|
+
const hasPlacement = computed(() => indicatorPlacements.some(indicatorPlacements => props.indicator?.includes(indicatorPlacements)))
|
|
16
|
+
|
|
17
|
+
const indicatorVariants = ['solid'] as const
|
|
18
|
+
const hasVariant = computed(() => indicatorVariants.some(indicatorVariants => props.indicator?.includes(indicatorVariants)))
|
|
19
|
+
|
|
20
|
+
const isBaseVariant = computed(() => props.indicator?.includes('~'))
|
|
21
|
+
|
|
22
|
+
const [DefineTemplate, ReuseTemplate] = createReusableTemplate<{
|
|
23
|
+
ping?: boolean
|
|
24
|
+
}>()
|
|
25
|
+
</script>
|
|
26
|
+
|
|
27
|
+
<template>
|
|
28
|
+
<div
|
|
29
|
+
indicator="wrapper"
|
|
30
|
+
:class="una?.indicatorWrapper"
|
|
31
|
+
>
|
|
32
|
+
<slot />
|
|
33
|
+
|
|
34
|
+
<span v-if="visible" :size="size">
|
|
35
|
+
<DefineTemplate v-slot="{ ping }">
|
|
36
|
+
<slot name="indicator">
|
|
37
|
+
<span
|
|
38
|
+
v-bind="$attrs"
|
|
39
|
+
:indicator="indicator"
|
|
40
|
+
class="indicator whitespace-nowrap"
|
|
41
|
+
:class="[
|
|
42
|
+
{ 'indicator-default-placement': !hasPlacement },
|
|
43
|
+
{ 'indicator-default-variant': !hasVariant && !isBaseVariant },
|
|
44
|
+
{ 'animate-ping ring-none': ping },
|
|
45
|
+
!label ? 'indicator-dot' : 'indicator-label',
|
|
46
|
+
una?.indicator,
|
|
47
|
+
]"
|
|
48
|
+
>
|
|
49
|
+
<slot name="label">
|
|
50
|
+
{{ label }}
|
|
51
|
+
</slot>
|
|
52
|
+
</span>
|
|
53
|
+
</slot>
|
|
54
|
+
</DefineTemplate>
|
|
55
|
+
|
|
56
|
+
<ReuseTemplate :ping="ping" />
|
|
57
|
+
<ReuseTemplate />
|
|
58
|
+
</span>
|
|
59
|
+
</div>
|
|
60
|
+
</template>
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { computed } from 'vue'
|
|
3
|
+
import type { NFormGroupProps } from '../../types'
|
|
4
|
+
import NFormGroupDefaultSlot from '../slots/FormGroupDefault'
|
|
5
|
+
import { randomId } from '../../utils'
|
|
6
|
+
|
|
7
|
+
const props = defineProps<NFormGroupProps>()
|
|
8
|
+
|
|
9
|
+
const id = randomId('form-group')
|
|
10
|
+
|
|
11
|
+
const statusClassVariants = computed(() => {
|
|
12
|
+
const text = {
|
|
13
|
+
info: 'text-info',
|
|
14
|
+
success: 'text-success',
|
|
15
|
+
warning: 'text-warning',
|
|
16
|
+
error: 'text-error',
|
|
17
|
+
default: 'text-$c-gray-500',
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return text[props.status ?? 'default']
|
|
21
|
+
})
|
|
22
|
+
</script>
|
|
23
|
+
|
|
24
|
+
<template>
|
|
25
|
+
<div
|
|
26
|
+
form-group
|
|
27
|
+
>
|
|
28
|
+
<slot name="top">
|
|
29
|
+
<div
|
|
30
|
+
form-group="message-wrapper"
|
|
31
|
+
:class="una?.formGroupMessageWrapper"
|
|
32
|
+
>
|
|
33
|
+
<div
|
|
34
|
+
v-if="label || hint || description"
|
|
35
|
+
form-group="top-wrapper"
|
|
36
|
+
:class="una?.formGroupTopWrapper"
|
|
37
|
+
>
|
|
38
|
+
<div
|
|
39
|
+
v-if="label || hint"
|
|
40
|
+
form-group="top-wrapper-inner"
|
|
41
|
+
:class="una?.formGroupTopWrapperInner"
|
|
42
|
+
>
|
|
43
|
+
<slot name="label">
|
|
44
|
+
<label
|
|
45
|
+
v-if="label"
|
|
46
|
+
:for="name ?? id"
|
|
47
|
+
form-group="label-wrapper"
|
|
48
|
+
:class="una?.formGroupLabelWrapper"
|
|
49
|
+
>
|
|
50
|
+
<span
|
|
51
|
+
form-group="label"
|
|
52
|
+
:class="una?.formGroupLabel"
|
|
53
|
+
>
|
|
54
|
+
{{ label }}
|
|
55
|
+
</span>
|
|
56
|
+
<span
|
|
57
|
+
v-if="required"
|
|
58
|
+
form-group="label-required"
|
|
59
|
+
:class="una?.formGroupLabelRequired"
|
|
60
|
+
/>
|
|
61
|
+
</label>
|
|
62
|
+
</slot>
|
|
63
|
+
|
|
64
|
+
<slot name="hint">
|
|
65
|
+
<span
|
|
66
|
+
v-if="hint" form-group="hint"
|
|
67
|
+
:class="una?.formGroupHint"
|
|
68
|
+
>
|
|
69
|
+
{{ hint }}
|
|
70
|
+
</span>
|
|
71
|
+
</slot>
|
|
72
|
+
</div>
|
|
73
|
+
|
|
74
|
+
<slot name="description">
|
|
75
|
+
<span
|
|
76
|
+
v-if="description"
|
|
77
|
+
form-group="description"
|
|
78
|
+
:class="una?.formGroupDescription"
|
|
79
|
+
>
|
|
80
|
+
{{ description }}
|
|
81
|
+
</span>
|
|
82
|
+
</slot>
|
|
83
|
+
</div>
|
|
84
|
+
</div>
|
|
85
|
+
</slot>
|
|
86
|
+
|
|
87
|
+
<NFormGroupDefaultSlot
|
|
88
|
+
:id="name ?? id"
|
|
89
|
+
:status="status"
|
|
90
|
+
>
|
|
91
|
+
<slot />
|
|
92
|
+
</NFormGroupDefaultSlot>
|
|
93
|
+
|
|
94
|
+
<slot name="bottom">
|
|
95
|
+
<div
|
|
96
|
+
v-if="message || counter"
|
|
97
|
+
form-group="bottom-wrapper"
|
|
98
|
+
:class="[
|
|
99
|
+
{ 'justify-end': !message && counter },
|
|
100
|
+
una?.formGroupBottomWrapper,
|
|
101
|
+
]"
|
|
102
|
+
>
|
|
103
|
+
<slot name="message">
|
|
104
|
+
<div
|
|
105
|
+
v-if="message"
|
|
106
|
+
form-group="message-wrapper"
|
|
107
|
+
:class="una?.formGroupMessageWrapper"
|
|
108
|
+
>
|
|
109
|
+
<p
|
|
110
|
+
form-group="message"
|
|
111
|
+
:class="[
|
|
112
|
+
una?.formGroupMessage,
|
|
113
|
+
statusClassVariants,
|
|
114
|
+
]"
|
|
115
|
+
>
|
|
116
|
+
{{ message }}
|
|
117
|
+
</p>
|
|
118
|
+
</div>
|
|
119
|
+
</slot>
|
|
120
|
+
|
|
121
|
+
<slot name="counter">
|
|
122
|
+
<div
|
|
123
|
+
v-if="counter"
|
|
124
|
+
form-group="counter-wrapper"
|
|
125
|
+
:class="una?.formGroupCounterWrapper"
|
|
126
|
+
>
|
|
127
|
+
<span
|
|
128
|
+
:class="`${counter?.value >= (counter?.max || 0) && counter?.max
|
|
129
|
+
? 'form-group-counter-error'
|
|
130
|
+
: 'form-group-counter-current'}`"
|
|
131
|
+
>
|
|
132
|
+
{{ counter?.value }}
|
|
133
|
+
</span>
|
|
134
|
+
<span v-if="counter?.max" form-group="counter-separator">/</span>
|
|
135
|
+
<span v-if="counter?.max" form-group="counter-max">{{ counter?.max }}</span>
|
|
136
|
+
</div>
|
|
137
|
+
</slot>
|
|
138
|
+
</div>
|
|
139
|
+
</slot>
|
|
140
|
+
</div>
|
|
141
|
+
</template>
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { useVModel } from '@vueuse/core'
|
|
3
|
+
import { computed } from 'vue'
|
|
4
|
+
import NIcon from '../elements/Icon.vue'
|
|
5
|
+
import type { NInputProps } from '../../types'
|
|
6
|
+
import { randomId } from '../../utils'
|
|
7
|
+
|
|
8
|
+
defineOptions({
|
|
9
|
+
inheritAttrs: false,
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
const props = withDefaults(defineProps<NInputProps>(), {
|
|
13
|
+
type: 'text',
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
const emit = defineEmits<{ (...args: any): void }>()
|
|
17
|
+
|
|
18
|
+
const slots = defineSlots<{
|
|
19
|
+
leading?: any
|
|
20
|
+
trailing?: any
|
|
21
|
+
}>()
|
|
22
|
+
|
|
23
|
+
const inputValue = useVModel(props, 'modelValue', emit, { passive: true })
|
|
24
|
+
|
|
25
|
+
const id = computed(() => props.id ?? randomId('input'))
|
|
26
|
+
|
|
27
|
+
const isLeading = computed(() => props.leading || slots.leading)
|
|
28
|
+
const isTrailing = computed(() => props.trailing || slots.trailing || props.status || props.loading)
|
|
29
|
+
|
|
30
|
+
const inputVariants = ['outline', 'solid'] as const
|
|
31
|
+
const hasVariant = computed(() => inputVariants.some(inputVariants => props.input?.includes(inputVariants)))
|
|
32
|
+
const isBaseVariant = computed(() => props.input?.includes('~'))
|
|
33
|
+
|
|
34
|
+
const statusClassVariants = computed(() => {
|
|
35
|
+
const input = {
|
|
36
|
+
info: 'input-status-info input-solid-info input-status-ring',
|
|
37
|
+
success: 'input-status-success input-solid-success input-status-ring',
|
|
38
|
+
warning: 'input-status-warning input-solid-warning input-status-ring',
|
|
39
|
+
error: 'input-status-error input-solid-error input-status-ring',
|
|
40
|
+
default: !hasVariant.value && !isBaseVariant.value ? 'input-default-variant' : '',
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const text = {
|
|
44
|
+
info: 'text-info',
|
|
45
|
+
success: 'text-success',
|
|
46
|
+
warning: 'text-warning',
|
|
47
|
+
error: 'text-error',
|
|
48
|
+
default: '',
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const icon = {
|
|
52
|
+
info: props.una?.inputWarningIcon ?? 'input-info-icon',
|
|
53
|
+
success: props.una?.inputSuccessIcon ?? 'input-success-icon',
|
|
54
|
+
warning: props.una?.inputWarningIcon ?? 'input-warning-icon',
|
|
55
|
+
error: props.una?.inputErrorIcon ?? 'input-error-icon',
|
|
56
|
+
default: '',
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return {
|
|
60
|
+
input: input[props.status ?? 'default'],
|
|
61
|
+
text: text[props.status ?? 'default'],
|
|
62
|
+
icon: icon[props.status ?? 'default'],
|
|
63
|
+
}
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
const reverseClassVariants = computed(() => {
|
|
67
|
+
const input = {
|
|
68
|
+
false: [{ 'input-leading-padding': isLeading.value }, { 'input-trailing-padding': isTrailing.value }],
|
|
69
|
+
true: [{ 'input-trailing-padding': isLeading.value }, { 'input-leading-padding': isTrailing.value }],
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return {
|
|
73
|
+
input: input[props.reverse ? 'true' : 'false'],
|
|
74
|
+
leadingWrapper: props.reverse ? 'input-trailing-wrapper' : 'input-leading-wrapper',
|
|
75
|
+
trailingWrapper: props.reverse ? 'input-leading-wrapper' : 'input-trailing-wrapper',
|
|
76
|
+
}
|
|
77
|
+
})
|
|
78
|
+
</script>
|
|
79
|
+
|
|
80
|
+
<template>
|
|
81
|
+
<div
|
|
82
|
+
input="wrapper"
|
|
83
|
+
:size="size"
|
|
84
|
+
:class="una?.inputWrapper"
|
|
85
|
+
>
|
|
86
|
+
<div
|
|
87
|
+
v-if="isLeading"
|
|
88
|
+
:class="[
|
|
89
|
+
una?.inputLeadingWrapper,
|
|
90
|
+
reverseClassVariants.leadingWrapper,
|
|
91
|
+
statusClassVariants.text,
|
|
92
|
+
]"
|
|
93
|
+
>
|
|
94
|
+
<slot name="leading">
|
|
95
|
+
<NIcon
|
|
96
|
+
v-if="leading"
|
|
97
|
+
:name="leading"
|
|
98
|
+
input="leading"
|
|
99
|
+
:class="una?.inputLeading"
|
|
100
|
+
@click="emit('leading')"
|
|
101
|
+
/>
|
|
102
|
+
</slot>
|
|
103
|
+
</div>
|
|
104
|
+
|
|
105
|
+
<input
|
|
106
|
+
:id="id"
|
|
107
|
+
v-model="inputValue"
|
|
108
|
+
:type="type"
|
|
109
|
+
class="input"
|
|
110
|
+
:class="[
|
|
111
|
+
statusClassVariants.input,
|
|
112
|
+
reverseClassVariants.input,
|
|
113
|
+
una?.input,
|
|
114
|
+
]"
|
|
115
|
+
:input="input"
|
|
116
|
+
v-bind="$attrs"
|
|
117
|
+
>
|
|
118
|
+
|
|
119
|
+
<div
|
|
120
|
+
v-if="isTrailing"
|
|
121
|
+
:class="[
|
|
122
|
+
una?.inputTrailingWrapper,
|
|
123
|
+
reverseClassVariants.trailingWrapper,
|
|
124
|
+
statusClassVariants.text,
|
|
125
|
+
]"
|
|
126
|
+
>
|
|
127
|
+
<NIcon
|
|
128
|
+
v-if="loading"
|
|
129
|
+
input="loading"
|
|
130
|
+
:name="una?.inputLoadingIcon ?? 'input-loading-icon'"
|
|
131
|
+
:class="una?.inputLoading"
|
|
132
|
+
/>
|
|
133
|
+
|
|
134
|
+
<NIcon
|
|
135
|
+
v-if="status"
|
|
136
|
+
input="status-icon-base"
|
|
137
|
+
:name="statusClassVariants.icon"
|
|
138
|
+
/>
|
|
139
|
+
|
|
140
|
+
<slot v-else name="trailing">
|
|
141
|
+
<NIcon
|
|
142
|
+
v-if="trailing"
|
|
143
|
+
input="trailing"
|
|
144
|
+
:class="una?.inputTrailing"
|
|
145
|
+
:name="trailing"
|
|
146
|
+
@click="emit('trailing')"
|
|
147
|
+
/>
|
|
148
|
+
</slot>
|
|
149
|
+
</div>
|
|
150
|
+
</div>
|
|
151
|
+
</template>
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { Switch } from '@headlessui/vue'
|
|
3
|
+
import { computed } from 'vue'
|
|
4
|
+
import { useVModel } from '@vueuse/core'
|
|
5
|
+
import type { NSwitchProps } from '../../types'
|
|
6
|
+
import NIcon from '../elements/Icon.vue'
|
|
7
|
+
|
|
8
|
+
defineOptions({
|
|
9
|
+
inheritAttrs: false,
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
const props = defineProps<NSwitchProps>()
|
|
13
|
+
|
|
14
|
+
const emit = defineEmits<{ (...args: any): void }>()
|
|
15
|
+
|
|
16
|
+
const on = useVModel(props, 'modelValue', emit, { passive: true })
|
|
17
|
+
|
|
18
|
+
const _switch = computed(() => props.switch)
|
|
19
|
+
|
|
20
|
+
const outsetClassVariants = computed(() => {
|
|
21
|
+
const switchWrapper = {
|
|
22
|
+
false: 'switch-inset',
|
|
23
|
+
true: 'switch-outset',
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const switchTrack = {
|
|
27
|
+
false: 'switch-track-inset',
|
|
28
|
+
true: 'switch-track-outset',
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const switchThumb = {
|
|
32
|
+
false: 'left-0.125em',
|
|
33
|
+
true: 'left-0 border-base border',
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return {
|
|
37
|
+
switchWrapper: switchWrapper[!props.outset ? 'false' : 'true'],
|
|
38
|
+
switchTrack: switchTrack[!props.outset ? 'false' : 'true'],
|
|
39
|
+
switchThumb: switchThumb[!props.outset ? 'false' : 'true'],
|
|
40
|
+
}
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
const onClassVariants = computed(() => {
|
|
44
|
+
const switchTrack = {
|
|
45
|
+
true: `${props.una?.switchTrackOn ?? ''} switch-track-on`,
|
|
46
|
+
false: `${props.una?.switchTrackOff ?? ''} switch-track-off`,
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const switchThumb = {
|
|
50
|
+
true: `${props.una?.switchThumbOn ?? ''} switch-thumb-on`,
|
|
51
|
+
false: `${props.una?.switchThumbOff ?? ''} switch-thumb-off`,
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const switchIcon = {
|
|
55
|
+
true: `${props.onIcon ?? ''} switch-icon-on`,
|
|
56
|
+
false: `${props.offIcon ?? ''} switch-icon-off`,
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return {
|
|
60
|
+
switchTrack: switchTrack[on.value ? 'true' : 'false'],
|
|
61
|
+
switchThumb: switchThumb[on.value ? 'true' : 'false'],
|
|
62
|
+
switchIcon: switchIcon[on.value ? 'true' : 'false'],
|
|
63
|
+
}
|
|
64
|
+
})
|
|
65
|
+
</script>
|
|
66
|
+
|
|
67
|
+
<template>
|
|
68
|
+
<Switch
|
|
69
|
+
v-model="on"
|
|
70
|
+
class="switch"
|
|
71
|
+
:class="[
|
|
72
|
+
{ 'switch-disabled': disabled || loading },
|
|
73
|
+
outsetClassVariants?.switchWrapper,
|
|
74
|
+
]"
|
|
75
|
+
:switch="_switch"
|
|
76
|
+
v-bind="$attrs"
|
|
77
|
+
:disabled="disabled"
|
|
78
|
+
>
|
|
79
|
+
<span class="sr-only">Track</span>
|
|
80
|
+
<span
|
|
81
|
+
aria-hidden="true"
|
|
82
|
+
switch="track"
|
|
83
|
+
:class="[
|
|
84
|
+
una?.switchTrack,
|
|
85
|
+
onClassVariants?.switchTrack,
|
|
86
|
+
outsetClassVariants?.switchTrack,
|
|
87
|
+
]"
|
|
88
|
+
/>
|
|
89
|
+
|
|
90
|
+
<span class="sr-only">Thumb</span>
|
|
91
|
+
<span
|
|
92
|
+
aria-hidden="true"
|
|
93
|
+
switch="thumb"
|
|
94
|
+
:class="[
|
|
95
|
+
una?.switchThumb,
|
|
96
|
+
onClassVariants?.switchThumb,
|
|
97
|
+
outsetClassVariants?.switchThumb,
|
|
98
|
+
]"
|
|
99
|
+
>
|
|
100
|
+
<span class="sr-only">Icon</span>
|
|
101
|
+
<slot v-if="!loading" name="icon" :on="on">
|
|
102
|
+
<NIcon
|
|
103
|
+
switch="icon-base"
|
|
104
|
+
:name="onClassVariants?.switchIcon"
|
|
105
|
+
:class="una?.switchIconBase"
|
|
106
|
+
/>
|
|
107
|
+
</slot>
|
|
108
|
+
<slot v-else name="loading-icon" :on="on">
|
|
109
|
+
<NIcon
|
|
110
|
+
switch="loading"
|
|
111
|
+
:class="una?.switchLoading"
|
|
112
|
+
:name="una?.switchloadingicon ?? 'switch-loading-icon'"
|
|
113
|
+
/>
|
|
114
|
+
</slot>
|
|
115
|
+
</span>
|
|
116
|
+
</Switch>
|
|
117
|
+
</template>
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { Popover, PopoverButton, PopoverPanel } from '@headlessui/vue'
|
|
3
|
+
import { useStorage, useToggle } from '@vueuse/core'
|
|
4
|
+
import { computed } from 'vue'
|
|
5
|
+
import { grayThemes, primaryThemes } from '../../composables/themes'
|
|
6
|
+
|
|
7
|
+
// TODO: globalize
|
|
8
|
+
export interface ThemeColors {
|
|
9
|
+
[key: string]: string
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// TODO: confingurable in the app.config
|
|
13
|
+
const defaultSettings = {
|
|
14
|
+
primaryColors: primaryThemes.filter(([colorName, _]) => colorName === 'yellow')[0][1],
|
|
15
|
+
grayColors: grayThemes.filter(([colorName, _]) => colorName === 'stone')[0][1],
|
|
16
|
+
fontSize: 16,
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const settings = useStorage('una-settings', defaultSettings)
|
|
20
|
+
|
|
21
|
+
// use yellow primary theme as default
|
|
22
|
+
const currentPrimaryTheme = computed(() => settings.value.primaryColors?.['--una-primary-hex'])
|
|
23
|
+
// get current theme name
|
|
24
|
+
const currentPrimaryThemeName = computed(() => {
|
|
25
|
+
const theme = primaryThemes.find(([, theme]) => theme['--una-primary-hex'] === currentPrimaryTheme.value)
|
|
26
|
+
return theme ? theme[0] : ''
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
// use stone primary theme as default
|
|
30
|
+
const currentGrayTheme = computed(() => settings.value.grayColors?.['--una-gray-hex'])
|
|
31
|
+
// get current theme name
|
|
32
|
+
const currentGrayThemeName = computed(() => {
|
|
33
|
+
const theme = grayThemes.find(([, theme]) => theme['--una-gray-hex'] === currentGrayTheme.value)
|
|
34
|
+
return theme ? theme[0] : ''
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
// update theme in storage
|
|
38
|
+
function updatePrimaryTheme(theme: ThemeColors) {
|
|
39
|
+
settings.value.primaryColors = theme
|
|
40
|
+
}
|
|
41
|
+
function updateGrayTheme(theme: ThemeColors) {
|
|
42
|
+
settings.value.grayColors = theme
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const [value, toggle] = useToggle()
|
|
46
|
+
function shuffleTheme() {
|
|
47
|
+
const randomPrimaryTheme = primaryThemes[Math.floor(Math.random() * primaryThemes.length)][1]
|
|
48
|
+
const randomGrayTheme = grayThemes[Math.floor(Math.random() * grayThemes.length)][1]
|
|
49
|
+
updatePrimaryTheme(randomPrimaryTheme)
|
|
50
|
+
updateGrayTheme(randomGrayTheme)
|
|
51
|
+
toggle()
|
|
52
|
+
}
|
|
53
|
+
</script>
|
|
54
|
+
|
|
55
|
+
<template>
|
|
56
|
+
<div class="sm:ml-5">
|
|
57
|
+
<Popover class="relative inline-block">
|
|
58
|
+
<PopoverButton
|
|
59
|
+
btn="~ square soft"
|
|
60
|
+
class="rounded-lg"
|
|
61
|
+
>
|
|
62
|
+
<span i-heroicons-swatch-20-solid text-md />
|
|
63
|
+
</PopoverButton>
|
|
64
|
+
|
|
65
|
+
<transition enter-active-class="transition ease-out duration-200" enter-from-class="opacity-0 translate-y-1" enter-to-class="opacity-100 translate-y-0" leave-active-class="transition ease-in duration-150" leave-from-class="opacity-100 translate-y-0" leave-to-class="opacity-0 translate-y-1">
|
|
66
|
+
<PopoverPanel class="absolute right-0 z-100 mt-2 w-54 border-1 border-base rounded-xl bg-muted px-4 py-5 shadow-lg">
|
|
67
|
+
<div class="flex flex-col space-y-5">
|
|
68
|
+
<div class="grid grid-cols-5 gap-3">
|
|
69
|
+
<button
|
|
70
|
+
v-for="[key, theme] in primaryThemes"
|
|
71
|
+
:key="key"
|
|
72
|
+
:style="{ background: theme['--una-primary-hex'] }"
|
|
73
|
+
class="h-6.5 w-6.5 rounded-full transition-all" :class="[currentPrimaryThemeName === key ? 'ring-2' : 'scale-93']"
|
|
74
|
+
ring="primary offset-4 offset-base"
|
|
75
|
+
@click="updatePrimaryTheme(theme)"
|
|
76
|
+
/>
|
|
77
|
+
</div>
|
|
78
|
+
|
|
79
|
+
<hr class="my-2 border-$c-divider">
|
|
80
|
+
|
|
81
|
+
<div class="grid grid-cols-5 gap-3">
|
|
82
|
+
<button
|
|
83
|
+
v-for="[key, theme] in grayThemes"
|
|
84
|
+
:key="key"
|
|
85
|
+
:style="{ background: theme['--una-gray-hex'] }"
|
|
86
|
+
:class="currentGrayThemeName === key ? 'ring-2' : 'scale-93'"
|
|
87
|
+
class="h-6.5 w-6.5 rounded-full transition-all"
|
|
88
|
+
ring="gray offset-4 offset-base"
|
|
89
|
+
@click="updateGrayTheme(theme)"
|
|
90
|
+
/>
|
|
91
|
+
</div>
|
|
92
|
+
|
|
93
|
+
<hr class="my-2 border-$c-divider">
|
|
94
|
+
|
|
95
|
+
<button
|
|
96
|
+
btn="~ solid block"
|
|
97
|
+
class="rounded-lg transition"
|
|
98
|
+
@click="shuffleTheme"
|
|
99
|
+
>
|
|
100
|
+
Shuffle
|
|
101
|
+
<span
|
|
102
|
+
i-heroicons-adjustments-horizontal-20-solid ml-2
|
|
103
|
+
:class="value ? 'rotate-180 transform' : 'rotate-0'"
|
|
104
|
+
/>
|
|
105
|
+
</button>
|
|
106
|
+
</div>
|
|
107
|
+
</PopoverPanel>
|
|
108
|
+
</transition>
|
|
109
|
+
</Popover>
|
|
110
|
+
</div>
|
|
111
|
+
</template>
|