@nexxtmove/ui 1.28.0 → 1.29.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
@@ -76,6 +76,7 @@ var s = {
76
76
  NexxtEditorGap: "components/editor/molecules/EditorGap/EditorGap.vue",
77
77
  NexxtEditorHeadingType: "components/editor/molecules/EditorHeadingType/EditorHeadingType.vue",
78
78
  NexxtEditorJustifyContent: "components/editor/molecules/EditorJustifyContent/EditorJustifyContent.vue",
79
+ NexxtEditorMaxWidth: "components/editor/molecules/EditorMaxWidth/EditorMaxWidth.vue",
79
80
  NexxtEditorPadding: "components/editor/molecules/EditorPadding/EditorPadding.vue",
80
81
  NexxtEditorRadius: "components/editor/molecules/EditorRadius/EditorRadius.vue",
81
82
  NexxtEditorTextAlign: "components/editor/molecules/EditorTextAlign/EditorTextAlign.vue"
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@nexxtmove/ui",
3
3
  "type": "module",
4
- "version": "1.28.0",
4
+ "version": "1.29.0",
5
5
  "exports": {
6
6
  ".": {
7
7
  "types": "./dist/index.d.ts",
@@ -66,7 +66,12 @@ const iconSizeClasses: Record<SwitchSize, string> = {
66
66
  squashes the toggle the moment a sibling label wraps onto a second line.
67
67
  -->
68
68
  <span :class="['relative inline-block shrink-0', trackClasses[size]]">
69
- <input v-bind="$attrs" type="checkbox" class="peer sr-only" v-model="model" />
69
+ <!--
70
+ `role="switch"` on a native checkbox: it is on/off rather than
71
+ selected/unselected, and the checked state is still the input's own, so
72
+ no `aria-checked` is set -- the browser derives it from the checkbox.
73
+ -->
74
+ <input v-bind="$attrs" type="checkbox" role="switch" class="peer sr-only" v-model="model" />
70
75
  <span
71
76
  :class="[
72
77
  'absolute inset-0 overflow-hidden rounded-[30px] border transition-colors duration-200',
@@ -0,0 +1,146 @@
1
+ <script lang="ts">
2
+ /**
3
+ * The steps the slider can land on: the `max-w-*` sizes, with the px numbers
4
+ * mirroring their values (rem × 16). These are Tailwind's own container sizes,
5
+ * not Figma tokens -- `tailwind/` defines no `--container-*`, so the token sync
6
+ * cannot move them. The desync to watch for is a Tailwind major that renames or
7
+ * renumbers the scale: nothing here fails when that happens, the numbers just
8
+ * stop matching the CSS, so this list has to be checked by hand on an upgrade.
9
+ */
10
+ export const EDITOR_MAX_WIDTH_STEPS = [
11
+ { token: 'xs', px: 320 },
12
+ { token: 'sm', px: 384 },
13
+ { token: 'md', px: 448 },
14
+ { token: 'lg', px: 512 },
15
+ { token: 'xl', px: 576 },
16
+ { token: '2xl', px: 672 },
17
+ { token: '3xl', px: 768 },
18
+ { token: '4xl', px: 896 },
19
+ { token: '5xl', px: 1024 },
20
+ { token: '6xl', px: 1152 },
21
+ { token: '7xl', px: 1280 },
22
+ ] as const
23
+
24
+ /**
25
+ * The maximum width as the editor needs it: the Tailwind class itself, so the
26
+ * consumer can put the model value straight on the element it styles.
27
+ */
28
+ export type EditorMaxWidthValue = `max-w-${(typeof EDITOR_MAX_WIDTH_STEPS)[number]['token']}`
29
+
30
+ /** Overridable copy of the value readout. Every key is optional. */
31
+ export interface EditorMaxWidthMessages {
32
+ /**
33
+ * Unit shown behind the value. Defaults to `'px'`. Keep it to a short symbol
34
+ * such as `'px'` or `'pt'`: the readout is only as wide as `1280 px` needs,
35
+ * and a spelled-out unit grows it and pushes the slider in.
36
+ */
37
+ unit?: string
38
+ }
39
+
40
+ export interface NexxtEditorMaxWidthProps {
41
+ /**
42
+ * Header text of the settings block. Defaults to the Dutch
43
+ * `'Vaste breedte'` -- switched off the page runs full width, so the setting
44
+ * is the fixed width, not a maximum. The library has no i18n layer, so a
45
+ * consumer in another language passes its own copy in.
46
+ */
47
+ label?: string
48
+ /**
49
+ * Copy of the value readout. Merged per key, so overriding one key keeps the
50
+ * defaults of the others. Copy is never translated inside the component.
51
+ */
52
+ messages?: EditorMaxWidthMessages
53
+ }
54
+
55
+ const DEFAULT_MESSAGES = {
56
+ unit: 'px',
57
+ } satisfies Required<EditorMaxWidthMessages>
58
+ </script>
59
+
60
+ <script lang="ts" setup>
61
+ import { computed } from 'vue'
62
+ import NexxtEditorSettingsBlock from '../../atoms/EditorSettingsBlock/EditorSettingsBlock.vue'
63
+ import NexxtSlider from '../../../atoms/Slider/Slider.vue'
64
+
65
+ defineOptions({ name: 'NexxtEditorMaxWidth' })
66
+
67
+ const { label = 'Vaste breedte', messages } = defineProps<NexxtEditorMaxWidthProps>()
68
+
69
+ /** The selected maximum width, as the Tailwind class to apply. */
70
+ const model = defineModel<EditorMaxWidthValue>({ required: true })
71
+
72
+ /**
73
+ * Open state of the block. Switched off means the page runs full width, which
74
+ * is the absence of a class rather than a value of its own: the consumer simply
75
+ * writes no `max-w-*` then. The chosen width is kept while the block is
76
+ * collapsed, so switching back on restores it.
77
+ */
78
+ const enabled = defineModel<boolean>('enabled', { default: true })
79
+
80
+ const copy = computed(() => ({ ...DEFAULT_MESSAGES, ...messages }))
81
+
82
+ const maxIndex = EDITOR_MAX_WIDTH_STEPS.length - 1
83
+
84
+ /**
85
+ * Position of the current value on the step list. A value that is not on the
86
+ * list -- a stored `max-w-prose` from before this component, say -- falls back
87
+ * to the first step instead of leaving the thumb off the track; the first
88
+ * change writes a valid class back.
89
+ */
90
+ const stepIndex = computed(() =>
91
+ Math.max(
92
+ 0,
93
+ EDITOR_MAX_WIDTH_STEPS.findIndex(({ token }) => `max-w-${token}` === model.value),
94
+ ),
95
+ )
96
+
97
+ // The slider moves over the index, not over the px value, so its model is
98
+ // translated both ways here.
99
+ const sliderValue = computed({
100
+ get: () => [stepIndex.value],
101
+ set: ([index]) => {
102
+ model.value = `max-w-${EDITOR_MAX_WIDTH_STEPS[index].token}`
103
+ },
104
+ })
105
+
106
+ const displayValue = computed(
107
+ () => `${EDITOR_MAX_WIDTH_STEPS[stepIndex.value].px} ${copy.value.unit}`,
108
+ )
109
+ </script>
110
+
111
+ <template>
112
+ <NexxtEditorSettingsBlock v-model="enabled" :label="label" switchable>
113
+ <div class="flex items-center gap-4">
114
+ <!--
115
+ Slider's own `showValue` prints its model, which here is the step index
116
+ rather than the width, so the readout is rendered next to it instead.
117
+ The thumb has the same problem in `aria-valuenow`, which
118
+ `aria-valuetext` overrides with the string the readout shows. The block
119
+ label is the switch's own label, not a <label> for the slider, so the
120
+ name goes on the thumb explicitly.
121
+ -->
122
+ <NexxtSlider
123
+ v-model="sliderValue"
124
+ :min="0"
125
+ :max="maxIndex"
126
+ :step="1"
127
+ :aria-label="label"
128
+ :aria-valuetext="displayValue"
129
+ />
130
+
131
+ <!--
132
+ `min-w-16` rather than the `min-w-12` of the narrower editor sliders:
133
+ `1280 px` does not fit in 12. That plus `shrink-0` is what fixes the
134
+ width of the box, so the slider keeps its size while dragging;
135
+ `tabular-nums` only stops the digits from jittering inside it.
136
+ gray-700, not the gray-500 Slider uses for its own readout: axe puts
137
+ gray-500 on white at 1.84:1, well under the 4.5:1 it needs at this size.
138
+ -->
139
+ <span
140
+ class="min-w-16 shrink-0 text-right small-normal whitespace-nowrap text-gray-700 tabular-nums"
141
+ >
142
+ {{ displayValue }}
143
+ </span>
144
+ </div>
145
+ </NexxtEditorSettingsBlock>
146
+ </template>
@@ -73,6 +73,7 @@
73
73
  "NexxtEditorGap": "components/editor/molecules/EditorGap/EditorGap.vue",
74
74
  "NexxtEditorHeadingType": "components/editor/molecules/EditorHeadingType/EditorHeadingType.vue",
75
75
  "NexxtEditorJustifyContent": "components/editor/molecules/EditorJustifyContent/EditorJustifyContent.vue",
76
+ "NexxtEditorMaxWidth": "components/editor/molecules/EditorMaxWidth/EditorMaxWidth.vue",
76
77
  "NexxtEditorPadding": "components/editor/molecules/EditorPadding/EditorPadding.vue",
77
78
  "NexxtEditorRadius": "components/editor/molecules/EditorRadius/EditorRadius.vue",
78
79
  "NexxtEditorTextAlign": "components/editor/molecules/EditorTextAlign/EditorTextAlign.vue"
package/src/index.ts CHANGED
@@ -56,6 +56,11 @@ export type {
56
56
  EditorJustifyContentMessages,
57
57
  EditorJustifyContentValue,
58
58
  } from './components/editor/molecules/EditorJustifyContent/EditorJustifyContent.vue'
59
+ export { EDITOR_MAX_WIDTH_STEPS } from './components/editor/molecules/EditorMaxWidth/EditorMaxWidth.vue'
60
+ export type {
61
+ EditorMaxWidthMessages,
62
+ EditorMaxWidthValue,
63
+ } from './components/editor/molecules/EditorMaxWidth/EditorMaxWidth.vue'
59
64
  export type { EditorPaddingMessages } from './components/editor/molecules/EditorPadding/EditorPadding.vue'
60
65
  export type {
61
66
  NexxtTemplateCardContentFeature,
@@ -87,6 +92,7 @@ export { default as NexxtEditorFlexDirection } from './components/editor/molecul
87
92
  export { default as NexxtEditorGap } from './components/editor/molecules/EditorGap/EditorGap.vue'
88
93
  export { default as NexxtEditorHeadingType } from './components/editor/molecules/EditorHeadingType/EditorHeadingType.vue'
89
94
  export { default as NexxtEditorJustifyContent } from './components/editor/molecules/EditorJustifyContent/EditorJustifyContent.vue'
95
+ export { default as NexxtEditorMaxWidth } from './components/editor/molecules/EditorMaxWidth/EditorMaxWidth.vue'
90
96
  export { default as NexxtEditorPadding } from './components/editor/molecules/EditorPadding/EditorPadding.vue'
91
97
  export { default as NexxtEditorRadius } from './components/editor/molecules/EditorRadius/EditorRadius.vue'
92
98
  export { default as NexxtEditorSettingsBlock } from './components/editor/atoms/EditorSettingsBlock/EditorSettingsBlock.vue'