@nexxtmove/ui 1.52.0 → 1.54.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.
@@ -0,0 +1,65 @@
1
+ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
+
3
+ exports[`EditorAnchor markup > renders unchanged with a chosen cell 1`] = `
4
+ "<!--v-if-->
5
+ <!--
6
+ \`w-fit\`: the grid columns are fractions, so in block flow the ring would
7
+ stretch to the full width of the parent.
8
+ -->
9
+ <div role="radiogroup" aria-label="Plek van de afbeelding" aria-describedby="hint" class="grid w-fit grid-cols-3 gap-1 rounded-lg p-1 ring ring-gray-200">
10
+ <!--
11
+ No \`focus:outline-none\` next to the focus-visible outline: in Tailwind v4
12
+ it sets the outline style to none, which the focus-visible variant does
13
+ not set back, so the ring would never show. The outline is -600, not
14
+ -500: -500 reaches 2.78:1 on white, short of the 3:1 for non-text.
15
+ --><button type="button" role="radio" aria-checked="false" aria-label="Linksboven" tabindex="-1" class="flex size-7 items-center justify-center rounded-md outline-cornflower-blue-600 transition-colors duration-150 ease-out focus-visible:outline-2 motion-reduce:transition-none hover:bg-gray-100 cursor-pointer"><span aria-hidden="true" class="rounded-full transition-colors duration-150 ease-out motion-reduce:transition-none size-1.5 bg-gray-400"></span></button><button type="button" role="radio" aria-checked="false" aria-label="Boven" tabindex="-1" class="flex size-7 items-center justify-center rounded-md outline-cornflower-blue-600 transition-colors duration-150 ease-out focus-visible:outline-2 motion-reduce:transition-none hover:bg-gray-100 cursor-pointer"><span aria-hidden="true" class="rounded-full transition-colors duration-150 ease-out motion-reduce:transition-none size-1.5 bg-gray-400"></span></button><button type="button" role="radio" aria-checked="false" aria-label="Rechtsboven" tabindex="-1" class="flex size-7 items-center justify-center rounded-md outline-cornflower-blue-600 transition-colors duration-150 ease-out focus-visible:outline-2 motion-reduce:transition-none hover:bg-gray-100 cursor-pointer"><span aria-hidden="true" class="rounded-full transition-colors duration-150 ease-out motion-reduce:transition-none size-1.5 bg-gray-400"></span></button><button type="button" role="radio" aria-checked="false" aria-label="Links" tabindex="-1" class="flex size-7 items-center justify-center rounded-md outline-cornflower-blue-600 transition-colors duration-150 ease-out focus-visible:outline-2 motion-reduce:transition-none hover:bg-gray-100 cursor-pointer"><span aria-hidden="true" class="rounded-full transition-colors duration-150 ease-out motion-reduce:transition-none size-1.5 bg-gray-400"></span></button><button type="button" role="radio" aria-checked="false" aria-label="Midden" tabindex="-1" class="flex size-7 items-center justify-center rounded-md outline-cornflower-blue-600 transition-colors duration-150 ease-out focus-visible:outline-2 motion-reduce:transition-none hover:bg-gray-100 cursor-pointer"><span aria-hidden="true" class="rounded-full transition-colors duration-150 ease-out motion-reduce:transition-none size-1.5 bg-gray-400"></span></button><button type="button" role="radio" aria-checked="false" aria-label="Rechts" tabindex="-1" class="flex size-7 items-center justify-center rounded-md outline-cornflower-blue-600 transition-colors duration-150 ease-out focus-visible:outline-2 motion-reduce:transition-none hover:bg-gray-100 cursor-pointer"><span aria-hidden="true" class="rounded-full transition-colors duration-150 ease-out motion-reduce:transition-none size-1.5 bg-gray-400"></span></button><button type="button" role="radio" aria-checked="false" aria-label="Linksonder" tabindex="-1" class="flex size-7 items-center justify-center rounded-md outline-cornflower-blue-600 transition-colors duration-150 ease-out focus-visible:outline-2 motion-reduce:transition-none hover:bg-gray-100 cursor-pointer"><span aria-hidden="true" class="rounded-full transition-colors duration-150 ease-out motion-reduce:transition-none size-1.5 bg-gray-400"></span></button><button type="button" role="radio" aria-checked="false" aria-label="Onder" tabindex="-1" class="flex size-7 items-center justify-center rounded-md outline-cornflower-blue-600 transition-colors duration-150 ease-out focus-visible:outline-2 motion-reduce:transition-none hover:bg-gray-100 cursor-pointer"><span aria-hidden="true" class="rounded-full transition-colors duration-150 ease-out motion-reduce:transition-none size-1.5 bg-gray-400"></span></button><button type="button" role="radio" aria-checked="true" aria-label="Rechtsonder" tabindex="0" class="flex size-7 items-center justify-center rounded-md outline-cornflower-blue-600 transition-colors duration-150 ease-out focus-visible:outline-2 motion-reduce:transition-none bg-cornflower-blue-600 cursor-pointer"><span aria-hidden="true" class="rounded-full transition-colors duration-150 ease-out motion-reduce:transition-none size-2.5 bg-white"></span></button>
16
+ </div>"
17
+ `;
18
+
19
+ exports[`EditorAnchor markup > renders unchanged with a visible label 1`] = `
20
+ "<p id="v-0" class="mb-1.5 small-normal text-gray-900">Plek van de afbeelding</p>
21
+ <!--
22
+ \`w-fit\`: the grid columns are fractions, so in block flow the ring would
23
+ stretch to the full width of the parent.
24
+ -->
25
+ <div role="radiogroup" aria-labelledby="v-0" aria-describedby="hint" class="grid w-fit grid-cols-3 gap-1 rounded-lg p-1 ring ring-gray-200">
26
+ <!--
27
+ No \`focus:outline-none\` next to the focus-visible outline: in Tailwind v4
28
+ it sets the outline style to none, which the focus-visible variant does
29
+ not set back, so the ring would never show. The outline is -600, not
30
+ -500: -500 reaches 2.78:1 on white, short of the 3:1 for non-text.
31
+ --><button type="button" role="radio" aria-checked="false" aria-label="Linksboven" tabindex="-1" class="flex size-7 items-center justify-center rounded-md outline-cornflower-blue-600 transition-colors duration-150 ease-out focus-visible:outline-2 motion-reduce:transition-none hover:bg-gray-100 cursor-pointer"><span aria-hidden="true" class="rounded-full transition-colors duration-150 ease-out motion-reduce:transition-none size-1.5 bg-gray-400"></span></button><button type="button" role="radio" aria-checked="false" aria-label="Boven" tabindex="-1" class="flex size-7 items-center justify-center rounded-md outline-cornflower-blue-600 transition-colors duration-150 ease-out focus-visible:outline-2 motion-reduce:transition-none hover:bg-gray-100 cursor-pointer"><span aria-hidden="true" class="rounded-full transition-colors duration-150 ease-out motion-reduce:transition-none size-1.5 bg-gray-400"></span></button><button type="button" role="radio" aria-checked="false" aria-label="Rechtsboven" tabindex="-1" class="flex size-7 items-center justify-center rounded-md outline-cornflower-blue-600 transition-colors duration-150 ease-out focus-visible:outline-2 motion-reduce:transition-none hover:bg-gray-100 cursor-pointer"><span aria-hidden="true" class="rounded-full transition-colors duration-150 ease-out motion-reduce:transition-none size-1.5 bg-gray-400"></span></button><button type="button" role="radio" aria-checked="false" aria-label="Links" tabindex="-1" class="flex size-7 items-center justify-center rounded-md outline-cornflower-blue-600 transition-colors duration-150 ease-out focus-visible:outline-2 motion-reduce:transition-none hover:bg-gray-100 cursor-pointer"><span aria-hidden="true" class="rounded-full transition-colors duration-150 ease-out motion-reduce:transition-none size-1.5 bg-gray-400"></span></button><button type="button" role="radio" aria-checked="true" aria-label="Midden" tabindex="0" class="flex size-7 items-center justify-center rounded-md outline-cornflower-blue-600 transition-colors duration-150 ease-out focus-visible:outline-2 motion-reduce:transition-none bg-cornflower-blue-600 cursor-pointer"><span aria-hidden="true" class="rounded-full transition-colors duration-150 ease-out motion-reduce:transition-none size-2.5 bg-white"></span></button><button type="button" role="radio" aria-checked="false" aria-label="Rechts" tabindex="-1" class="flex size-7 items-center justify-center rounded-md outline-cornflower-blue-600 transition-colors duration-150 ease-out focus-visible:outline-2 motion-reduce:transition-none hover:bg-gray-100 cursor-pointer"><span aria-hidden="true" class="rounded-full transition-colors duration-150 ease-out motion-reduce:transition-none size-1.5 bg-gray-400"></span></button><button type="button" role="radio" aria-checked="false" aria-label="Linksonder" tabindex="-1" class="flex size-7 items-center justify-center rounded-md outline-cornflower-blue-600 transition-colors duration-150 ease-out focus-visible:outline-2 motion-reduce:transition-none hover:bg-gray-100 cursor-pointer"><span aria-hidden="true" class="rounded-full transition-colors duration-150 ease-out motion-reduce:transition-none size-1.5 bg-gray-400"></span></button><button type="button" role="radio" aria-checked="false" aria-label="Onder" tabindex="-1" class="flex size-7 items-center justify-center rounded-md outline-cornflower-blue-600 transition-colors duration-150 ease-out focus-visible:outline-2 motion-reduce:transition-none hover:bg-gray-100 cursor-pointer"><span aria-hidden="true" class="rounded-full transition-colors duration-150 ease-out motion-reduce:transition-none size-1.5 bg-gray-400"></span></button><button type="button" role="radio" aria-checked="false" aria-label="Rechtsonder" tabindex="-1" class="flex size-7 items-center justify-center rounded-md outline-cornflower-blue-600 transition-colors duration-150 ease-out focus-visible:outline-2 motion-reduce:transition-none hover:bg-gray-100 cursor-pointer"><span aria-hidden="true" class="rounded-full transition-colors duration-150 ease-out motion-reduce:transition-none size-1.5 bg-gray-400"></span></button>
32
+ </div>"
33
+ `;
34
+
35
+ exports[`EditorAnchor markup > renders unchanged with disabled 1`] = `
36
+ "<!--v-if-->
37
+ <!--
38
+ \`w-fit\`: the grid columns are fractions, so in block flow the ring would
39
+ stretch to the full width of the parent.
40
+ -->
41
+ <div role="radiogroup" aria-label="Plek van de afbeelding" aria-describedby="hint" aria-disabled="true" class="grid w-fit grid-cols-3 gap-1 rounded-lg p-1 ring ring-gray-200 opacity-50">
42
+ <!--
43
+ No \`focus:outline-none\` next to the focus-visible outline: in Tailwind v4
44
+ it sets the outline style to none, which the focus-visible variant does
45
+ not set back, so the ring would never show. The outline is -600, not
46
+ -500: -500 reaches 2.78:1 on white, short of the 3:1 for non-text.
47
+ --><button type="button" role="radio" aria-checked="true" aria-label="Linksboven" tabindex="0" disabled="" class="flex size-7 items-center justify-center rounded-md outline-cornflower-blue-600 transition-colors duration-150 ease-out focus-visible:outline-2 motion-reduce:transition-none bg-cornflower-blue-600 cursor-not-allowed"><span aria-hidden="true" class="rounded-full transition-colors duration-150 ease-out motion-reduce:transition-none size-2.5 bg-white"></span></button><button type="button" role="radio" aria-checked="false" aria-label="Boven" tabindex="-1" disabled="" class="flex size-7 items-center justify-center rounded-md outline-cornflower-blue-600 transition-colors duration-150 ease-out focus-visible:outline-2 motion-reduce:transition-none cursor-not-allowed"><span aria-hidden="true" class="rounded-full transition-colors duration-150 ease-out motion-reduce:transition-none size-1.5 bg-gray-400"></span></button><button type="button" role="radio" aria-checked="false" aria-label="Rechtsboven" tabindex="-1" disabled="" class="flex size-7 items-center justify-center rounded-md outline-cornflower-blue-600 transition-colors duration-150 ease-out focus-visible:outline-2 motion-reduce:transition-none cursor-not-allowed"><span aria-hidden="true" class="rounded-full transition-colors duration-150 ease-out motion-reduce:transition-none size-1.5 bg-gray-400"></span></button><button type="button" role="radio" aria-checked="false" aria-label="Links" tabindex="-1" disabled="" class="flex size-7 items-center justify-center rounded-md outline-cornflower-blue-600 transition-colors duration-150 ease-out focus-visible:outline-2 motion-reduce:transition-none cursor-not-allowed"><span aria-hidden="true" class="rounded-full transition-colors duration-150 ease-out motion-reduce:transition-none size-1.5 bg-gray-400"></span></button><button type="button" role="radio" aria-checked="false" aria-label="Midden" tabindex="-1" disabled="" class="flex size-7 items-center justify-center rounded-md outline-cornflower-blue-600 transition-colors duration-150 ease-out focus-visible:outline-2 motion-reduce:transition-none cursor-not-allowed"><span aria-hidden="true" class="rounded-full transition-colors duration-150 ease-out motion-reduce:transition-none size-1.5 bg-gray-400"></span></button><button type="button" role="radio" aria-checked="false" aria-label="Rechts" tabindex="-1" disabled="" class="flex size-7 items-center justify-center rounded-md outline-cornflower-blue-600 transition-colors duration-150 ease-out focus-visible:outline-2 motion-reduce:transition-none cursor-not-allowed"><span aria-hidden="true" class="rounded-full transition-colors duration-150 ease-out motion-reduce:transition-none size-1.5 bg-gray-400"></span></button><button type="button" role="radio" aria-checked="false" aria-label="Linksonder" tabindex="-1" disabled="" class="flex size-7 items-center justify-center rounded-md outline-cornflower-blue-600 transition-colors duration-150 ease-out focus-visible:outline-2 motion-reduce:transition-none cursor-not-allowed"><span aria-hidden="true" class="rounded-full transition-colors duration-150 ease-out motion-reduce:transition-none size-1.5 bg-gray-400"></span></button><button type="button" role="radio" aria-checked="false" aria-label="Onder" tabindex="-1" disabled="" class="flex size-7 items-center justify-center rounded-md outline-cornflower-blue-600 transition-colors duration-150 ease-out focus-visible:outline-2 motion-reduce:transition-none cursor-not-allowed"><span aria-hidden="true" class="rounded-full transition-colors duration-150 ease-out motion-reduce:transition-none size-1.5 bg-gray-400"></span></button><button type="button" role="radio" aria-checked="false" aria-label="Rechtsonder" tabindex="-1" disabled="" class="flex size-7 items-center justify-center rounded-md outline-cornflower-blue-600 transition-colors duration-150 ease-out focus-visible:outline-2 motion-reduce:transition-none cursor-not-allowed"><span aria-hidden="true" class="rounded-full transition-colors duration-150 ease-out motion-reduce:transition-none size-1.5 bg-gray-400"></span></button>
48
+ </div>"
49
+ `;
50
+
51
+ exports[`EditorAnchor markup > renders unchanged with no choice 1`] = `
52
+ "<!--v-if-->
53
+ <!--
54
+ \`w-fit\`: the grid columns are fractions, so in block flow the ring would
55
+ stretch to the full width of the parent.
56
+ -->
57
+ <div role="radiogroup" aria-label="Plek van de afbeelding" aria-describedby="hint" class="grid w-fit grid-cols-3 gap-1 rounded-lg p-1 ring ring-gray-200">
58
+ <!--
59
+ No \`focus:outline-none\` next to the focus-visible outline: in Tailwind v4
60
+ it sets the outline style to none, which the focus-visible variant does
61
+ not set back, so the ring would never show. The outline is -600, not
62
+ -500: -500 reaches 2.78:1 on white, short of the 3:1 for non-text.
63
+ --><button type="button" role="radio" aria-checked="false" aria-label="Linksboven" tabindex="-1" class="flex size-7 items-center justify-center rounded-md outline-cornflower-blue-600 transition-colors duration-150 ease-out focus-visible:outline-2 motion-reduce:transition-none hover:bg-gray-100 cursor-pointer"><span aria-hidden="true" class="rounded-full transition-colors duration-150 ease-out motion-reduce:transition-none size-1.5 bg-gray-400"></span></button><button type="button" role="radio" aria-checked="false" aria-label="Boven" tabindex="-1" class="flex size-7 items-center justify-center rounded-md outline-cornflower-blue-600 transition-colors duration-150 ease-out focus-visible:outline-2 motion-reduce:transition-none hover:bg-gray-100 cursor-pointer"><span aria-hidden="true" class="rounded-full transition-colors duration-150 ease-out motion-reduce:transition-none size-1.5 bg-gray-400"></span></button><button type="button" role="radio" aria-checked="false" aria-label="Rechtsboven" tabindex="-1" class="flex size-7 items-center justify-center rounded-md outline-cornflower-blue-600 transition-colors duration-150 ease-out focus-visible:outline-2 motion-reduce:transition-none hover:bg-gray-100 cursor-pointer"><span aria-hidden="true" class="rounded-full transition-colors duration-150 ease-out motion-reduce:transition-none size-1.5 bg-gray-400"></span></button><button type="button" role="radio" aria-checked="false" aria-label="Links" tabindex="-1" class="flex size-7 items-center justify-center rounded-md outline-cornflower-blue-600 transition-colors duration-150 ease-out focus-visible:outline-2 motion-reduce:transition-none hover:bg-gray-100 cursor-pointer"><span aria-hidden="true" class="rounded-full transition-colors duration-150 ease-out motion-reduce:transition-none size-1.5 bg-gray-400"></span></button><button type="button" role="radio" aria-checked="false" aria-label="Midden" tabindex="0" class="flex size-7 items-center justify-center rounded-md outline-cornflower-blue-600 transition-colors duration-150 ease-out focus-visible:outline-2 motion-reduce:transition-none hover:bg-gray-100 cursor-pointer"><span aria-hidden="true" class="rounded-full transition-colors duration-150 ease-out motion-reduce:transition-none size-1.5 bg-gray-400"></span></button><button type="button" role="radio" aria-checked="false" aria-label="Rechts" tabindex="-1" class="flex size-7 items-center justify-center rounded-md outline-cornflower-blue-600 transition-colors duration-150 ease-out focus-visible:outline-2 motion-reduce:transition-none hover:bg-gray-100 cursor-pointer"><span aria-hidden="true" class="rounded-full transition-colors duration-150 ease-out motion-reduce:transition-none size-1.5 bg-gray-400"></span></button><button type="button" role="radio" aria-checked="false" aria-label="Linksonder" tabindex="-1" class="flex size-7 items-center justify-center rounded-md outline-cornflower-blue-600 transition-colors duration-150 ease-out focus-visible:outline-2 motion-reduce:transition-none hover:bg-gray-100 cursor-pointer"><span aria-hidden="true" class="rounded-full transition-colors duration-150 ease-out motion-reduce:transition-none size-1.5 bg-gray-400"></span></button><button type="button" role="radio" aria-checked="false" aria-label="Onder" tabindex="-1" class="flex size-7 items-center justify-center rounded-md outline-cornflower-blue-600 transition-colors duration-150 ease-out focus-visible:outline-2 motion-reduce:transition-none hover:bg-gray-100 cursor-pointer"><span aria-hidden="true" class="rounded-full transition-colors duration-150 ease-out motion-reduce:transition-none size-1.5 bg-gray-400"></span></button><button type="button" role="radio" aria-checked="false" aria-label="Rechtsonder" tabindex="-1" class="flex size-7 items-center justify-center rounded-md outline-cornflower-blue-600 transition-colors duration-150 ease-out focus-visible:outline-2 motion-reduce:transition-none hover:bg-gray-100 cursor-pointer"><span aria-hidden="true" class="rounded-full transition-colors duration-150 ease-out motion-reduce:transition-none size-1.5 bg-gray-400"></span></button>
64
+ </div>"
65
+ `;
@@ -33,10 +33,25 @@ export const EDITOR_GAP_STEPS = [
33
33
  */
34
34
  export type EditorGapValue = `gap-${(typeof EDITOR_GAP_STEPS)[number]['token']}`
35
35
 
36
- /** Overridable copy of the value readout. Every key is optional. */
36
+ /** Overridable copy. Every key is optional. */
37
37
  export interface EditorGapMessages {
38
38
  /** Unit shown behind the value. Defaults to `'px'`. */
39
39
  unit?: string
40
+ /** The fixed option, with the `auto` model bound. Defaults to `'Vast'`. */
41
+ fixed?: string
42
+ /** The auto option, with the `auto` model bound. Defaults to `'Auto'`. */
43
+ auto?: string
44
+ /**
45
+ * Name of the slider while Auto is on, when the gap is a minimum rather than
46
+ * the gap itself. Defaults to `'Tussenruimte, minimaal'`; on Vast the slider
47
+ * is named by `label`.
48
+ */
49
+ minimumName?: string
50
+ /**
51
+ * Name of the Vast | Auto toolbar, so it is not named like the slider.
52
+ * Defaults to `'Soort tussenruimte'`.
53
+ */
54
+ modeName?: string
40
55
  }
41
56
 
42
57
  export interface NexxtEditorGapProps {
@@ -55,6 +70,10 @@ export interface NexxtEditorGapProps {
55
70
 
56
71
  const DEFAULT_MESSAGES = {
57
72
  unit: 'px',
73
+ fixed: 'Vast',
74
+ auto: 'Auto',
75
+ minimumName: 'Tussenruimte, minimaal',
76
+ modeName: 'Soort tussenruimte',
58
77
  } satisfies Required<EditorGapMessages>
59
78
  </script>
60
79
 
@@ -62,6 +81,7 @@ const DEFAULT_MESSAGES = {
62
81
  import { computed } from 'vue'
63
82
  import NexxtEditorSettingsBlock from '../../atoms/EditorSettingsBlock/EditorSettingsBlock.vue'
64
83
  import NexxtSlider from '../../../atoms/Slider/Slider.vue'
84
+ import NexxtToggleGroup from '../../../molecules/ToggleGroup/ToggleGroup.vue'
65
85
 
66
86
  defineOptions({ name: 'NexxtEditorGap' })
67
87
 
@@ -70,6 +90,38 @@ const { label = 'Tussenruimte', messages } = defineProps<NexxtEditorGapProps>()
70
90
  /** The selected gap, as the Tailwind class to apply. */
71
91
  const model = defineModel<EditorGapValue>({ required: true })
72
92
 
93
+ /**
94
+ * Optional: whether the main axis distributes the space (Auto). Only when it
95
+ * is bound does the block offer Vast | Auto above the slider; the slider then
96
+ * sets the minimum gap. The component writes nothing else for Auto, and the
97
+ * gap model does not change when it is switched.
98
+ */
99
+ // `default: undefined` on purpose: a boolean prop without a default is cast to
100
+ // `false` when it is absent, and then an unbound model could not be told apart
101
+ // from Auto switched off.
102
+ const auto = defineModel<boolean>('auto', { default: undefined })
103
+
104
+ /**
105
+ * Bound by the consumer: an unbound model stays `undefined`. The template
106
+ * renders two whole branches rather than `v-if`s inside one, so without it the
107
+ * markup is exactly what it always was, without even an empty comment node.
108
+ */
109
+ const offersAuto = computed(() => auto.value !== undefined)
110
+
111
+ const autoOptions = computed(() => [
112
+ { value: 'fixed', label: copy.value.fixed },
113
+ { value: 'auto', label: copy.value.auto },
114
+ ])
115
+
116
+ const autoChoice = computed({
117
+ get: () => (auto.value ? 'auto' : 'fixed'),
118
+ set: (value: string) => {
119
+ auto.value = value === 'auto'
120
+ },
121
+ })
122
+
123
+ const sliderName = computed(() => (auto.value ? copy.value.minimumName : label))
124
+
73
125
  const copy = computed(() => ({ ...DEFAULT_MESSAGES, ...messages }))
74
126
 
75
127
  const maxIndex = EDITOR_GAP_STEPS.length - 1
@@ -101,7 +153,29 @@ const displayValue = computed(() => `${EDITOR_GAP_STEPS[stepIndex.value].px} ${c
101
153
 
102
154
  <template>
103
155
  <NexxtEditorSettingsBlock :label="label">
104
- <div class="flex items-center gap-4">
156
+ <div v-if="offersAuto" class="flex flex-col gap-3">
157
+ <NexxtToggleGroup v-model="autoChoice" :options="autoOptions" :aria-label="copy.modeName" />
158
+
159
+ <!--
160
+ The slider and readout exactly as below, see the notes there, so they
161
+ keep their width and place on Vast, on Auto and without the model. Only
162
+ the slider's name changes with Auto.
163
+ -->
164
+ <div class="flex items-center gap-4">
165
+ <NexxtSlider
166
+ v-model="sliderValue"
167
+ :min="0"
168
+ :max="maxIndex"
169
+ :step="1"
170
+ :aria-label="sliderName"
171
+ :aria-valuetext="displayValue"
172
+ />
173
+ <span class="min-w-12 shrink-0 text-right small-normal whitespace-nowrap text-gray-700">
174
+ {{ displayValue }}
175
+ </span>
176
+ </div>
177
+ </div>
178
+ <div v-else class="flex items-center gap-4">
105
179
  <!--
106
180
  Slider's own `showValue` prints its model, which here is the step index
107
181
  rather than the gap, so the readout is rendered next to it instead. The
@@ -0,0 +1,103 @@
1
+ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
+
3
+ exports[`EditorGap markup without auto > renders unchanged with a value on the scale 1`] = `
4
+ "<div class="py-4">
5
+ <!--
6
+ Switch.vue sets \`inheritAttrs: false\` and binds \`$attrs\` on its sr-only
7
+ input, so a class on <Switch> never reaches its root <label>. The child
8
+ variant below styles that label from the header instead, which keeps the
9
+ whole row clickable and the accessible name on the real <label>. The gap
10
+ has to go on that label too: the header has a single child in both modes,
11
+ so its own \`gap\` would never apply and the spacing would silently fall
12
+ back to the \`gap-2\` that Switch.vue sets for itself.
13
+ -->
14
+ <div class="flex items-center [&amp;>label]:flex [&amp;>label]:w-full [&amp;>label]:gap-3">
15
+ <p class="small-normal text-gray-900">Tussenruimte</p>
16
+ </div>
17
+ <div class="grid grid-rows-[1fr]">
18
+ <!--
19
+ \`overflow-hidden\` is what makes the 0fr collapse clip, but it also clips
20
+ a focus ring on a full-width control in the slot, and the block has no
21
+ horizontal padding of its own. \`-mx-1\` widens the clipping box by 4px on
22
+ each side and the \`px-1\` below pushes the content back, so the ring has
23
+ room while the content keeps the same x position as the label.
24
+ -->
25
+ <div class="-mx-1 min-h-0 overflow-hidden opacity-100">
26
+ <!-- Padding sits on the inner element so nothing is left over at 0fr. -->
27
+ <div class="px-1 pt-1.5 pb-1">
28
+ <div class="flex items-center gap-4">
29
+ <!--
30
+ Slider's own \`showValue\` prints its model, which here is the step index
31
+ rather than the gap, so the readout is rendered next to it instead. The
32
+ thumb has the same problem in \`aria-valuenow\`, which \`aria-valuetext\`
33
+ overrides with the string the readout shows. The block label is a <p>,
34
+ not a <label>, so the name goes on the thumb explicitly.
35
+ -->
36
+ <div class="flex w-full flex-col gap-2">
37
+ <!--v-if-->
38
+ <div class="flex items-center gap-4"><span data-slider-impl="" dir="ltr" data-orientation="horizontal" style="--reka-slider-thumb-transform: translateX(-50%);" class="relative flex w-full touch-none items-center select-none cursor-pointer" aria-disabled="false"><span data-orientation="horizontal" class="relative w-full grow rounded-full bg-gray-200 h-1"><span data-orientation="horizontal" style="left: 0%; right: 57.14285714285714%;" class="absolute h-full rounded-full bg-cornflower-blue-500"></span></span><span data-reka-collection-item="" aria-label="Tussenruimte" aria-valuetext="24 px" class="block size-4 rounded-full border shadow-sm transition-colors border-gray-200 bg-white focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-cornflower-blue-500" role="slider" tabindex="0" data-orientation="horizontal" aria-valuemin="0" aria-valuemax="14" aria-orientation="horizontal" style="transform: var(--reka-slider-thumb-transform); position: absolute; left: calc(0% + 0px); display: none;"></span>
39
+ <!--v-if--></span>
40
+ <!--v-if-->
41
+ </div>
42
+ </div>
43
+ <!--
44
+ gray-700, not the gray-500 Slider uses for its own readout: axe puts
45
+ gray-500 on white at 1.84:1, well under the 4.5:1 it needs at this size.
46
+ --><span class="min-w-12 shrink-0 text-right small-normal whitespace-nowrap text-gray-700">24 px</span>
47
+ </div>
48
+ </div>
49
+ </div>
50
+ </div>
51
+ </div>"
52
+ `;
53
+
54
+ exports[`EditorGap markup without auto > renders unchanged with a value outside the scale 1`] = `
55
+ "<div class="py-4">
56
+ <!--
57
+ Switch.vue sets \`inheritAttrs: false\` and binds \`$attrs\` on its sr-only
58
+ input, so a class on <Switch> never reaches its root <label>. The child
59
+ variant below styles that label from the header instead, which keeps the
60
+ whole row clickable and the accessible name on the real <label>. The gap
61
+ has to go on that label too: the header has a single child in both modes,
62
+ so its own \`gap\` would never apply and the spacing would silently fall
63
+ back to the \`gap-2\` that Switch.vue sets for itself.
64
+ -->
65
+ <div class="flex items-center [&amp;>label]:flex [&amp;>label]:w-full [&amp;>label]:gap-3">
66
+ <p class="small-normal text-gray-900">Tussenruimte</p>
67
+ </div>
68
+ <div class="grid grid-rows-[1fr]">
69
+ <!--
70
+ \`overflow-hidden\` is what makes the 0fr collapse clip, but it also clips
71
+ a focus ring on a full-width control in the slot, and the block has no
72
+ horizontal padding of its own. \`-mx-1\` widens the clipping box by 4px on
73
+ each side and the \`px-1\` below pushes the content back, so the ring has
74
+ room while the content keeps the same x position as the label.
75
+ -->
76
+ <div class="-mx-1 min-h-0 overflow-hidden opacity-100">
77
+ <!-- Padding sits on the inner element so nothing is left over at 0fr. -->
78
+ <div class="px-1 pt-1.5 pb-1">
79
+ <div class="flex items-center gap-4">
80
+ <!--
81
+ Slider's own \`showValue\` prints its model, which here is the step index
82
+ rather than the gap, so the readout is rendered next to it instead. The
83
+ thumb has the same problem in \`aria-valuenow\`, which \`aria-valuetext\`
84
+ overrides with the string the readout shows. The block label is a <p>,
85
+ not a <label>, so the name goes on the thumb explicitly.
86
+ -->
87
+ <div class="flex w-full flex-col gap-2">
88
+ <!--v-if-->
89
+ <div class="flex items-center gap-4"><span data-slider-impl="" dir="ltr" data-orientation="horizontal" style="--reka-slider-thumb-transform: translateX(-50%);" class="relative flex w-full touch-none items-center select-none cursor-pointer" aria-disabled="false"><span data-orientation="horizontal" class="relative w-full grow rounded-full bg-gray-200 h-1"><span data-orientation="horizontal" style="left: 0%; right: 100%;" class="absolute h-full rounded-full bg-cornflower-blue-500"></span></span><span data-reka-collection-item="" aria-label="Tussenruimte" aria-valuetext="0 px" class="block size-4 rounded-full border shadow-sm transition-colors border-gray-200 bg-white focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-cornflower-blue-500" role="slider" tabindex="0" data-orientation="horizontal" aria-valuemin="0" aria-valuemax="14" aria-orientation="horizontal" style="transform: var(--reka-slider-thumb-transform); position: absolute; left: calc(0% + 0px); display: none;"></span>
90
+ <!--v-if--></span>
91
+ <!--v-if-->
92
+ </div>
93
+ </div>
94
+ <!--
95
+ gray-700, not the gray-500 Slider uses for its own readout: axe puts
96
+ gray-500 on white at 1.84:1, well under the 4.5:1 it needs at this size.
97
+ --><span class="min-w-12 shrink-0 text-right small-normal whitespace-nowrap text-gray-700">0 px</span>
98
+ </div>
99
+ </div>
100
+ </div>
101
+ </div>
102
+ </div>"
103
+ `;
@@ -77,6 +77,7 @@
77
77
  "NexxtEditorSettingsBlock": "components/editor/atoms/EditorSettingsBlock/EditorSettingsBlock.vue",
78
78
  "NexxtEditorSettingsGroup": "components/editor/atoms/EditorSettingsGroup/EditorSettingsGroup.vue",
79
79
  "NexxtEditorAlignItems": "components/editor/molecules/EditorAlignItems/EditorAlignItems.vue",
80
+ "NexxtEditorAlignmentGrid": "components/editor/molecules/EditorAlignmentGrid/EditorAlignmentGrid.vue",
80
81
  "NexxtEditorAnchor": "components/editor/molecules/EditorAnchor/EditorAnchor.vue",
81
82
  "NexxtEditorBackgroundSize": "components/editor/molecules/EditorBackgroundSize/EditorBackgroundSize.vue",
82
83
  "NexxtEditorBorder": "components/editor/molecules/EditorBorder/EditorBorder.vue",
package/src/index.ts CHANGED
@@ -9,6 +9,7 @@ export type {
9
9
  TimelinePhase,
10
10
  TimelineResponse,
11
11
  } from './components/molecules/TimelineEvent/TimelineTypes'
12
+ export { ENERGY_LABEL_COLORS } from './components/atoms/EnergyLabel/EnergyLabel.vue'
12
13
  export type { EnergyLabelLetter } from './components/atoms/EnergyLabel/EnergyLabel.vue'
13
14
  export type { IconType, NexxtIconName } from './components/atoms/Icon/Icon.vue'
14
15
  export type { CustomIconName } from './components/atoms/Icon/customIcons'
@@ -34,6 +35,14 @@ export type {
34
35
  NexxtPlaceholderInputMessages,
35
36
  NexxtPlaceholderInputProps,
36
37
  } from './components/molecules/PlaceholderInput/PlaceholderInput.vue'
38
+ export { EDITOR_ALIGNMENT_POSITIONS } from './components/editor/molecules/EditorAlignmentGrid/EditorAlignmentGrid.vue'
39
+ export type {
40
+ EditorAlignmentDirection,
41
+ EditorAlignmentGridMessages,
42
+ EditorAlignmentMode,
43
+ EditorAlignmentPosition,
44
+ EditorAlignmentValue,
45
+ } from './components/editor/molecules/EditorAlignmentGrid/EditorAlignmentGrid.vue'
37
46
  export { EDITOR_ALIGN_ITEMS_OPTIONS } from './components/editor/molecules/EditorAlignItems/EditorAlignItems.vue'
38
47
  export type {
39
48
  EditorAlignItemsMessages,
@@ -211,6 +220,7 @@ export { default as NexxtDatePicker } from './components/organisms/DatePicker/Da
211
220
  export { default as NexxtDoughnutChart } from './components/molecules/DoughnutChart/DoughnutChart.vue'
212
221
  export { default as NexxtDropdownButton } from './components/organisms/DropdownButton/DropdownButton.vue'
213
222
  export { default as NexxtEditorAlignItems } from './components/editor/molecules/EditorAlignItems/EditorAlignItems.vue'
223
+ export { default as NexxtEditorAlignmentGrid } from './components/editor/molecules/EditorAlignmentGrid/EditorAlignmentGrid.vue'
214
224
  export { default as NexxtEditorAnchor } from './components/editor/molecules/EditorAnchor/EditorAnchor.vue'
215
225
  export { default as NexxtEditorBackgroundSize } from './components/editor/molecules/EditorBackgroundSize/EditorBackgroundSize.vue'
216
226
  export { default as NexxtEditorBorder } from './components/editor/molecules/EditorBorder/EditorBorder.vue'
package/src/nuxt.ts CHANGED
@@ -144,8 +144,12 @@ const module: NuxtModule<ModuleOptions> = defineNuxtModule<ModuleOptions>({
144
144
 
145
145
  // Compiled component `<style>` blocks (scoped rules, keyframes). Not
146
146
  // covered by the Tailwind entry above — that only scans the .vue source
147
- // for class names. Pushed last so it stays unlayered and wins over
148
- // Tailwind's layers.
147
+ // for class names. Pushed after the Tailwind entry on purpose. Most rules
148
+ // are unlayered and beat Tailwind's layers, but EnergyLabel puts its
149
+ // sizing defaults in `@layer components`, so that classes passed to the
150
+ // component can override them. Because `@import "tailwindcss"` loads
151
+ // first, the layer order is already theme, base, components, utilities,
152
+ // and utilities win.
149
153
  const componentCSS = resolve('./ui.css')
150
154
 
151
155
  if (existsSync(componentCSS) && !hasComponentCSS(nuxt.options.css, componentCSS)) {