@una-ui/nuxt 0.3.0-beta.1 → 0.4.0-beta.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/README.md +10 -1
- package/dist/module.d.mts +35 -0
- package/dist/module.d.ts +11 -7
- package/dist/module.json +1 -1
- package/dist/module.mjs +12 -40
- package/dist/runtime/components/elements/Link.vue +34 -3
- package/dist/runtime/components/elements/Link.vue.d.ts +8 -0
- package/dist/runtime/components/forms/Input.vue +49 -9
- package/dist/runtime/components/misc/ThemeSwitcher.vue +43 -40
- package/dist/runtime/components/navigation/NavLink.vue +36 -8
- package/dist/runtime/composables/useUnaSettings.d.ts +1 -0
- package/dist/runtime/composables/useUnaSettings.mjs +30 -0
- package/dist/runtime/composables/useUnaThemes.d.ts +7 -0
- package/dist/runtime/composables/{themes.mjs → useUnaThemes.mjs} +31 -17
- package/dist/runtime/plugins/theme.client.d.ts +0 -3
- package/dist/runtime/plugins/theme.client.mjs +4 -9
- package/dist/runtime/plugins/theme.server.mjs +6 -1
- package/dist/runtime/types/accordion.d.ts +15 -15
- package/dist/runtime/types/alert.d.ts +6 -6
- package/dist/runtime/types/avatar-group.d.ts +3 -3
- package/dist/runtime/types/avatar.d.ts +9 -9
- package/dist/runtime/types/badge.d.ts +4 -4
- package/dist/runtime/types/button.d.ts +10 -10
- package/dist/runtime/types/checkbox.d.ts +9 -9
- package/dist/runtime/types/form-group.d.ts +10 -10
- package/dist/runtime/types/icon.d.ts +1 -1
- package/dist/runtime/types/index.d.ts +23 -0
- package/dist/runtime/types/indicator.d.ts +9 -9
- package/dist/runtime/types/input.d.ts +42 -13
- package/dist/runtime/types/kbd.d.ts +3 -3
- package/dist/runtime/types/link.d.ts +13 -4
- package/dist/runtime/types/nav-link.d.ts +10 -1
- package/dist/runtime/types/radio.d.ts +11 -11
- package/dist/runtime/types/switch.d.ts +8 -8
- package/dist/runtime/utils/index.d.ts +12 -12
- package/dist/types.d.mts +16 -0
- package/dist/types.d.ts +3 -2
- package/dist/una.config.d.mts +5 -0
- package/dist/una.config.d.ts +5 -0
- package/dist/una.config.mjs +35 -0
- package/package.json +23 -19
- package/playground/.nuxt/components.d.ts +208 -0
- package/playground/.nuxt/imports.d.ts +28 -0
- package/playground/.nuxt/nuxt.d.ts +18 -0
- package/playground/.nuxt/schema/nuxt.schema.d.ts +17 -0
- package/playground/.nuxt/types/app.config.d.ts +37 -0
- package/playground/.nuxt/types/imports.d.ts +1069 -0
- package/playground/.nuxt/types/nitro-config.d.ts +14 -0
- package/playground/.nuxt/types/nitro-imports.d.ts +121 -0
- package/playground/.nuxt/types/nitro-nuxt.d.ts +26 -0
- package/playground/.nuxt/types/nitro-routes.d.ts +11 -0
- package/playground/.nuxt/types/nitro.d.ts +3 -0
- package/playground/.nuxt/types/plugins.d.ts +30 -0
- package/playground/.nuxt/types/schema.d.ts +29 -0
- package/playground/.nuxt/types/vue-shim.d.ts +5 -0
- package/playground/.nuxt/vue-router-stub.d.ts +1 -0
- package/una.config.d.ts +1 -0
- package/dist/runtime/composables/themes.d.ts +0 -7
package/README.md
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# ✨ Una UI
|
|
2
2
|
|
|
3
|
-
>
|
|
3
|
+
> [!WARNING]
|
|
4
|
+
> This project is heavily working in progress.
|
|
4
5
|
|
|
5
6
|
## 🏗️ Release Status
|
|
6
7
|
|
|
@@ -13,6 +14,14 @@
|
|
|
13
14
|
|
|
14
15
|
Visit https://www.unaui.com for full documentation.
|
|
15
16
|
|
|
17
|
+
## 🌻 Sponsors
|
|
18
|
+
|
|
19
|
+
<p align="center">
|
|
20
|
+
<a href="https://cdn.jsdelivr.net/gh/phojie/static/sponsors.svg">
|
|
21
|
+
<img src='https://cdn.jsdelivr.net/gh/phojie/static/sponsors.svg'/>
|
|
22
|
+
</a>
|
|
23
|
+
</p>
|
|
24
|
+
|
|
16
25
|
## 🏛️ License
|
|
17
26
|
|
|
18
27
|
[MIT](./LICENSE) License © 2023 [Phojie](https://github.com/phojie)
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import * as _nuxt_schema from '@nuxt/schema';
|
|
2
|
+
|
|
3
|
+
interface UnaOptions {
|
|
4
|
+
primary?: string;
|
|
5
|
+
gray?: string;
|
|
6
|
+
}
|
|
7
|
+
declare module '@nuxt/schema' {
|
|
8
|
+
interface AppConfigInput {
|
|
9
|
+
una?: UnaOptions;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
interface ModuleOptions {
|
|
13
|
+
/**
|
|
14
|
+
* @default 'N'
|
|
15
|
+
*/
|
|
16
|
+
prefix?: string;
|
|
17
|
+
/**
|
|
18
|
+
* @default true
|
|
19
|
+
* @description Enable themeable ui
|
|
20
|
+
*
|
|
21
|
+
*/
|
|
22
|
+
themeable?: boolean;
|
|
23
|
+
/**
|
|
24
|
+
* @default true
|
|
25
|
+
* @description Register components globally
|
|
26
|
+
*/
|
|
27
|
+
global?: boolean;
|
|
28
|
+
/**
|
|
29
|
+
* @default false
|
|
30
|
+
*/
|
|
31
|
+
dev: boolean;
|
|
32
|
+
}
|
|
33
|
+
declare const _default: _nuxt_schema.NuxtModule<ModuleOptions>;
|
|
34
|
+
|
|
35
|
+
export { type ModuleOptions, _default as default };
|
package/dist/module.d.ts
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
import * as _nuxt_schema from '@nuxt/schema';
|
|
2
2
|
|
|
3
|
+
interface UnaOptions {
|
|
4
|
+
primary?: string;
|
|
5
|
+
gray?: string;
|
|
6
|
+
}
|
|
7
|
+
declare module '@nuxt/schema' {
|
|
8
|
+
interface AppConfigInput {
|
|
9
|
+
una?: UnaOptions;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
3
12
|
interface ModuleOptions {
|
|
4
13
|
/**
|
|
5
14
|
* @default 'N'
|
|
@@ -9,18 +18,13 @@ interface ModuleOptions {
|
|
|
9
18
|
* @default true
|
|
10
19
|
* @description Enable themeable ui
|
|
11
20
|
*
|
|
12
|
-
|
|
21
|
+
*/
|
|
13
22
|
themeable?: boolean;
|
|
14
23
|
/**
|
|
15
24
|
* @default true
|
|
16
25
|
* @description Register components globally
|
|
17
26
|
*/
|
|
18
27
|
global?: boolean;
|
|
19
|
-
/**
|
|
20
|
-
* @default '@una-ui/preset'
|
|
21
|
-
* @description Path to preset
|
|
22
|
-
*/
|
|
23
|
-
preset?: string;
|
|
24
28
|
/**
|
|
25
29
|
* @default false
|
|
26
30
|
*/
|
|
@@ -28,4 +32,4 @@ interface ModuleOptions {
|
|
|
28
32
|
}
|
|
29
33
|
declare const _default: _nuxt_schema.NuxtModule<ModuleOptions>;
|
|
30
34
|
|
|
31
|
-
export { ModuleOptions, _default as default };
|
|
35
|
+
export { type ModuleOptions, _default as default };
|
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -1,45 +1,13 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
import
|
|
1
|
+
import { defineNuxtModule, createResolver, addComponentsDir, addPlugin, installModule, addImportsDir } from '@nuxt/kit';
|
|
2
|
+
import extendUnocssOptions from './una.config.mjs';
|
|
3
|
+
import 'unocss';
|
|
4
|
+
import '@una-ui/preset';
|
|
5
|
+
import '@una-ui/preset/prefixes';
|
|
6
|
+
import '@una-ui/extractor-vue-script';
|
|
7
7
|
|
|
8
8
|
const name = "@una-ui/nuxt";
|
|
9
|
-
const version = "0.
|
|
9
|
+
const version = "0.4.0-beta.0";
|
|
10
10
|
|
|
11
|
-
function extendUnocssOptions(user = {}) {
|
|
12
|
-
return {
|
|
13
|
-
...user,
|
|
14
|
-
preflight: false,
|
|
15
|
-
presets: [
|
|
16
|
-
presetUno(),
|
|
17
|
-
presetAttributify(),
|
|
18
|
-
presetIcons({
|
|
19
|
-
scale: 1.2,
|
|
20
|
-
extraProperties: {
|
|
21
|
-
"display": "inline-block",
|
|
22
|
-
"vertical-align": "middle"
|
|
23
|
-
}
|
|
24
|
-
}),
|
|
25
|
-
presetUna(),
|
|
26
|
-
...user.presets || []
|
|
27
|
-
],
|
|
28
|
-
extractors: [
|
|
29
|
-
extratorUna({
|
|
30
|
-
prefixes
|
|
31
|
-
})
|
|
32
|
-
],
|
|
33
|
-
transformers: [
|
|
34
|
-
transformerDirectives(),
|
|
35
|
-
transformerVariantGroup()
|
|
36
|
-
]
|
|
37
|
-
};
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function rPath(p) {
|
|
41
|
-
return fileURLToPath(new URL(p, import.meta.url).toString());
|
|
42
|
-
}
|
|
43
11
|
const module = defineNuxtModule({
|
|
44
12
|
meta: {
|
|
45
13
|
name,
|
|
@@ -52,13 +20,16 @@ const module = defineNuxtModule({
|
|
|
52
20
|
defaults: {
|
|
53
21
|
prefix: "N",
|
|
54
22
|
themeable: true,
|
|
55
|
-
preset: rPath("./preset"),
|
|
56
23
|
global: true,
|
|
57
24
|
dev: false
|
|
58
25
|
},
|
|
59
26
|
async setup(options, nuxt) {
|
|
60
27
|
const { resolve } = createResolver(import.meta.url);
|
|
61
28
|
nuxt.options.css.unshift("@una-ui/preset/una.css");
|
|
29
|
+
nuxt.options.appConfig.una = {
|
|
30
|
+
primary: "yellow",
|
|
31
|
+
gray: "stone"
|
|
32
|
+
};
|
|
62
33
|
const runtimeDir = resolve("./runtime");
|
|
63
34
|
nuxt.options.build.transpile.push(runtimeDir);
|
|
64
35
|
nuxt.options.build.transpile.push("@headlessui/vue");
|
|
@@ -97,6 +68,7 @@ const module = defineNuxtModule({
|
|
|
97
68
|
classSuffix: ""
|
|
98
69
|
});
|
|
99
70
|
await installModule("@vueuse/nuxt");
|
|
71
|
+
addImportsDir(resolve(runtimeDir, "composables"));
|
|
100
72
|
}
|
|
101
73
|
});
|
|
102
74
|
|
|
@@ -23,6 +23,15 @@ export default defineComponent({
|
|
|
23
23
|
inactiveClass: {
|
|
24
24
|
type: String,
|
|
25
25
|
default: void 0
|
|
26
|
+
},
|
|
27
|
+
// preset
|
|
28
|
+
navLinkActive: {
|
|
29
|
+
type: String,
|
|
30
|
+
default: void 0
|
|
31
|
+
},
|
|
32
|
+
navLinkInactive: {
|
|
33
|
+
type: String,
|
|
34
|
+
default: void 0
|
|
26
35
|
}
|
|
27
36
|
},
|
|
28
37
|
setup(props) {
|
|
@@ -37,8 +46,30 @@ export default defineComponent({
|
|
|
37
46
|
return props.activeClass;
|
|
38
47
|
return props.inactiveClass;
|
|
39
48
|
}
|
|
49
|
+
function resolveNavLinkActive(route, $route, { isActive, isExactActive }) {
|
|
50
|
+
if (props.exactQuery && !isEqual(route.query, $route.query))
|
|
51
|
+
return null;
|
|
52
|
+
if (props.exactHash && !isEqual(route.hash, $route.hash))
|
|
53
|
+
return null;
|
|
54
|
+
if (props.exact && isExactActive)
|
|
55
|
+
return props.navLinkActive;
|
|
56
|
+
if (!props.exact && isActive)
|
|
57
|
+
return props.navLinkActive;
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
function resolveNavLinkInactive(route, $route, { isActive, isExactActive }) {
|
|
61
|
+
if (props.exactQuery && !isEqual(route.query, $route.query))
|
|
62
|
+
return props.navLinkInactive;
|
|
63
|
+
if (props.exactHash && !isEqual(route.hash, $route.hash))
|
|
64
|
+
return props.navLinkInactive;
|
|
65
|
+
if (!props.exact && isActive || props.exact && isExactActive)
|
|
66
|
+
return null;
|
|
67
|
+
return props.navLinkInactive;
|
|
68
|
+
}
|
|
40
69
|
return {
|
|
41
|
-
resolveLinkClass
|
|
70
|
+
resolveLinkClass,
|
|
71
|
+
resolveNavLinkActive,
|
|
72
|
+
resolveNavLinkInactive
|
|
42
73
|
};
|
|
43
74
|
}
|
|
44
75
|
});
|
|
@@ -48,16 +79,16 @@ export default defineComponent({
|
|
|
48
79
|
<NuxtLink
|
|
49
80
|
v-slot="{ route, href, target, rel, navigate, isActive, isExactActive, isExternal }"
|
|
50
81
|
v-bind="$props"
|
|
51
|
-
class="link"
|
|
52
82
|
custom
|
|
53
83
|
>
|
|
54
84
|
<a
|
|
55
85
|
v-bind="$attrs"
|
|
56
86
|
:href="href"
|
|
57
87
|
:rel="rel"
|
|
58
|
-
class="link"
|
|
59
88
|
:target="target"
|
|
60
89
|
:class="resolveLinkClass(route, $route, { isActive, isExactActive })"
|
|
90
|
+
:nav-link-active="resolveNavLinkActive(route, $route, { isActive, isExactActive })"
|
|
91
|
+
:nav-link-inactive="resolveNavLinkInactive(route, $route, { isActive, isExactActive })"
|
|
61
92
|
@click="(e) => !isExternal && navigate(e)"
|
|
62
93
|
>
|
|
63
94
|
<slot v-bind="{ isActive: exact ? isExactActive : isActive }" />
|
|
@@ -3,6 +3,14 @@ declare const _default: import("vue").DefineComponent<any, {
|
|
|
3
3
|
isActive: boolean;
|
|
4
4
|
isExactActive: boolean;
|
|
5
5
|
}) => any;
|
|
6
|
+
resolveNavLinkActive: (route: any, $route: any, { isActive, isExactActive }: {
|
|
7
|
+
isActive: boolean;
|
|
8
|
+
isExactActive: boolean;
|
|
9
|
+
}) => any;
|
|
10
|
+
resolveNavLinkInactive: (route: any, $route: any, { isActive, isExactActive }: {
|
|
11
|
+
isActive: boolean;
|
|
12
|
+
isExactActive: boolean;
|
|
13
|
+
}) => any;
|
|
6
14
|
}, unknown, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, Readonly<any>, {} | {
|
|
7
15
|
[x: string]: any;
|
|
8
16
|
}, {}>;
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import {
|
|
3
|
-
import { computed } from 'vue'
|
|
2
|
+
import { computed, onMounted, ref } from 'vue'
|
|
4
3
|
import NIcon from '../elements/Icon.vue'
|
|
5
4
|
import type { NInputProps } from '../../types'
|
|
6
5
|
import { randomId } from '../../utils'
|
|
@@ -11,6 +10,8 @@ defineOptions({
|
|
|
11
10
|
|
|
12
11
|
const props = withDefaults(defineProps<NInputProps>(), {
|
|
13
12
|
type: 'text',
|
|
13
|
+
resize: 'none',
|
|
14
|
+
rows: 3,
|
|
14
15
|
})
|
|
15
16
|
|
|
16
17
|
const emit = defineEmits<{ (...args: any): void }>()
|
|
@@ -20,8 +21,6 @@ const slots = defineSlots<{
|
|
|
20
21
|
trailing?: any
|
|
21
22
|
}>()
|
|
22
23
|
|
|
23
|
-
const inputValue = useVModel(props, 'modelValue', emit, { passive: true })
|
|
24
|
-
|
|
25
24
|
const id = computed(() => props.id ?? randomId('input'))
|
|
26
25
|
|
|
27
26
|
const isLeading = computed(() => props.leading || slots.leading)
|
|
@@ -75,6 +74,41 @@ const reverseClassVariants = computed(() => {
|
|
|
75
74
|
trailingWrapper: props.reverse ? 'input-leading-wrapper' : 'input-trailing-wrapper',
|
|
76
75
|
}
|
|
77
76
|
})
|
|
77
|
+
|
|
78
|
+
// html refs
|
|
79
|
+
const textarea = ref<HTMLTextAreaElement>()
|
|
80
|
+
|
|
81
|
+
function resizeTextarea() {
|
|
82
|
+
if (!(props.type === 'textarea' && props.autoresize) || !textarea.value)
|
|
83
|
+
return
|
|
84
|
+
|
|
85
|
+
textarea.value.rows = props.rows
|
|
86
|
+
|
|
87
|
+
const styles = window.getComputedStyle(textarea.value)
|
|
88
|
+
const paddingTop = Number.parseInt(styles.paddingTop)
|
|
89
|
+
const paddingBottom = Number.parseInt(styles.paddingBottom)
|
|
90
|
+
const padding = paddingTop + paddingBottom
|
|
91
|
+
const lineHeight = Number.parseInt(styles.lineHeight)
|
|
92
|
+
const { scrollHeight } = textarea.value
|
|
93
|
+
const newRows = (scrollHeight - padding) / lineHeight
|
|
94
|
+
|
|
95
|
+
if (newRows > props.rows)
|
|
96
|
+
textarea.value.rows = newRows
|
|
97
|
+
|
|
98
|
+
const maxAutoresizeRows = typeof props.autoresize === 'number' ? props.autoresize : Number.POSITIVE_INFINITY
|
|
99
|
+
if (textarea.value.rows > maxAutoresizeRows)
|
|
100
|
+
textarea.value.rows = maxAutoresizeRows
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function onInput(event: Event) {
|
|
104
|
+
emit('update:modelValue', (event.target as HTMLInputElement).value)
|
|
105
|
+
|
|
106
|
+
resizeTextarea()
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
onMounted(() => {
|
|
110
|
+
resizeTextarea()
|
|
111
|
+
})
|
|
78
112
|
</script>
|
|
79
113
|
|
|
80
114
|
<template>
|
|
@@ -102,10 +136,12 @@ const reverseClassVariants = computed(() => {
|
|
|
102
136
|
</slot>
|
|
103
137
|
</div>
|
|
104
138
|
|
|
105
|
-
<
|
|
139
|
+
<Component
|
|
140
|
+
:is="props.type !== 'textarea' ? 'input' : 'textarea'"
|
|
106
141
|
:id="id"
|
|
107
|
-
|
|
108
|
-
:
|
|
142
|
+
ref="textarea"
|
|
143
|
+
:value="modelValue"
|
|
144
|
+
:type="props.type !== 'textarea' ? props.type : undefined"
|
|
109
145
|
class="input"
|
|
110
146
|
:class="[
|
|
111
147
|
statusClassVariants.input,
|
|
@@ -113,8 +149,12 @@ const reverseClassVariants = computed(() => {
|
|
|
113
149
|
una?.input,
|
|
114
150
|
]"
|
|
115
151
|
:input="input"
|
|
152
|
+
:resize="type === 'textarea' ? resize : undefined"
|
|
153
|
+
:rows="type === 'textarea' ? rows : undefined"
|
|
154
|
+
:cols="type === 'textarea' ? cols : undefined"
|
|
116
155
|
v-bind="$attrs"
|
|
117
|
-
|
|
156
|
+
@input="onInput"
|
|
157
|
+
/>
|
|
118
158
|
|
|
119
159
|
<div
|
|
120
160
|
v-if="isTrailing"
|
|
@@ -132,7 +172,7 @@ const reverseClassVariants = computed(() => {
|
|
|
132
172
|
/>
|
|
133
173
|
|
|
134
174
|
<NIcon
|
|
135
|
-
v-if="status"
|
|
175
|
+
v-else-if="status"
|
|
136
176
|
input="status-icon-base"
|
|
137
177
|
:name="statusClassVariants.icon"
|
|
138
178
|
/>
|
|
@@ -1,51 +1,42 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { Popover, PopoverButton, PopoverPanel } from '@headlessui/vue'
|
|
3
|
-
import {
|
|
3
|
+
import { useToggle } from '@vueuse/core'
|
|
4
4
|
import { computed } from 'vue'
|
|
5
|
-
import {
|
|
5
|
+
import { useUnaThemes } from '../../composables/useUnaThemes'
|
|
6
|
+
import { useUnaSettings } from '../../composables/useUnaSettings'
|
|
7
|
+
import NButton from '../elements/Button.vue'
|
|
6
8
|
|
|
7
|
-
|
|
8
|
-
export interface ThemeColors {
|
|
9
|
-
[key: string]: string
|
|
10
|
-
}
|
|
9
|
+
const { primaryThemes, grayThemes } = useUnaThemes()
|
|
11
10
|
|
|
12
|
-
|
|
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
|
-
}
|
|
11
|
+
const { settings, reset } = useUnaSettings()
|
|
18
12
|
|
|
19
|
-
const
|
|
13
|
+
const currentPrimaryThemeHex = computed(() => settings.value.primaryColors?.['--una-primary-hex'])
|
|
20
14
|
|
|
21
|
-
// use yellow primary theme as default
|
|
22
|
-
const currentPrimaryTheme = computed(() => settings.value.primaryColors?.['--una-primary-hex'])
|
|
23
|
-
// get current theme name
|
|
24
15
|
const currentPrimaryThemeName = computed(() => {
|
|
25
|
-
const theme = primaryThemes.find(([, theme]) => theme['--una-primary-hex'] ===
|
|
16
|
+
const theme = primaryThemes.find(([, theme]) => theme['--una-primary-hex'] === currentPrimaryThemeHex.value)
|
|
26
17
|
return theme ? theme[0] : ''
|
|
27
18
|
})
|
|
28
19
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
// get current theme name
|
|
20
|
+
const currentGrayThemeHex = computed(() => settings.value.grayColors?.['--una-gray-hex'])
|
|
21
|
+
|
|
32
22
|
const currentGrayThemeName = computed(() => {
|
|
33
|
-
const theme = grayThemes.find(([, theme]) => theme['--una-gray-hex'] ===
|
|
23
|
+
const theme = grayThemes.find(([, theme]) => theme['--una-gray-hex'] === currentGrayThemeHex.value)
|
|
34
24
|
return theme ? theme[0] : ''
|
|
35
25
|
})
|
|
36
26
|
|
|
37
27
|
// update theme in storage
|
|
38
|
-
function updatePrimaryTheme(theme:
|
|
39
|
-
settings.value.
|
|
28
|
+
function updatePrimaryTheme(theme: string) {
|
|
29
|
+
settings.value.primary = theme
|
|
40
30
|
}
|
|
41
|
-
|
|
42
|
-
|
|
31
|
+
|
|
32
|
+
function updateGrayTheme(theme: string) {
|
|
33
|
+
settings.value.gray = theme
|
|
43
34
|
}
|
|
44
35
|
|
|
45
36
|
const [value, toggle] = useToggle()
|
|
46
37
|
function shuffleTheme() {
|
|
47
|
-
const randomPrimaryTheme = primaryThemes[Math.floor(Math.random() * primaryThemes.length)][
|
|
48
|
-
const randomGrayTheme = grayThemes[Math.floor(Math.random() * grayThemes.length)][
|
|
38
|
+
const randomPrimaryTheme = primaryThemes[Math.floor(Math.random() * primaryThemes.length)][0]
|
|
39
|
+
const randomGrayTheme = grayThemes[Math.floor(Math.random() * grayThemes.length)][0]
|
|
49
40
|
updatePrimaryTheme(randomPrimaryTheme)
|
|
50
41
|
updateGrayTheme(randomGrayTheme)
|
|
51
42
|
toggle()
|
|
@@ -53,11 +44,12 @@ function shuffleTheme() {
|
|
|
53
44
|
</script>
|
|
54
45
|
|
|
55
46
|
<template>
|
|
56
|
-
<div
|
|
47
|
+
<div>
|
|
57
48
|
<Popover class="relative inline-block">
|
|
58
49
|
<PopoverButton
|
|
59
50
|
btn="~ square soft"
|
|
60
51
|
class="rounded-lg"
|
|
52
|
+
aria-label="Theme"
|
|
61
53
|
>
|
|
62
54
|
<span i-heroicons-swatch-20-solid text-md />
|
|
63
55
|
</PopoverButton>
|
|
@@ -72,7 +64,8 @@ function shuffleTheme() {
|
|
|
72
64
|
:style="{ background: theme['--una-primary-hex'] }"
|
|
73
65
|
class="h-6.5 w-6.5 rounded-full transition-all" :class="[currentPrimaryThemeName === key ? 'ring-2' : 'scale-93']"
|
|
74
66
|
ring="primary offset-4 offset-base"
|
|
75
|
-
|
|
67
|
+
aria-label="Primary Color"
|
|
68
|
+
@click="updatePrimaryTheme(key)"
|
|
76
69
|
/>
|
|
77
70
|
</div>
|
|
78
71
|
|
|
@@ -85,24 +78,34 @@ function shuffleTheme() {
|
|
|
85
78
|
:style="{ background: theme['--una-gray-hex'] }"
|
|
86
79
|
:class="currentGrayThemeName === key ? 'ring-2' : 'scale-93'"
|
|
87
80
|
class="h-6.5 w-6.5 rounded-full transition-all"
|
|
81
|
+
aria-label="Gray Color"
|
|
88
82
|
ring="gray offset-4 offset-base"
|
|
89
|
-
@click="updateGrayTheme(
|
|
83
|
+
@click="updateGrayTheme(key)"
|
|
90
84
|
/>
|
|
91
85
|
</div>
|
|
92
86
|
|
|
93
87
|
<hr class="my-2 border-$c-divider">
|
|
94
88
|
|
|
95
|
-
<
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
89
|
+
<div class="flex space-x-3">
|
|
90
|
+
<NButton
|
|
91
|
+
btn="~ solid block"
|
|
92
|
+
class="transition"
|
|
93
|
+
label="Shuffle"
|
|
94
|
+
leading="i-heroicons-adjustments-horizontal-solid"
|
|
95
|
+
:una="{
|
|
96
|
+
btnLeading: value ? 'rotate-180 transform' : 'rotate-0',
|
|
97
|
+
}"
|
|
98
|
+
@click="shuffleTheme"
|
|
104
99
|
/>
|
|
105
|
-
|
|
100
|
+
|
|
101
|
+
<NButton
|
|
102
|
+
btn="~ solid-gray"
|
|
103
|
+
size="xs"
|
|
104
|
+
icon
|
|
105
|
+
label="i-heroicons-arrow-uturn-left"
|
|
106
|
+
@click="reset"
|
|
107
|
+
/>
|
|
108
|
+
</div>
|
|
106
109
|
</div>
|
|
107
110
|
</PopoverPanel>
|
|
108
111
|
</transition>
|
|
@@ -1,24 +1,52 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
+
import { computed } from 'vue'
|
|
2
3
|
import type { NNavLinkProps } from '../../types'
|
|
3
4
|
import { omitProps } from '../../utils'
|
|
5
|
+
import NButton from '../elements/Button.vue'
|
|
4
6
|
|
|
5
7
|
defineOptions({
|
|
6
8
|
inheritAttrs: false,
|
|
7
9
|
})
|
|
8
10
|
|
|
9
|
-
const props =
|
|
11
|
+
const props = withDefaults(
|
|
12
|
+
defineProps<NNavLinkProps>(),
|
|
13
|
+
{
|
|
14
|
+
navLinkActive: 'text-primary',
|
|
15
|
+
navLinkInactive: 'text-primary',
|
|
16
|
+
una: () => ({
|
|
17
|
+
btnDefaultVariant: '~',
|
|
18
|
+
navLinkDefaultVariant: 'nav-link-default-variant',
|
|
19
|
+
}),
|
|
20
|
+
},
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
const btnProps = omitProps(props.una, [
|
|
24
|
+
'navLinkDefaultVariant',
|
|
25
|
+
'navLink',
|
|
26
|
+
'navLinkActive',
|
|
27
|
+
'navLinkInactive',
|
|
28
|
+
])
|
|
29
|
+
|
|
30
|
+
const navLinkVariants = ['text', 'solid'] as const
|
|
31
|
+
const hasVariant = computed(() => navLinkVariants.some(navLinkVariants => props.navLink?.includes(navLinkVariants)))
|
|
32
|
+
const isBaseVariant = computed(() => props.navLink?.includes('~') || props.una.navLink?.includes('~'))
|
|
10
33
|
</script>
|
|
11
34
|
|
|
12
35
|
<template>
|
|
13
36
|
<NButton
|
|
14
|
-
nav-link="
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
:una="
|
|
18
|
-
|
|
19
|
-
|
|
37
|
+
:nav-link="navLink"
|
|
38
|
+
:nav-link-active="navLinkActive"
|
|
39
|
+
:nav-link-inactive="navLinkInactive"
|
|
40
|
+
:una="btnProps"
|
|
41
|
+
class="nav-link"
|
|
42
|
+
:class="[
|
|
43
|
+
!hasVariant && !isBaseVariant ? una?.navLinkDefaultVariant : null,
|
|
44
|
+
{ 'btn-reverse': reverse },
|
|
45
|
+
]"
|
|
46
|
+
v-bind="{
|
|
47
|
+
...omitProps(props, ['badge', 'una']),
|
|
48
|
+
...$attrs,
|
|
20
49
|
}"
|
|
21
|
-
v-bind="{ ...omitProps(props, ['badge', 'una']), ...$attrs }"
|
|
22
50
|
>
|
|
23
51
|
<template #leading>
|
|
24
52
|
<NIcon
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function useUnaSettings(): any;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { useStorage } from "@vueuse/core";
|
|
2
|
+
import { watchEffect } from "vue";
|
|
3
|
+
import { useUnaThemes } from "./useUnaThemes.mjs";
|
|
4
|
+
import { useAppConfig } from "#imports";
|
|
5
|
+
export function useUnaSettings() {
|
|
6
|
+
const { una } = useAppConfig();
|
|
7
|
+
const { getPrimaryColors, getGrayColors } = useUnaThemes();
|
|
8
|
+
const defaultSettings = {
|
|
9
|
+
primaryColors: getPrimaryColors(una.primary),
|
|
10
|
+
grayColors: getGrayColors(una.gray),
|
|
11
|
+
primary: una.primary,
|
|
12
|
+
gray: una.gray,
|
|
13
|
+
fontSize: 16
|
|
14
|
+
};
|
|
15
|
+
const settings = useStorage("una-settings", defaultSettings);
|
|
16
|
+
watchEffect(() => {
|
|
17
|
+
settings.value.primaryColors = getPrimaryColors(settings.value.primary);
|
|
18
|
+
settings.value.grayColors = getGrayColors(settings.value.gray);
|
|
19
|
+
});
|
|
20
|
+
function reset() {
|
|
21
|
+
settings.value.primary = defaultSettings.primary;
|
|
22
|
+
settings.value.gray = defaultSettings.gray;
|
|
23
|
+
settings.value.fontSize = defaultSettings.fontSize;
|
|
24
|
+
}
|
|
25
|
+
return {
|
|
26
|
+
defaultSettings,
|
|
27
|
+
settings,
|
|
28
|
+
reset
|
|
29
|
+
};
|
|
30
|
+
}
|
|
@@ -99,21 +99,35 @@ const filteredColors = {
|
|
|
99
99
|
...filteredPrimaryColors,
|
|
100
100
|
...filteredGrayColors
|
|
101
101
|
};
|
|
102
|
-
export function
|
|
103
|
-
const
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
102
|
+
export function useUnaThemes() {
|
|
103
|
+
const primaryThemes = Object.entries(filteredPrimaryColors).map(([color]) => [
|
|
104
|
+
color,
|
|
105
|
+
getColors(color, "primary")
|
|
106
|
+
]);
|
|
107
|
+
const grayThemes = Object.entries(filteredGrayColors).map(([color]) => [
|
|
108
|
+
color,
|
|
109
|
+
getColors(color, "gray")
|
|
110
|
+
]);
|
|
111
|
+
function getColors(color, prefix) {
|
|
112
|
+
const colorPalette = filteredColors[color];
|
|
113
|
+
if (!colorPalette)
|
|
114
|
+
throw new Error(`Invalid primary color: ${color}`);
|
|
115
|
+
const colors2 = {};
|
|
116
|
+
colors2[`--una-${prefix}-hex`] = colorPalette[600];
|
|
117
|
+
for (const shade of Object.keys(colorPalette))
|
|
118
|
+
colors2[`--una-${prefix}-${shade}`] = hexToRgb(colorPalette[shade]).join(", ");
|
|
119
|
+
return colors2;
|
|
120
|
+
}
|
|
121
|
+
function getPrimaryColors(color) {
|
|
122
|
+
return primaryThemes.filter(([colorName, _]) => colorName === color)[0][1];
|
|
123
|
+
}
|
|
124
|
+
function getGrayColors(color) {
|
|
125
|
+
return grayThemes.filter(([colorName, _]) => colorName === color)[0][1];
|
|
126
|
+
}
|
|
127
|
+
return {
|
|
128
|
+
primaryThemes,
|
|
129
|
+
grayThemes,
|
|
130
|
+
getPrimaryColors,
|
|
131
|
+
getGrayColors
|
|
132
|
+
};
|
|
111
133
|
}
|
|
112
|
-
export const primaryThemes = Object.entries(filteredPrimaryColors).map(([color]) => [
|
|
113
|
-
color,
|
|
114
|
-
getColors(color, "primary")
|
|
115
|
-
]);
|
|
116
|
-
export const grayThemes = Object.entries(filteredGrayColors).map(([color]) => [
|
|
117
|
-
color,
|
|
118
|
-
getColors(color, "gray")
|
|
119
|
-
]);
|