@pienter/ui 0.18.0 → 0.20.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/CHANGELOG.md CHANGED
@@ -2,6 +2,37 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 0.20.0 - 2026-09-16
6
+
7
+ ### Added
8
+
9
+ - `BlockEditor` takes `hint`, `emptyText` and `addLabel(typeLabel)`, the
10
+ three visible strings it used to hard-code in English: the reorder hint
11
+ above the list, the edit-mode empty message under the `empty` slot, and the
12
+ add-button label built from each block type's `label`. The English defaults
13
+ are unchanged, so a Dutch consumer can pass `Tekst toevoegen` and a Dutch
14
+ hint without CSS overrides or replacing the add row. The aria-only labels
15
+ and announcements are still English.
16
+
17
+ ## 0.19.0 - 2026-09-16
18
+
19
+ ### Added
20
+
21
+ - The English accessibility text the icon-only controls and landmarks used
22
+ to hardcode is now a prop with that text as its default, following
23
+ `AppLayout`'s `skipLabel` / `drawerLabel` / `navigationLabel`, so a
24
+ single-language consumer can name them in its own language. `Pagination`
25
+ takes `label` (the `<nav>` landmark, `Pagination`), `previousLabel`
26
+ (`Previous page`) and `nextLabel` (`Next page`); `Breadcrumb` takes
27
+ `label` (`Breadcrumb`); `NumberField` takes `increaseLabel` (`Increase`)
28
+ and `decreaseLabel` (`Decrease`); `Table` takes `selectAllLabel`
29
+ (`Select all rows`), which `DataTable` forwards alongside its own
30
+ `selectRowLabel`, a string or a function of the row that replaces the
31
+ default `Select <first cell>` name; `Toast` takes `closeLabel` (`Close`),
32
+ and `ToastHost` takes the same `closeLabel` for every toast it renders
33
+ next to its existing `label` for the notifications region.
34
+ `PaginationFooter` has no text of its own and is unchanged.
35
+
5
36
  ## 0.18.0 - 2026-09-16
6
37
 
7
38
  ### Changed
package/CONVENTIONS.md CHANGED
@@ -1498,8 +1498,11 @@ counts the selected values, with a Clear, joining values on the comma
1498
1498
  convention) and `FilterRange` (`name`, `label`, `min`, `max`, `step`; two
1499
1499
  NumberFields writing `<name>_min` and `<name>_max` on change). Each binds
1500
1500
  through `useIndexFilters` to the provided state, or to a `state` prop, and
1501
- touches only its own names; without a state it warns and does nothing. They
1502
- sit in the `filters` slot or anywhere the definition provides the state; a
1501
+ touches only its own names; without a state it warns and does nothing. On a
1502
+ module index they live in the `PageAside`: the definition renders them
1503
+ inside `<PageAside title="Filters">` beside `<Index :state>`, both under the
1504
+ component that created and provided the state, and the aside's content grid
1505
+ stacks them. The `filters` slot stays for a toolbar placement; elsewhere a
1503
1506
  column of them is a consumer grid with a spacing token for its gap. Options
1504
1507
  and counts are the consumer's (`CONTEXT.md`: Facet).
1505
1508
 
@@ -19,7 +19,7 @@
19
19
  v-if="dismissible"
20
20
  class="pui-toast__close"
21
21
  type="button"
22
- aria-label="Close"
22
+ :aria-label="closeLabel"
23
23
  @click="$emit('dismiss')"
24
24
  >
25
25
  <Icon name="x" size="sm" />
@@ -48,6 +48,8 @@ const props = withDefaults(
48
48
  dismissible?: boolean;
49
49
  icon?: IconName;
50
50
  action?: { label: string };
51
+ /** Accessible name of the icon-only close button. */
52
+ closeLabel?: string;
51
53
  }>(),
52
54
  {
53
55
  tone: undefined,
@@ -55,6 +57,7 @@ const props = withDefaults(
55
57
  dismissible: true,
56
58
  icon: undefined,
57
59
  action: undefined,
60
+ closeLabel: 'Close',
58
61
  },
59
62
  );
60
63
 
