@pienter/ui 0.23.0 → 0.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/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,21 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 0.24.0 - 2026-09-16
|
|
6
|
+
### Added
|
|
7
|
+
|
|
8
|
+
- `RecordForm` and `RecordFields` gain two built-in field types, so a price
|
|
9
|
+
or a sale window no longer needs a `#field:name` slot repeating label,
|
|
10
|
+
hint, error and id wiring by hand. `{ type: 'number', min?, max?, step?,
|
|
11
|
+
placeholder?, steppers? }` renders `NumberField`; only finite numbers
|
|
12
|
+
display, and clearing the input writes `null` into the record rather than
|
|
13
|
+
`NaN`, since the payload is JSON. `{ type: 'date' | 'datetime-local' |
|
|
14
|
+
'time', min?, max?, step? }` renders `DateInput` with the ISO string shapes
|
|
15
|
+
it documents. Both honour `busy`, `disabled`, `required` and `readonly`
|
|
16
|
+
like every other built-in and can still be replaced through their slot.
|
|
17
|
+
- `shopping-cart` icon, so a commerce module or webshop entry no longer has
|
|
18
|
+
to borrow `layers`.
|
|
19
|
+
|
|
5
20
|
## 0.23.0 - 2026-09-16
|
|
6
21
|
### Added
|
|
7
22
|
|
package/CONVENTIONS.md
CHANGED
|
@@ -1545,7 +1545,10 @@ and request state. Index's compact row spacing uses
|
|
|
1545
1545
|
RecordForm emits detached JSON snapshots, preserving every supplied property.
|
|
1546
1546
|
Consumers construct an explicit writable object from backend records. Typed path
|
|
1547
1547
|
segments identify nested fields and array indices; fields display issues for
|
|
1548
|
-
their path and descendants. The form summary retains unmatched issues.
|
|
1548
|
+
their path and descendants. The form summary retains unmatched issues. Built-in
|
|
1549
|
+
field types map one to one onto the form primitives: text, email, textarea,
|
|
1550
|
+
select, checkbox, image, number (NumberField, cleared writes `null`) and
|
|
1551
|
+
date, datetime-local and time (DateInput, ISO strings).
|
|
1549
1552
|
`--pui-record-form-columns` customizes the field grid, defaulting to one column.
|
|
1550
1553
|
|
|
1551
1554
|
RecordForm's `fields` slot replaces its default RecordFields rendering for
|
|
@@ -8,6 +8,8 @@ import TextInput from '../text-input/TextInput.vue';
|
|
|
8
8
|
import PuiTextarea from '../textarea/Textarea.vue';
|
|
9
9
|
import PuiSelect from '../select/Select.vue';
|
|
10
10
|
import Checkbox from '../checkbox/Checkbox.vue';
|
|
11
|
+
import NumberField from '../number-field/NumberField.vue';
|
|
12
|
+
import DateInput from '../date-input/DateInput.vue';
|
|
11
13
|
import {
|
|
12
14
|
readPath,
|
|
13
15
|
updatePath,
|
|
@@ -46,6 +48,21 @@ function textFor(field: RecordFormField): string {
|
|
|
46
48
|
const value = valueFor(field);
|
|
47
49
|
return typeof value === 'string' ? value : '';
|
|
48
50
|
}
|
|
51
|
+
function numberFor(field: RecordFormField): number | undefined {
|
|
52
|
+
const value = valueFor(field);
|
|
53
|
+
return typeof value === 'number' && Number.isFinite(value)
|
|
54
|
+
? value
|
|
55
|
+
: undefined;
|
|
56
|
+
}
|
|
57
|
+
function isDateType(
|
|
58
|
+
field: RecordFormField,
|
|
59
|
+
): field is RecordFormField & { type: 'date' | 'datetime-local' | 'time' } {
|
|
60
|
+
return (
|
|
61
|
+
field.type === 'date' ||
|
|
62
|
+
field.type === 'datetime-local' ||
|
|
63
|
+
field.type === 'time'
|
|
64
|
+
);
|
|
65
|
+
}
|
|
49
66
|
function altFor(field: RecordFormField): string {
|
|
50
67
|
const name = field.type === 'image' ? field.altName : undefined;
|
|
51
68
|
if (!name) return '';
|
|
@@ -116,6 +133,41 @@ function update(field: RecordFormField, value: unknown): void {
|
|
|
116
133
|
:errors="errors"
|
|
117
134
|
@update:model-value="update(field, $event)"
|
|
118
135
|
/>
|
|
136
|
+
<NumberField
|
|
137
|
+
v-else-if="field.type === 'number'"
|
|
138
|
+
:label="field.label"
|
|
139
|
+
:name="JSON.stringify(pathFor(field))"
|
|
140
|
+
:model-value="numberFor(field)"
|
|
141
|
+
:min="field.min"
|
|
142
|
+
:max="field.max"
|
|
143
|
+
:step="field.step"
|
|
144
|
+
:steppers="field.steppers"
|
|
145
|
+
:hint="field.hint"
|
|
146
|
+
:required="field.required"
|
|
147
|
+
:disabled="busy || field.disabled"
|
|
148
|
+
:readonly="readonly"
|
|
149
|
+
:placeholder="field.placeholder"
|
|
150
|
+
:errors="errors"
|
|
151
|
+
@update:model-value="
|
|
152
|
+
update(field, Number.isNaN($event) ? null : $event)
|
|
153
|
+
"
|
|
154
|
+
/>
|
|
155
|
+
<DateInput
|
|
156
|
+
v-else-if="isDateType(field)"
|
|
157
|
+
:label="field.label"
|
|
158
|
+
:name="JSON.stringify(pathFor(field))"
|
|
159
|
+
:model-value="textFor(field)"
|
|
160
|
+
:type="field.type"
|
|
161
|
+
:min="field.min"
|
|
162
|
+
:max="field.max"
|
|
163
|
+
:step="field.step"
|
|
164
|
+
:hint="field.hint"
|
|
165
|
+
:required="field.required"
|
|
166
|
+
:disabled="busy || field.disabled"
|
|
167
|
+
:readonly="readonly"
|
|
168
|
+
:errors="errors"
|
|
169
|
+
@update:model-value="update(field, $event)"
|
|
170
|
+
/>
|
|
119
171
|
<template v-else-if="field.type === 'image'">
|
|
120
172
|
<div v-if="readonly" class="pui-field" data-readonly="true">
|
|
121
173
|
<span class="pui-field__label">{{ field.label }}</span>
|
|
@@ -30,5 +30,21 @@ export type RecordFormField = RecordFieldBase &
|
|
|
30
30
|
altName?: string;
|
|
31
31
|
}
|
|
32
32
|
| { type: 'checkbox' }
|
|
33
|
+
| {
|
|
34
|
+
type: 'number';
|
|
35
|
+
min?: number;
|
|
36
|
+
max?: number;
|
|
37
|
+
step?: number | string;
|
|
38
|
+
placeholder?: string;
|
|
39
|
+
/** NumberField's +/− buttons; on unless set to `false`. */
|
|
40
|
+
steppers?: boolean;
|
|
41
|
+
}
|
|
42
|
+
| {
|
|
43
|
+
type: 'date' | 'datetime-local' | 'time';
|
|
44
|
+
/** Bounds in the ISO shape of the value, as on DateInput. */
|
|
45
|
+
min?: string;
|
|
46
|
+
max?: string;
|
|
47
|
+
step?: number | string;
|
|
48
|
+
}
|
|
33
49
|
| { type: 'custom' }
|
|
34
50
|
);
|
package/icons/index.ts
CHANGED
|
@@ -43,6 +43,7 @@ import tool from './tool.js';
|
|
|
43
43
|
import trash from './trash.js';
|
|
44
44
|
import sidebarLeft from './sidebar-left.js';
|
|
45
45
|
import sidebarRight from './sidebar-right.js';
|
|
46
|
+
import shoppingCart from './shopping-cart.js';
|
|
46
47
|
|
|
47
48
|
export const icons = {
|
|
48
49
|
info: info,
|
|
@@ -90,6 +91,7 @@ export const icons = {
|
|
|
90
91
|
trash: trash,
|
|
91
92
|
'sidebar-left': sidebarLeft,
|
|
92
93
|
'sidebar-right': sidebarRight,
|
|
94
|
+
'shopping-cart': shoppingCart,
|
|
93
95
|
} as const;
|
|
94
96
|
|
|
95
97
|
export type IconName = keyof typeof icons;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" stroke="var(--pui-icon-clr, currentColor)" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" viewBox="0 0 24 24"><circle cx="8" cy="21" r="1"/><circle cx="19" cy="21" r="1"/><path d="M2.05 2.05h2l2.66 12.42a2 2 0 0 0 2 1.58h9.78a2 2 0 0 0 1.95-1.57l1.65-7.43H5.12"/></svg>';
|