@nexxtmove/ui 1.34.0 → 1.35.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/components/editor/molecules/EditorColor/EditorColor.vue.d.ts +4 -1
- package/dist/components/organisms/ColorPicker/ColorPicker.vue.d.ts +7 -0
- package/dist/index.js +696 -646
- package/package.json +1 -1
- package/src/components/editor/molecules/EditorColor/EditorColor.vue +8 -1
- package/src/components/organisms/ColorPicker/ColorPicker.vue +86 -10
package/package.json
CHANGED
|
@@ -14,10 +14,11 @@ export type EditorColorProperty = 'bg' | 'text'
|
|
|
14
14
|
*/
|
|
15
15
|
export type EditorColorValue = `${EditorColorProperty}-[#${string}]`
|
|
16
16
|
|
|
17
|
-
/** Overridable copy of the picker. Every key is optional. */
|
|
18
17
|
export interface EditorColorMessages {
|
|
19
18
|
/** Text shown while no colour is set. Defaults to `'Kies kleur...'`. */
|
|
20
19
|
placeholder?: string
|
|
20
|
+
/** Label shown above reusable colours. */
|
|
21
|
+
suggestionsLabel?: string
|
|
21
22
|
}
|
|
22
23
|
|
|
23
24
|
export interface NexxtEditorColorProps {
|
|
@@ -44,10 +45,13 @@ export interface NexxtEditorColorProps {
|
|
|
44
45
|
* defaults of the others. Copy is never translated inside the component.
|
|
45
46
|
*/
|
|
46
47
|
messages?: EditorColorMessages
|
|
48
|
+
/** Hex colours available inside the picker panel as one-click suggestions. */
|
|
49
|
+
suggestions?: string[]
|
|
47
50
|
}
|
|
48
51
|
|
|
49
52
|
const DEFAULT_MESSAGES = {
|
|
50
53
|
placeholder: 'Kies kleur...',
|
|
54
|
+
suggestionsLabel: 'Gebruikte kleuren',
|
|
51
55
|
} satisfies Required<EditorColorMessages>
|
|
52
56
|
|
|
53
57
|
/**
|
|
@@ -72,6 +76,7 @@ const {
|
|
|
72
76
|
switchable = false,
|
|
73
77
|
property = 'bg',
|
|
74
78
|
messages,
|
|
79
|
+
suggestions = [],
|
|
75
80
|
} = defineProps<NexxtEditorColorProps>()
|
|
76
81
|
|
|
77
82
|
/**
|
|
@@ -150,6 +155,8 @@ watch(
|
|
|
150
155
|
:formats="['hex']"
|
|
151
156
|
:show-alpha="false"
|
|
152
157
|
:placeholder="copy.placeholder"
|
|
158
|
+
:suggestions="{ hex: suggestions }"
|
|
159
|
+
:suggestions-label="copy.suggestionsLabel"
|
|
153
160
|
/>
|
|
154
161
|
</NexxtEditorSettingsBlock>
|
|
155
162
|
</template>
|
|
@@ -19,6 +19,7 @@ import NexxtFloatingPanel from '../../atoms/FloatingPanel/FloatingPanel.vue'
|
|
|
19
19
|
import NexxtIcon from '../../atoms/Icon/Icon.vue'
|
|
20
20
|
|
|
21
21
|
export type ColorFormat = 'hex' | 'hsl' | 'rgb'
|
|
22
|
+
export type ColorSuggestions = Partial<Record<ColorFormat, string[]>>
|
|
22
23
|
|
|
23
24
|
export interface NexxtColorPickerProps {
|
|
24
25
|
label?: string
|
|
@@ -30,6 +31,10 @@ export interface NexxtColorPickerProps {
|
|
|
30
31
|
formats?: ColorFormat[]
|
|
31
32
|
fullWidth?: boolean
|
|
32
33
|
matchWidth?: boolean
|
|
34
|
+
/** Existing colours grouped by format; only suggestions for the active format are shown. */
|
|
35
|
+
suggestions?: ColorSuggestions
|
|
36
|
+
/** Accessible and visible label for the suggestions. */
|
|
37
|
+
suggestionsLabel?: string
|
|
33
38
|
}
|
|
34
39
|
|
|
35
40
|
defineOptions({ name: 'NexxtColorPicker' })
|
|
@@ -42,22 +47,62 @@ const props = withDefaults(defineProps<NexxtColorPickerProps>(), {
|
|
|
42
47
|
formats: () => ['hex'],
|
|
43
48
|
fullWidth: false,
|
|
44
49
|
matchWidth: false,
|
|
50
|
+
suggestions: () => ({}),
|
|
51
|
+
suggestionsLabel: 'Gebruikte kleuren',
|
|
45
52
|
})
|
|
46
53
|
|
|
47
54
|
const model = defineModel<string>({ default: '' })
|
|
48
55
|
|
|
49
|
-
const
|
|
50
|
-
|
|
56
|
+
const withoutAlpha = (color: Color) =>
|
|
57
|
+
color.alpha < 1 ? setChannelValue(color, 'alpha', 100) : color
|
|
58
|
+
|
|
59
|
+
const normalizeModelColor = (value: string) => {
|
|
60
|
+
const color = normalizeColor(value)
|
|
61
|
+
|
|
62
|
+
return props.showAlpha ? color : withoutAlpha(color)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const normalizeInputColor = (value: string) => {
|
|
66
|
+
const color = normalizeColor(value)
|
|
67
|
+
|
|
68
|
+
if (!props.showAlpha && color.alpha < 1) {
|
|
69
|
+
throw new Error('Transparantie is niet beschikbaar')
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return color
|
|
73
|
+
}
|
|
74
|
+
|
|
51
75
|
const activeFormatIndex = ref(0)
|
|
76
|
+
const activeFormat = computed<ColorFormat>(() => props.formats[activeFormatIndex.value] ?? 'hex')
|
|
77
|
+
const fmt = (color: Color, format: ColorFormat) => {
|
|
78
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
79
|
+
return colorToString(color, format as any)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const colorObj = ref<Color>(normalizeModelColor(model.value || '#3b82f6'))
|
|
83
|
+
const hexInputError = ref('')
|
|
52
84
|
const channelInputErrors = ref<Partial<Record<ColorChannel, string>>>({})
|
|
53
85
|
|
|
54
86
|
const hexColor = computed(() => colorToString(colorObj.value, 'hex'))
|
|
55
|
-
const activeFormat = computed<ColorFormat>(() => props.formats[activeFormatIndex.value] ?? 'hex')
|
|
56
87
|
const displayValue = computed(() => (model.value ? fmt(colorObj.value, activeFormat.value) : ''))
|
|
88
|
+
const colorSuggestions = computed(() => {
|
|
89
|
+
const values = new Set<string>()
|
|
57
90
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
91
|
+
props.suggestions[activeFormat.value]?.forEach((suggestion) => {
|
|
92
|
+
try {
|
|
93
|
+
const color = normalizeInputColor(suggestion)
|
|
94
|
+
values.add(fmt(color, activeFormat.value))
|
|
95
|
+
} catch {
|
|
96
|
+
// Consumers can derive suggestions from stored classes; ignore malformed or unsupported values safely.
|
|
97
|
+
}
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
return [...values]
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
const selectSuggestion = (suggestion: string) => {
|
|
104
|
+
colorObj.value = normalizeInputColor(suggestion)
|
|
105
|
+
model.value = fmt(colorObj.value, activeFormat.value)
|
|
61
106
|
}
|
|
62
107
|
|
|
63
108
|
const cycleFormat = (direction: 1 | -1) => {
|
|
@@ -67,14 +112,15 @@ const cycleFormat = (direction: 1 | -1) => {
|
|
|
67
112
|
}
|
|
68
113
|
|
|
69
114
|
const handleColorUpdate = (newColor: Color) => {
|
|
70
|
-
|
|
71
|
-
|
|
115
|
+
const updated = props.showAlpha ? newColor : withoutAlpha(newColor)
|
|
116
|
+
colorObj.value = updated
|
|
117
|
+
model.value = fmt(updated, activeFormat.value)
|
|
72
118
|
}
|
|
73
119
|
|
|
74
120
|
const handleHexInput = (event: Event) => {
|
|
75
121
|
const val = (event.target as HTMLInputElement).value
|
|
76
122
|
try {
|
|
77
|
-
colorObj.value =
|
|
123
|
+
colorObj.value = normalizeInputColor(val)
|
|
78
124
|
model.value = fmt(colorObj.value, activeFormat.value)
|
|
79
125
|
hexInputError.value = ''
|
|
80
126
|
} catch (e) {
|
|
@@ -103,12 +149,23 @@ watch(
|
|
|
103
149
|
(val) => {
|
|
104
150
|
if (!val) return
|
|
105
151
|
try {
|
|
106
|
-
colorObj.value =
|
|
152
|
+
colorObj.value = normalizeModelColor(val)
|
|
107
153
|
} catch {
|
|
108
154
|
// Ignore invalid color strings
|
|
109
155
|
}
|
|
110
156
|
},
|
|
111
157
|
)
|
|
158
|
+
|
|
159
|
+
watch(
|
|
160
|
+
() => props.showAlpha,
|
|
161
|
+
(showAlpha) => {
|
|
162
|
+
if (!showAlpha && colorObj.value.alpha < 1) {
|
|
163
|
+
const updated = withoutAlpha(colorObj.value)
|
|
164
|
+
colorObj.value = updated
|
|
165
|
+
model.value = fmt(updated, activeFormat.value)
|
|
166
|
+
}
|
|
167
|
+
},
|
|
168
|
+
)
|
|
112
169
|
</script>
|
|
113
170
|
|
|
114
171
|
<template>
|
|
@@ -386,6 +443,25 @@ watch(
|
|
|
386
443
|
</ColorFieldRoot>
|
|
387
444
|
</div>
|
|
388
445
|
</div>
|
|
446
|
+
<div v-if="colorSuggestions.length" class="flex flex-col gap-1">
|
|
447
|
+
<span class="extra-small-normal text-gray-500">{{ suggestionsLabel }}</span>
|
|
448
|
+
<div class="flex flex-wrap gap-1.5">
|
|
449
|
+
<button
|
|
450
|
+
v-for="suggestion in colorSuggestions"
|
|
451
|
+
:key="suggestion"
|
|
452
|
+
type="button"
|
|
453
|
+
class="rounded-md p-0.5 ring-gray-200 transition-shadow hover:ring-2 hover:ring-cornflower-blue-400 focus-visible:ring-2 focus-visible:ring-cornflower-blue-500 focus-visible:outline-none"
|
|
454
|
+
:aria-label="`Kies kleur ${suggestion}`"
|
|
455
|
+
@click="selectSuggestion(suggestion)"
|
|
456
|
+
>
|
|
457
|
+
<ColorSwatch
|
|
458
|
+
:color="suggestion"
|
|
459
|
+
class="size-6 rounded-sm shadow-[inset_0_0_0_1px_rgba(0,0,0,0.1)]"
|
|
460
|
+
:style="{ backgroundColor: 'var(--reka-color-swatch-color)' }"
|
|
461
|
+
/>
|
|
462
|
+
</button>
|
|
463
|
+
</div>
|
|
464
|
+
</div>
|
|
389
465
|
</div>
|
|
390
466
|
</template>
|
|
391
467
|
</NexxtFloatingPanel>
|