@nexxtmove/ui 1.43.0 → 1.45.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/ui.css CHANGED
@@ -1,2 +1,2 @@
1
- input[data-snap][data-v-5d3904ac]{animation:snap-in-5d3904ac .18s var(--ease-out-strong)}input[data-snap=up][data-v-5d3904ac]{--snap-from:2px}input[data-snap=down][data-v-5d3904ac]{--snap-from:-2px}@keyframes snap-in-5d3904ac{0%{opacity:0;transform:translateY(var(--snap-from))}to{opacity:1;transform:translateY(0)}}@media (prefers-reduced-motion:reduce){input[data-snap][data-v-5d3904ac]{animation:1ms snap-hold-5d3904ac}@keyframes snap-hold-5d3904ac{0%,to{opacity:1}}}input[data-snap][data-v-17eb933b]{animation:snap-in-17eb933b .18s var(--ease-out-strong)}input[data-snap=up][data-v-17eb933b]{--snap-from:2px}input[data-snap=down][data-v-17eb933b]{--snap-from:-2px}@keyframes snap-in-17eb933b{0%{opacity:0;transform:translateY(var(--snap-from))}to{opacity:1;transform:translateY(0)}}@media (prefers-reduced-motion:reduce){input[data-snap][data-v-17eb933b]{animation:1ms snap-hold-17eb933b}@keyframes snap-hold-17eb933b{0%,to{opacity:1}}}rect[data-v-0a5d9b21]{animation-duration:4s;animation-timing-function:cubic-bezier(.77,0,.175,1);animation-iteration-count:infinite}rect[data-v-0a5d9b21]:first-child{animation-name:load-1-0a5d9b21}rect[data-v-0a5d9b21]:nth-child(2){animation-name:load-2-0a5d9b21}rect[data-v-0a5d9b21]:nth-child(3){animation-name:load-3-0a5d9b21}rect[data-v-0a5d9b21]:nth-child(4){animation-name:load-4-0a5d9b21}rect[data-v-0a5d9b21]:nth-child(5){animation-name:load-5-0a5d9b21}rect[data-v-0a5d9b21]:nth-child(6){animation-name:load-6-0a5d9b21}rect[data-v-0a5d9b21]:nth-child(7){animation-name:load-7-0a5d9b21}rect[data-v-0a5d9b21]:nth-child(8){animation-name:load-8-0a5d9b21}@media (prefers-reduced-motion:reduce){rect[data-v-0a5d9b21]{animation:none}svg[data-v-0a5d9b21]{animation:2.5s ease-in-out infinite pulse-0a5d9b21}}@keyframes pulse-0a5d9b21{0%,to{opacity:1}50%{opacity:.65}}@keyframes load-1-0a5d9b21{0%,80%{transform:translate(0)}5%,20%,60%,75%{transform:translate(-25px,25px)}30%,50%{transform:translate(-75px,75px)}}@keyframes load-2-0a5d9b21{5%,75%{transform:translate(0%)}10%,20%,60%,70%{transform:translate(50px)}30%,50%{transform:translateY(50px)}}@keyframes load-3-0a5d9b21{10%,70%{transform:translate(0%)}15%,65%{transform:translateY(-50px)}20%,60%{fill:var(--color-sapphire-500);transform:translateY(-50px)}30%,50%{fill:var(--color-purple-500);transform:translate(-50px)}}@keyframes load-4-0a5d9b21{20%,60%{transform:translate(0%)}30%,50%{transform:translate(50px,-50px)}}@keyframes load-5-0a5d9b21{0%,80%{transform:translate(0%)}5%,20%,60%,75%{transform:translateY(50px)}30%,50%{transform:translate(50px)}}@keyframes load-6-0a5d9b21{5%,75%{transform:translate(0%)}10%,20%,60%,70%{transform:translate(-50px)}30%,50%{transform:translateY(-50px)}}@keyframes load-7-0a5d9b21{10%,70%{transform:translate(0%)}15%,20%,60%,65%{transform:translate(50px)}30%,50%{transform:translate(100px,-50px)}}@keyframes load-8-0a5d9b21{15%,65%{transform:translate(0%)}20%,60%{transform:translateY(-50px)}30%,50%{transform:translate(50px,-100px)}}.timer-circle[data-v-7e3c5bda]{stroke-dashoffset:0;animation:linear forwards toast-timer-7e3c5bda}@keyframes toast-timer-7e3c5bda{to{stroke-dashoffset:var(--fa0d20de)}}
1
+ input[data-snap][data-v-5d3904ac]{animation:snap-in-5d3904ac .18s var(--ease-out-strong)}input[data-snap=up][data-v-5d3904ac]{--snap-from:2px}input[data-snap=down][data-v-5d3904ac]{--snap-from:-2px}@keyframes snap-in-5d3904ac{0%{opacity:0;transform:translateY(var(--snap-from))}to{opacity:1;transform:translateY(0)}}@media (prefers-reduced-motion:reduce){input[data-snap][data-v-5d3904ac]{animation:1ms snap-hold-5d3904ac}@keyframes snap-hold-5d3904ac{0%,to{opacity:1}}}input[data-snap][data-v-15feed8b]{animation:snap-in-15feed8b .18s var(--ease-out-strong)}input[data-snap=up][data-v-15feed8b]{--snap-from:2px}input[data-snap=down][data-v-15feed8b]{--snap-from:-2px}@keyframes snap-in-15feed8b{0%{opacity:0;transform:translateY(var(--snap-from))}to{opacity:1;transform:translateY(0)}}@media (prefers-reduced-motion:reduce){input[data-snap][data-v-15feed8b]{animation:1ms snap-hold-15feed8b}@keyframes snap-hold-15feed8b{0%,to{opacity:1}}}rect[data-v-0a5d9b21]{animation-duration:4s;animation-timing-function:cubic-bezier(.77,0,.175,1);animation-iteration-count:infinite}rect[data-v-0a5d9b21]:first-child{animation-name:load-1-0a5d9b21}rect[data-v-0a5d9b21]:nth-child(2){animation-name:load-2-0a5d9b21}rect[data-v-0a5d9b21]:nth-child(3){animation-name:load-3-0a5d9b21}rect[data-v-0a5d9b21]:nth-child(4){animation-name:load-4-0a5d9b21}rect[data-v-0a5d9b21]:nth-child(5){animation-name:load-5-0a5d9b21}rect[data-v-0a5d9b21]:nth-child(6){animation-name:load-6-0a5d9b21}rect[data-v-0a5d9b21]:nth-child(7){animation-name:load-7-0a5d9b21}rect[data-v-0a5d9b21]:nth-child(8){animation-name:load-8-0a5d9b21}@media (prefers-reduced-motion:reduce){rect[data-v-0a5d9b21]{animation:none}svg[data-v-0a5d9b21]{animation:2.5s ease-in-out infinite pulse-0a5d9b21}}@keyframes pulse-0a5d9b21{0%,to{opacity:1}50%{opacity:.65}}@keyframes load-1-0a5d9b21{0%,80%{transform:translate(0)}5%,20%,60%,75%{transform:translate(-25px,25px)}30%,50%{transform:translate(-75px,75px)}}@keyframes load-2-0a5d9b21{5%,75%{transform:translate(0%)}10%,20%,60%,70%{transform:translate(50px)}30%,50%{transform:translateY(50px)}}@keyframes load-3-0a5d9b21{10%,70%{transform:translate(0%)}15%,65%{transform:translateY(-50px)}20%,60%{fill:var(--color-sapphire-500);transform:translateY(-50px)}30%,50%{fill:var(--color-purple-500);transform:translate(-50px)}}@keyframes load-4-0a5d9b21{20%,60%{transform:translate(0%)}30%,50%{transform:translate(50px,-50px)}}@keyframes load-5-0a5d9b21{0%,80%{transform:translate(0%)}5%,20%,60%,75%{transform:translateY(50px)}30%,50%{transform:translate(50px)}}@keyframes load-6-0a5d9b21{5%,75%{transform:translate(0%)}10%,20%,60%,70%{transform:translate(-50px)}30%,50%{transform:translateY(-50px)}}@keyframes load-7-0a5d9b21{10%,70%{transform:translate(0%)}15%,20%,60%,65%{transform:translate(50px)}30%,50%{transform:translate(100px,-50px)}}@keyframes load-8-0a5d9b21{15%,65%{transform:translate(0%)}20%,60%{transform:translateY(-50px)}30%,50%{transform:translate(50px,-100px)}}.timer-circle[data-v-7e3c5bda]{stroke-dashoffset:0;animation:linear forwards toast-timer-7e3c5bda}@keyframes toast-timer-7e3c5bda{to{stroke-dashoffset:var(--fa0d20de)}}
2
2
  /*$vite$:1*/
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@nexxtmove/ui",
3
3
  "type": "module",
