@nexxtmove/ui 1.22.0 → 1.24.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/nuxt.js CHANGED
@@ -66,12 +66,14 @@ var s = {
66
66
  NexxtVideoPlayer: "components/organisms/VideoPlayer/VideoPlayer.vue",
67
67
  NexxtEditorSettingsBlock: "components/editor/atoms/EditorSettingsBlock/EditorSettingsBlock.vue",
68
68
  NexxtEditorAlignItems: "components/editor/molecules/EditorAlignItems/EditorAlignItems.vue",
69
+ NexxtEditorColor: "components/editor/molecules/EditorColor/EditorColor.vue",
69
70
  NexxtEditorFlexDirection: "components/editor/molecules/EditorFlexDirection/EditorFlexDirection.vue",
70
71
  NexxtEditorGap: "components/editor/molecules/EditorGap/EditorGap.vue",
71
72
  NexxtEditorHeadingType: "components/editor/molecules/EditorHeadingType/EditorHeadingType.vue",
72
73
  NexxtEditorJustifyContent: "components/editor/molecules/EditorJustifyContent/EditorJustifyContent.vue",
73
74
  NexxtEditorPadding: "components/editor/molecules/EditorPadding/EditorPadding.vue",
74
- NexxtEditorRadius: "components/editor/molecules/EditorRadius/EditorRadius.vue"
75
+ NexxtEditorRadius: "components/editor/molecules/EditorRadius/EditorRadius.vue",
76
+ NexxtEditorTextAlign: "components/editor/molecules/EditorTextAlign/EditorTextAlign.vue"
75
77
  }, c = ["@nexxtmove/ui/ui.css", "@nexxtmove/ui/dist/ui.css"], l = (e, t) => e.some((e) => e === t || c.includes(e)), u = i({
76
78
  meta: {
77
79
  name: "@nexxtmove/ui",
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@nexxtmove/ui",
3
3
  "type": "module",
4
- "version": "1.22.0",
4
+ "version": "1.24.0",
5
5
  "exports": {
6
6
  ".": {
7
7
  "types": "./dist/index.d.ts",
@@ -0,0 +1,155 @@
1
+ <script lang="ts">
2
+ /**
3
+ * The CSS property the chosen colour is written to. It is the prefix of the
4
+ * Tailwind class the model carries, so it decides whether the colour becomes a
5
+ * background or a text colour. `border` is deliberately not offered: a border
6
+ * is colour plus width, and that pair gets a component of its own.
7
+ */
8
+ export type EditorColorProperty = 'bg' | 'text'
9
+
10
+ /**
11
+ * The colour as the editor needs it: a Tailwind arbitrary-value class such as
12
+ * `'bg-[#1777EA]'`, so the consumer can put the model value straight on the
13
+ * element it styles.
14
+ */
15
+ export type EditorColorValue = `${EditorColorProperty}-[#${string}]`
16
+
17
+ /** Overridable copy of the picker. Every key is optional. */
18
+ export interface EditorColorMessages {
19
+ /** Text shown while no colour is set. Defaults to `'Kies kleur...'`. */
20
+ placeholder?: string
21
+ }
22
+
23
+ export interface NexxtEditorColorProps {
24
+ /**
25
+ * Header text of the settings block. Defaults to the Dutch `'Kleur'`; the
26
+ * library has no i18n layer, so a consumer in another language passes its
27
+ * own copy in.
28
+ */
29
+ label?: string
30
+ /**
31
+ * Render a switch next to the label that collapses and expands the picker.
32
+ * The switch state is the `enabled` model; the colour itself is kept while
33
+ * the block is collapsed.
34
+ */
35
+ switchable?: boolean
36
+ /**
37
+ * The prefix of the class that is written: `'bg'` for a background colour,
38
+ * `'text'` for a text colour. Changing it rewrites the current value, so the
39
+ * model never disagrees with this prop.
40
+ */
41
+ property?: EditorColorProperty
42
+ /**
43
+ * Copy of the picker. Merged per key, so overriding one key keeps the
44
+ * defaults of the others. Copy is never translated inside the component.
45
+ */
46
+ messages?: EditorColorMessages
47
+ }
48
+
49
+ const DEFAULT_MESSAGES = {
50
+ placeholder: 'Kies kleur...',
51
+ } satisfies Required<EditorColorMessages>
52
+
53
+ /**
54
+ * A full model value: one of the two prefixes, then a 6- or 8-digit hex between
55
+ * brackets. A `border-[…]` class is therefore read as unparseable, the same as
56
+ * any other class this component does not own. The
57
+ * alpha pair is accepted on read so a stored `#1777EA80` survives a round trip,
58
+ * even though the picker itself is opened without an alpha channel here.
59
+ */
60
+ const VALUE_PATTERN = /^(bg|text)-\[(#[0-9A-Fa-f]{6}(?:[0-9A-Fa-f]{2})?)\]$/
61
+ </script>
62
+
63
+ <script lang="ts" setup>
64
+ import { computed, watch } from 'vue'
65
+ import NexxtEditorSettingsBlock from '../../atoms/EditorSettingsBlock/EditorSettingsBlock.vue'
66
+ import NexxtColorPicker from '../../../organisms/ColorPicker/ColorPicker.vue'
67
+
68
+ defineOptions({ name: 'NexxtEditorColor' })
69
+
70
+ const {
71
+ label = 'Kleur',
72
+ switchable = false,
73
+ property = 'bg',
74
+ messages,
75
+ } = defineProps<NexxtEditorColorProps>()
76
+
77
+ /**
78
+ * The chosen colour, as the Tailwind class to apply.
79
+ *
80
+ * Beware: Tailwind only generates an arbitrary class for text it finds in the
81
+ * source at build time. A value that arrives at runtime -- from a database,
82
+ * say -- therefore gets no CSS of its own; the consumer either translates it
83
+ * into an inline style or safelists the classes it can produce. The hex is
84
+ * always parseable out of the string, which is what makes that translation
85
+ * possible.
86
+ */
87
+ const model = defineModel<EditorColorValue>({ required: true })
88
+
89
+ /** Open state of the block. Only has an effect when `switchable` is `true`. */
90
+ const enabled = defineModel<boolean>('enabled', { default: true })
91
+
92
+ const copy = computed(() => ({ ...DEFAULT_MESSAGES, ...messages }))
93
+
94
+ /**
95
+ * The `#` is part of the model's type, so a plain `string` cannot be written
96
+ * into it. This narrows the picker's output and the regex capture to the shape
97
+ * the model promises, without a cast.
98
+ */
99
+ const isHex = (value: string): value is `#${string}` => value.startsWith('#')
100
+
101
+ /** The hex out of the model, or an empty string when it is not a colour class. */
102
+ const hex = computed(() => {
103
+ const captured = VALUE_PATTERN.exec(model.value)?.[2]
104
+ return captured && isHex(captured) ? captured : ''
105
+ })
106
+
107
+ // An unparseable value -- empty, a token class such as `bg-red-500`, or copy
108
+ // from before this component -- leaves the picker empty and its placeholder
109
+ // visible, rather than being reported as a problem: the first pick writes a
110
+ // valid class back.
111
+ const picked = computed({
112
+ get: () => hex.value,
113
+ // The picker clears its own value in no flow it offers, but an empty string
114
+ // would still wipe a perfectly good colour, so it is dropped rather than
115
+ // written through. The picker keeps rendering the model's colour.
116
+ set: (value: string) => {
117
+ if (isHex(value)) model.value = `${property}-[${value}]`
118
+ },
119
+ })
120
+
121
+ // `property` is a prop, so the parent can flip a background setting into a
122
+ // text-colour one while a value is already set. Rewriting the prefix here is
123
+ // what keeps the stored class and the prop from drifting apart.
124
+ watch(
125
+ () => property,
126
+ (next) => {
127
+ if (hex.value) model.value = `${next}-[${hex.value}]`
128
+ },
129
+ )
130
+ </script>
131
+
132
+ <template>
133
+ <NexxtEditorSettingsBlock v-model="enabled" :label="label" :switchable="switchable">
134
+ <!--
135
+ The block label is a <p> (or the switch's own label), so the picker gets
136
+ no `label` of its own -- that would print the heading twice. Its trigger
137
+ falls back to the placeholder for its accessible name, which is why the
138
+ placeholder is the one piece of copy this component passes down.
139
+ -->
140
+ <!--
141
+ `matchWidth` makes the panel as wide as the full-width trigger instead of
142
+ the fixed `w-70` it defaults to. It works by setting a `min-width` on the
143
+ floating element, so a panel in a container narrower than its content
144
+ would still be wider than the trigger; in the settings column it is not.
145
+ -->
146
+ <NexxtColorPicker
147
+ v-model="picked"
148
+ full-width
149
+ match-width
150
+ :formats="['hex']"
151
+ :show-alpha="false"
152
+ :placeholder="copy.placeholder"
153
+ />
154
+ </NexxtEditorSettingsBlock>
155
+ </template>
@@ -0,0 +1,86 @@
1
+ <script lang="ts">
2
+ /**
3
+ * The text alignment as the editor needs it: the Tailwind class itself, so the
4
+ * consumer can put the model value straight on the element it styles.
5
+ */
6
+ export type EditorTextAlignValue = 'text-left' | 'text-center' | 'text-right' | 'text-justify'
7
+
8
+ /** Overridable copy for the four options. Every key is optional. */
9
+ export interface EditorTextAlignMessages {
10
+ /** Accessible name of the `text-left` option. Defaults to `'Links'`. */
11
+ left?: string
12
+ /** Accessible name of the `text-center` option. Defaults to `'Midden'`. */
13
+ center?: string
14
+ /** Accessible name of the `text-right` option. Defaults to `'Rechts'`. */
15
+ right?: string
16
+ /** Accessible name of the `text-justify` option. Defaults to `'Uitvullen'`. */
17
+ justify?: string
18
+ }
19
+
20
+ export interface NexxtEditorTextAlignProps {
21
+ /**
22
+ * Header text of the settings block. Defaults to the Dutch
23
+ * `'Tekst uitlijning'`; the library has no i18n layer, so a consumer in
24
+ * another language passes its own copy in.
25
+ */
26
+ label?: string
27
+ /**
28
+ * Copy of the four options. The options are icon-only and show no tooltip, so
29
+ * this copy is purely the accessible name of each button. Defaults are Dutch
30
+ * and are merged per key, so overriding one keeps the defaults of the others.
31
+ * Copy is never translated inside the component.
32
+ */
33
+ messages?: EditorTextAlignMessages
34
+ }
35
+
36
+ const DEFAULT_MESSAGES = {
37
+ left: 'Links',
38
+ center: 'Midden',
39
+ right: 'Rechts',
40
+ justify: 'Uitvullen',
41
+ } satisfies Required<EditorTextAlignMessages>
42
+ </script>
43
+
44
+ <script lang="ts" setup>
45
+ import { computed } from 'vue'
46
+ import NexxtEditorSettingsBlock from '../../atoms/EditorSettingsBlock/EditorSettingsBlock.vue'
47
+ import NexxtToggleGroup from '../../../molecules/ToggleGroup/ToggleGroup.vue'
48
+ import type { NexxtToggleGroupOption } from '../../../molecules/ToggleGroup/ToggleGroup.vue'
49
+
50
+ defineOptions({ name: 'NexxtEditorTextAlign' })
51
+
52
+ const { label = 'Tekst uitlijning', messages } = defineProps<NexxtEditorTextAlignProps>()
53
+
54
+ /** The selected text alignment, as the Tailwind class to apply. */
55
+ const model = defineModel<EditorTextAlignValue>({ required: true })
56
+
57
+ const copy = computed(() => ({ ...DEFAULT_MESSAGES, ...messages }))
58
+
59
+ // Icon-only and deliberately without tooltips: four labels do not fit side by
60
+ // side at the width the editor sidebar gives this block, and the icons are
61
+ // conventional enough to stand on their own. The copy travels as `ariaLabel`,
62
+ // so the buttons still have a name for assistive technology.
63
+ const options = computed<NexxtToggleGroupOption[]>(() => [
64
+ { value: 'text-left', icon: 'align-left', ariaLabel: copy.value.left },
65
+ { value: 'text-center', icon: 'align-center', ariaLabel: copy.value.center },
66
+ { value: 'text-right', icon: 'align-right', ariaLabel: copy.value.right },
67
+ { value: 'text-justify', icon: 'align-justify', ariaLabel: copy.value.justify },
68
+ ])
69
+
70
+ // ToggleGroup types its model as the open `string` of its options, so writing
71
+ // straight back into the narrower model is a type error. The setter is the one
72
+ // place that narrowing happens, and it is safe: the only values ToggleGroup can
73
+ // emit are the option values built above.
74
+ const selected = computed({
75
+ get: () => model.value,
76
+ set: (value: string) => {
77
+ model.value = value as EditorTextAlignValue
78
+ },
79
+ })
80
+ </script>
81
+
82
+ <template>
83
+ <NexxtEditorSettingsBlock :label="label">
84
+ <NexxtToggleGroup v-model="selected" :options="options" />
85
+ </NexxtEditorSettingsBlock>
86
+ </template>
@@ -63,10 +63,12 @@
63
63
  "NexxtVideoPlayer": "components/organisms/VideoPlayer/VideoPlayer.vue",
64
64
  "NexxtEditorSettingsBlock": "components/editor/atoms/EditorSettingsBlock/EditorSettingsBlock.vue",
65
65
  "NexxtEditorAlignItems": "components/editor/molecules/EditorAlignItems/EditorAlignItems.vue",
66
+ "NexxtEditorColor": "components/editor/molecules/EditorColor/EditorColor.vue",
66
67
  "NexxtEditorFlexDirection": "components/editor/molecules/EditorFlexDirection/EditorFlexDirection.vue",
67
68
  "NexxtEditorGap": "components/editor/molecules/EditorGap/EditorGap.vue",
68
69
  "NexxtEditorHeadingType": "components/editor/molecules/EditorHeadingType/EditorHeadingType.vue",
69
70
  "NexxtEditorJustifyContent": "components/editor/molecules/EditorJustifyContent/EditorJustifyContent.vue",
70
71
  "NexxtEditorPadding": "components/editor/molecules/EditorPadding/EditorPadding.vue",
71
- "NexxtEditorRadius": "components/editor/molecules/EditorRadius/EditorRadius.vue"
72
+ "NexxtEditorRadius": "components/editor/molecules/EditorRadius/EditorRadius.vue",
73
+ "NexxtEditorTextAlign": "components/editor/molecules/EditorTextAlign/EditorTextAlign.vue"
72
74
  }
package/src/index.ts CHANGED
@@ -25,10 +25,23 @@ export type {
25
25
  EditorGapMessages,
26
26
  EditorGapValue,
27
27
  } from './components/editor/molecules/EditorGap/EditorGap.vue'
28
+ export type {
29
+ EditorColorMessages,
30
+ EditorColorProperty,
31
+ EditorColorValue,
32
+ } from './components/editor/molecules/EditorColor/EditorColor.vue'
33
+ export type {
34
+ EditorHeadingTypeMessages,
35
+ EditorHeadingTypeValue,
36
+ } from './components/editor/molecules/EditorHeadingType/EditorHeadingType.vue'
28
37
  export type {
29
38
  EditorRadiusMessages,
30
39
  EditorRadiusValue,
31
40
  } from './components/editor/molecules/EditorRadius/EditorRadius.vue'
41
+ export type {
42
+ EditorTextAlignMessages,
43
+ EditorTextAlignValue,
44
+ } from './components/editor/molecules/EditorTextAlign/EditorTextAlign.vue'
32
45
  export type {
33
46
  EditorJustifyContentMessages,
34
47
  EditorJustifyContentValue,
@@ -58,6 +71,7 @@ export { default as NexxtDatePicker } from './components/organisms/DatePicker/Da
58
71
  export { default as NexxtDoughnutChart } from './components/molecules/DoughnutChart/DoughnutChart.vue'
59
72
  export { default as NexxtDropdownButton } from './components/organisms/DropdownButton/DropdownButton.vue'
60
73
  export { default as NexxtEditorAlignItems } from './components/editor/molecules/EditorAlignItems/EditorAlignItems.vue'
74
+ export { default as NexxtEditorColor } from './components/editor/molecules/EditorColor/EditorColor.vue'
61
75
  export { default as NexxtEditorFlexDirection } from './components/editor/molecules/EditorFlexDirection/EditorFlexDirection.vue'
62
76
  export { default as NexxtEditorGap } from './components/editor/molecules/EditorGap/EditorGap.vue'
63
77
  export { default as NexxtEditorHeadingType } from './components/editor/molecules/EditorHeadingType/EditorHeadingType.vue'
@@ -65,6 +79,7 @@ export { default as NexxtEditorJustifyContent } from './components/editor/molecu
65
79
  export { default as NexxtEditorPadding } from './components/editor/molecules/EditorPadding/EditorPadding.vue'
66
80
  export { default as NexxtEditorRadius } from './components/editor/molecules/EditorRadius/EditorRadius.vue'
67
81
  export { default as NexxtEditorSettingsBlock } from './components/editor/atoms/EditorSettingsBlock/EditorSettingsBlock.vue'
82
+ export { default as NexxtEditorTextAlign } from './components/editor/molecules/EditorTextAlign/EditorTextAlign.vue'
68
83
  export { default as NexxtEnergyLabel } from './components/atoms/EnergyLabel/EnergyLabel.vue'
69
84
  export { default as NexxtFloatingPanel } from './components/atoms/FloatingPanel/FloatingPanel.vue'
70
85
  export { default as NexxtHeader } from './components/organisms/Header/Header.vue'