@@ -17,11 +17,15 @@ interface Timer {
17
17
  const props = withDefaults(
18
18
  defineProps<{
19
19
  controller?: ToastController;
20
+ /** Accessible name of the notifications region. */
20
21
  label?: string;
22
+ /** Accessible name of every toast's close button. */
23
+ closeLabel?: string;
21
24
  }>(),
22
25
  {
23
26
  controller: () => defaultController,
24
27
  label: 'Notifications',
28
+ closeLabel: 'Close',
25
29
  },
26
30
  );
27
31
  const items = computed(() => props.controller.visible.value);
@@ -151,6 +155,7 @@ onBeforeUnmount(() => {
151
155
  :dismissible="item.dismissible"
152
156
  :icon="item.icon"
153
157
  :action="item.action"
158
+ :close-label="closeLabel"
154
159
  aria-keyshortcuts="Escape"
155
160
  @dismiss="dismiss(item.id)"
156
161
  @action="runAction(item)"
@@ -24,6 +24,12 @@ const props = withDefaults(
24
24
  /** Heading level of each block title: `3` sits under a level-2 `Panel` title. */
25
25
  level?: 2 | 3 | 4 | 5 | 6;
26
26
  blockLabel?: (block: T, index: number) => string;
27
+ /** Label of each add button, given the block type's `label`. */
28
+ addLabel?: (typeLabel: string) => string;
29
+ /** Reorder hint shown above the list in edit mode. */
30
+ hint?: string;
31
+ /** Empty-list message in edit mode; the `empty` slot replaces it entirely. */
32
+ emptyText?: string;
27
33
  }>(),
28
34
  {
29
35
  disabled: false,
@@ -32,6 +38,9 @@ const props = withDefaults(
32
38
  label: 'Content blocks',
33
39
  level: 3,
34
40
  blockLabel: undefined,
41
+ addLabel: (typeLabel: string) => `Add ${typeLabel}`,
42
+ hint: 'Use the move buttons to reorder, or drag with a pointer.',
43
+ emptyText: 'No blocks yet. Add a block to start writing.',
35
44
  },
36
45
  );
37
46
 
@@ -317,7 +326,7 @@ onBeforeUnmount(stopDrag);
317
326
  :data-readonly="readonly ? 'true' : undefined"
318
327
  >
319
328
  <p v-if="!readonly" :id="`${id}-hint`" class="pui-block-editor__hint">
320
- Use the move buttons to reorder, or drag with a pointer.
329
+ {{ hint }}
321
330
  </p>
322
331
  <ol
323
332
  v-if="modelValue.length"
@@ -448,9 +457,7 @@ onBeforeUnmount(stopDrag);
448
457
  </ol>
449
458
  <div v-else class="pui-block-editor__empty">
450
459
  <slot name="empty">{{
451
- readonly
452
- ? 'No content blocks.'
453
- : 'No blocks yet. Add a block to start writing.'
460
+ readonly ? 'No content blocks.' : emptyText
454
461
  }}</slot>
455
462
  </div>
456
463
  <div v-if="!readonly" :id="`${id}-add`" class="pui-block-editor__add">
@@ -461,7 +468,7 @@ onBeforeUnmount(stopDrag);
461
468
  size="sm"
462
469
  :disabled="disabled"
463
470
  @click="add(type)"
464
- >Add {{ type.label }}</Button
471
+ >{{ addLabel(type.label) }}</Button
465
472
  >
466
473
  </div>
467
474
  </div>
@@ -43,7 +43,7 @@
43
43
  type="button"
44
44
  class="pui-number-field__step"
45
45
  data-direction="increment"
46
- aria-label="Increase"
46
+ :aria-label="increaseLabel"
47
47
  :disabled="disabled"
48
48
  @click="handleStep('increment')"
49
49
  >
@@ -53,7 +53,7 @@
53
53
  type="button"
54
54
  class="pui-number-field__step"
55
55
  data-direction="decrement"
56
- aria-label="Decrease"
56
+ :aria-label="decreaseLabel"
57
57
  :disabled="disabled"
58
58
  @click="handleStep('decrement')"
59
59
  >
@@ -100,6 +100,10 @@ const props = withDefaults(
100
100
  readonly?: boolean;
101
101
  /** The +/− buttons beside the input; off, the field is a plain number input. */
102
102
  steppers?: boolean;
103
+ /** Accessible name of the + stepper. */
104
+ increaseLabel?: string;
105
+ /** Accessible name of the − stepper. */
106
+ decreaseLabel?: string;
103
107
  hint?: string;
104
108
  errors?: string[];
105
109
  status?: 'error' | 'success';
@@ -118,6 +122,8 @@ const props = withDefaults(
118
122
  disabled: false,
119
123
  readonly: false,
120
124
  steppers: true,
125
+ increaseLabel: 'Increase',
126
+ decreaseLabel: 'Decrease',
121
127
  hint: undefined,
122
128
  errors: () => [],
123
129
  status: undefined,
@@ -10,6 +10,7 @@
10
10
  :selectable="selectable"
11
11
  :clickable="clickable"
12
12
  :all-selected="allSelected"
13
+ :select-all-label="selectAllLabel"
13
14
  :label="label"
14
15
  :labelledby="labelledby"
15
16
  @toggle-all="toggleAll"
@@ -76,6 +77,10 @@ const props = withDefaults(
76
77
  selectable?: boolean;
77
78
  /** Show a pointer cursor on rows. */
78
79
  clickable?: boolean;
80
+ /** Accessible name of the header select-all checkbox. */
81
+ selectAllLabel?: string;
82
+ /** Accessible name of a row's checkbox; a function receives the row. Default: `Select <first cell>`, or `Select row` when that cell is empty. */
83
+ selectRowLabel?: string | ((row: T) => string);
79
84
  /** Which column is sorted, and how. `rows` is passed through untouched — ordering them is the consumer's job (ADR 0005). */
80
85
  sort?: SortState;
81
86
  /** Frosts and disables the body while the rows are stale, and suppresses the empty state. */
@@ -90,6 +95,8 @@ const props = withDefaults(
90
95
  selected: () => [],
91
96
  selectable: false,
92
97
  clickable: false,
98
+ selectAllLabel: 'Select all rows',
99
+ selectRowLabel: undefined,
93
100
  sort: null,
94
101
  loading: false,
95
102
  label: undefined,
@@ -112,6 +119,9 @@ function keyOf(row: T): string | number {
112
119
  return cellValue(row, props.rowKey) as string | number;
113
120
  }
114
121
  function rowLabel(row: T): string {
122
+ if (typeof props.selectRowLabel === 'function')
123
+ return props.selectRowLabel(row);
124
+ if (props.selectRowLabel !== undefined) return props.selectRowLabel;
115
125
  const first = props.columns[0];
116
126
  const value = first ? String(cellValue(row, first.key) ?? '').trim() : '';
117
127
  return value ? `Select ${value}` : 'Select row';
@@ -27,7 +27,7 @@
27
27
  class="pui-table__check"
28
28
  type="checkbox"
29
29
  :checked="allSelected"
30
- aria-label="Select all rows"
30
+ :aria-label="selectAllLabel"
31
31
  @change="emit('toggle-all')"
32
32
  />
33
33
  </th>
@@ -106,6 +106,8 @@ const props = withDefaults(
106
106
  clickable?: boolean;
107
107
  /** Checked state of the header select-all checkbox. */
108
108
  allSelected?: boolean;
109
+ /** Accessible name of the header select-all checkbox. */
110
+ selectAllLabel?: string;
109
111
  /** Which column is sorted, and how. Rendered, never changed here; `null` is unsorted (ADR 0005). */
110
112
  sort?: SortState;
111
113
  /** Frosts and disables the body while the rows are stale, leaving the header usable. */
@@ -119,6 +121,7 @@ const props = withDefaults(
119
121
  selectable: false,
120
122
  clickable: false,
121
123
  allSelected: false,
124
+ selectAllLabel: 'Select all rows',
122
125
  sort: null,
123
126
  loading: false,
124
127
  label: undefined,
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <nav aria-label="Breadcrumb" class="pui-breadcrumb">
2
+ <nav :aria-label="label" class="pui-breadcrumb">
3
3
  <ol class="pui-breadcrumb__trail">
4
4
  <template v-for="(item, index) in items" :key="index">
5
5
  <li
@@ -36,9 +36,14 @@
36
36
  </template>
37
37
 
38
38
  <script setup lang="ts">
39
- defineProps<{
40
- items: { label: string; href?: string }[];
41
- }>();
39
+ withDefaults(
40
+ defineProps<{
41
+ items: { label: string; href?: string }[];
42
+ /** Accessible name of the `<nav>` landmark. */
43
+ label?: string;
44
+ }>(),
45
+ { label: 'Breadcrumb' },
46
+ );
42
47
 
43
48
  defineSlots<{
44
49
  link?: (scope: {
@@ -1,11 +1,11 @@
1
1
  <template>
2
- <nav class="pui-pagination-nav" aria-label="Pagination">
2
+ <nav class="pui-pagination-nav" :aria-label="label">
3
3
  <ul class="pui-pagination">
4
4
  <li>
5
5
  <button
6
6
  class="pui-pagination__item"
7
7
  :disabled="currentPage <= 1"
8
- aria-label="Previous page"
8
+ :aria-label="previousLabel"
9
9
  @click="emit('update:currentPage', currentPage - 1)"
10
10
  >
11
11
  <Icon name="chevron-left" size="sm" />
@@ -33,7 +33,7 @@
33
33
  <button
34
34
  class="pui-pagination__item"
35
35
  :disabled="currentPage >= totalPages"
36
- aria-label="Next page"
36
+ :aria-label="nextLabel"
37
37
  @click="emit('update:currentPage', currentPage + 1)"
38
38
  >
39
39
  <Icon name="chevron-right" size="sm" />
@@ -52,9 +52,18 @@ const props = withDefaults(
52
52
  currentPage: number;
53
53
  totalPages: number;
54
54
  maxVisible?: number;
55
+ /** Accessible name of the `<nav>` landmark. */
56
+ label?: string;
57
+ /** Accessible name of the icon-only previous button. */
58
+ previousLabel?: string;
59
+ /** Accessible name of the icon-only next button. */
60
+ nextLabel?: string;
55
61
  }>(),
56
62
  {
57
63
  maxVisible: 5,
64
+ label: 'Pagination',
65
+ previousLabel: 'Previous page',
66
+ nextLabel: 'Next page',
58
67
  },
59
68
  );
60
69
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pienter/ui",
3
- "version": "0.18.0",
3
+ "version": "0.20.0",
4
4
  "description": "Shared Pienter UI components, styles, icons, and browser utilities.",
5
5
  "type": "module",
6
6
  "license": "MIT",