4
- "version": "1.43.0",
4
+ "version": "1.45.0",
5
5
  "exports": {
6
6
  ".": {
7
7
  "types": "./dist/index.d.ts",
@@ -97,7 +97,12 @@ const emit = defineEmits<{
97
97
  commit: []
98
98
  }>()
99
99
 
100
- /** The dimension, or `null` for none. */
100
+ /**
101
+ * The dimension, or `null` for none. A `null` from outside -- another element
102
+ * selected, an undo -- resets the unit to the first of `units`; a field the
103
+ * user emptied keeps the unit picked in it. A value from outside brings its
104
+ * own unit.
105
+ */
101
106
  const model = defineModel<EditorDimensionValue | null>({ default: null })
102
107
 
103
108
  const copy = computed(() => ({ ...DEFAULT_MESSAGES, ...messages }))
@@ -109,10 +114,20 @@ const copy = computed(() => ({ ...DEFAULT_MESSAGES, ...messages }))
109
114
  */
110
115
  const unit = ref<EditorDimensionUnit>(model.value?.unit ?? units[0])
111
116
 
117
+ /**
118
+ * Set while the field's own `null` is on its way through the model. Without
119
+ * it, the user emptying the field could not be told apart from a `null` from
120
+ * outside, and only the latter resets the unit.
121
+ */
122
+ let emptiedHere = false
123
+
112
124
  watch(
113
- () => model.value?.unit,
125
+ () => model.value,
114
126
  (next) => {
115
- if (next) unit.value = next
127
+ if (next) unit.value = next.unit
128
+ else if (!emptiedHere) unit.value = units[0]
129
+
130
+ emptiedHere = false
116
131
  },
117
132
  )
118
133
 
@@ -183,6 +198,7 @@ const write = (next: EditorDimensionValue | null) => {
183
198
  )
184
199
  return false
185
200
 
201
+ emptiedHere = next === null
186
202
  model.value = next
187
203
  return true
188
204
  }
@@ -37,6 +37,12 @@ export interface EditorMaxWidthMessages {
37
37
  unit?: string
38
38
  }
39
39
 
40
+ /**
41
+ * Sets a width on Tailwind's `max-w-*` steps, stored as the class.
42
+ *
43
+ * @deprecated Use `NexxtEditorSizeLimit`: a free maximum in px or % that can
44
+ * be none.
45
+ */
40
46
  export interface NexxtEditorMaxWidthProps {
41
47
  /**
42
48
  * Header text of the settings block. Defaults to the Dutch
@@ -21,7 +21,9 @@ export interface EditorSizeValue {
21
21
  mode: EditorSizeMode
22
22
  /**
23
23
  * The fixed dimension. Kept when another mode is picked, so switching back
24
- * to `'fixed'` restores it; it only applies while `mode` is `'fixed'`.
24
+ * to `'fixed'` restores it; it only applies while `mode` is `'fixed'`. The
25
+ * app always fills it with the measured size of the element, so picking
26
+ * `'fixed'` starts from that size.
25
27
  */
26
28
  fixed?: EditorDimensionValue
27
29
  }
@@ -119,9 +121,10 @@ const emit = defineEmits<{
119
121
  }>()
120
122
 
121
123
  /**
122
- * The size. Picking `'fixed'` without a `fixed` dimension leaves the field
123
- * empty until a value is entered; seed `fixed` -- with the measured size, say
124
- * -- to start from there instead.
124
+ * The size. Fill `fixed` with the measured size of the element, as the app
125
+ * always does. Without it, picking `'fixed'` writes `{ mode: 'fixed' }` and
126
+ * commits straight away, leaving a fixed mode without a size until a value is
127
+ * typed; with the measured size, that state does not come up.
125
128
  */
126
129
  const model = defineModel<EditorSizeValue>({ required: true })
127
130
 
@@ -7,6 +7,11 @@ import type {
7
7
  /** Overridable copy of the field. Every key is optional. */
8
8
  export type EditorSizeLimitMessages = EditorDimensionFieldMessages
9
9
 
10
+ /**
11
+ * A free minimum or maximum of an element's size, in px or %, that can be
12
+ * none. Replaces the deprecated `NexxtEditorMaxWidth`, which offers only
13
+ * Tailwind's `max-w-*` steps.
14
+ */
10
15
  export interface NexxtEditorSizeLimitProps {
11
16
  /** Visible label above the field, and its accessible name, as in `'Maximale breedte'`. */
12
17
  label: string
@@ -80,7 +80,7 @@ const selected = defineModel<string | null>('selected', { default: null })
80
80
  /** Ids of the nodes whose children are hidden. Tracking the collapsed ones makes "everything open" the default. */
81
81
  const collapsed = defineModel<string[]>('collapsed', { default: () => [] })
82
82
 
83
- const INDENT = 22
83
+ const INDENT = 8
84
84
 
85
85
  const root = useTemplateRef<HTMLElement>('root')
86
86
  const focusedId = ref<string | null>(null)
@@ -890,7 +890,7 @@ defineExpose({ focusNode })
890
890
  <!-- The drop indicator: where the dragged node lands, at the depth it lands on. Out of
891
891
  the accessibility tree and without `data-tree-id`, so it is no row to the keyboard,
892
892
  the tab stop or the highlight. One element for the whole drag, that changes spots. -->
893
- <div v-if="item.ghost" aria-hidden="true" class="mb-0.5" data-tree-ghost>
893
+ <div v-if="item.ghost" aria-hidden="true" class="mb-1.5" data-tree-ghost>
894
894
  <div
895
895
  class="flex rounded-md bg-cornflower-blue-50 py-1 pr-2 pl-1 small-normal text-gray-700 outline-1 -outline-offset-1 outline-cornflower-blue-600 transition-[margin-left] duration-200 ease-in-out-strong outline-dashed motion-reduce:transition-none"
896
896
  :style="{ marginLeft: `${item.depth * INDENT}px` }"
@@ -918,7 +918,7 @@ defineExpose({ focusNode })
918
918
  :data-tree-id="item.row.node.id"
919
919
  :data-tree-drop="collapsedTargetId === item.row.node.id ? 'inside' : undefined"
920
920
  :draggable="item.row.draggable"
921
- class="group mb-0.5 flex items-center gap-1.5 rounded-md py-1 pr-2 small-normal transition-[color,background-color,outline-color,opacity] duration-150 select-none focus-visible:outline-2 focus-visible:outline-cornflower-blue-500 motion-reduce:transition-none"
921
+ class="group mb-1.5 flex items-center gap-1.5 overflow-x-clip rounded-md py-1 pr-2 small-normal transition-[color,background-color,outline-color,opacity] duration-150 select-none focus-visible:outline-2 focus-visible:outline-cornflower-blue-500 motion-reduce:transition-none"
922
922
  :class="rowClasses(item.row)"
923
923
  :style="{ paddingLeft: `${item.row.depth * INDENT + 4}px` }"
924
924
  @click="onRowClick(item.row.node.id)"
@@ -955,19 +955,39 @@ defineExpose({ focusNode })
955
955
  class="shrink-0 transition-colors duration-150 motion-reduce:transition-none"
956
956
  :class="selected === item.row.node.id ? 'text-cornflower-blue-600' : 'text-gray-600'"
957
957
  />
958
- <span :id="labelId(item.row.node.id)" class="truncate">{{ item.row.node.label }}</span>
959
-
960
- <!-- Revealed by opacity rather than removed, so the row keeps its layout while it
961
- appears. Pulled into the row's padding so a 24px button leaves the row at 28px.
962
- Stopping here keeps the row's click and the tree's keys away from the actions. -->
958
+ <span :id="labelId(item.row.node.id)" data-tree-label class="min-w-0 flex-1 truncate">{{
959
+ item.row.node.label
960
+ }}</span>
961
+
962
+ <!-- Only the selected row has actions: the track grows from 0 to their width and pushes
963
+ its label in, while their start, pinned to it, slides them in from the row's end.
964
+ The clip hides what has not slid in yet; the row's `overflow-x-clip` keeps them out
965
+ of the panel's scroll width meanwhile. Inert on every other row, so what cannot be
966
+ seen cannot be focused or read either; `undefined` rather than `false`, so no
967
+ `inert="false"` lands where the DOM has no such property. Pulled into the row's padding so a 24px
968
+ button leaves the row at 28px, and into the row's gap so a label without actions
969
+ runs to the row's end. Stopping here keeps the row's click and the tree's keys away
970
+ from the actions. -->
963
971
  <div
964
972
  v-if="slots['node-actions']"
965
973
  data-tree-actions
966
- class="-my-0.5 ml-auto flex shrink-0 items-center opacity-0 transition-opacity duration-150 ease-out group-focus-within:opacity-100 group-hover:opacity-100 motion-reduce:transition-none pointer-coarse:opacity-100"
974
+ class="-my-0.5 -ml-1.5 grid shrink-0 transition-[grid-template-columns] duration-200 ease-in-out-strong [clip-path:inset(-0.5rem_-0.25rem)] motion-reduce:transition-none"
975
+ :class="
976
+ selected === item.row.node.id
977
+ ? 'grid-cols-[minmax(0,1fr)]'
978
+ : 'grid-cols-[minmax(0,0fr)]'
979
+ "
980
+ :inert="selected !== item.row.node.id || undefined"
967
981
  @click.stop
968
982
  @keydown.stop
969
983
  >
970
- <NodeActions :node="item.row.node" :tabindex="tabindexOf(item.row.node.id)" />
984
+ <div
985
+ data-tree-actions-content
986
+ class="flex w-max items-center pl-1.5 transition-opacity duration-150 ease-out motion-reduce:transition-none"
987
+ :class="selected === item.row.node.id ? 'opacity-100' : 'opacity-0'"
988
+ >
989
+ <NodeActions :node="item.row.node" :tabindex="tabindexOf(item.row.node.id)" />
990
+ </div>
971
991
  </div>
972
992
  </div>
973
993
  </template>