@aglyn/plugins-mui 1.0.0-beta.256 → 1.0.0-beta.258
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/package.json +9 -7
- package/src/lib/components/form-controls.d.ts +33 -0
- package/src/lib/components/form-controls.js +16 -12
- package/src/lib/components/form-controls.js.map +1 -1
- package/src/lib/components/form-pickers.d.ts +69 -0
- package/src/lib/components/form-pickers.js +455 -0
- package/src/lib/components/form-pickers.js.map +1 -0
- package/src/lib/element-tier.d.ts +1 -0
- package/src/lib/element-tier.js +17 -1
- package/src/lib/element-tier.js.map +1 -1
- package/src/lib/plugin.js +13 -0
- package/src/lib/plugin.js.map +1 -1
|
@@ -0,0 +1,455 @@
|
|
|
1
|
+
import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
2
|
+
import { _ as _object_without_properties_loose } from "@swc/helpers/_/_object_without_properties_loose";
|
|
3
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
4
|
+
/**
|
|
5
|
+
* @license
|
|
6
|
+
* Copyright 2026 Aglyn LLC
|
|
7
|
+
*
|
|
8
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
9
|
+
* you may not use this file except in compliance with the License.
|
|
10
|
+
* You may obtain a copy of the License at
|
|
11
|
+
*
|
|
12
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
13
|
+
*
|
|
14
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
15
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
16
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
17
|
+
* See the License for the specific language governing permissions and
|
|
18
|
+
* limitations under the License.
|
|
19
|
+
*/ import * as Aglyn from "@aglyn/aglyn";
|
|
20
|
+
import { mdiCalendarMonthOutline, mdiClockOutline } from "@aglyn/shared-data-mdi";
|
|
21
|
+
import Box from "@mui/material/Box";
|
|
22
|
+
import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs";
|
|
23
|
+
import { DatePicker } from "@mui/x-date-pickers/DatePicker";
|
|
24
|
+
import { LocalizationProvider } from "@mui/x-date-pickers/LocalizationProvider";
|
|
25
|
+
import { TimePicker } from "@mui/x-date-pickers/TimePicker";
|
|
26
|
+
import dayjs from "dayjs";
|
|
27
|
+
import { forwardRef, useContext, useEffect, useRef, useState } from "react";
|
|
28
|
+
import { BUNDLE_ID } from "../constants/bundle-common.js";
|
|
29
|
+
import { dropClearedProps } from "../utils/drop-cleared-props.js";
|
|
30
|
+
import { generatePresetId } from "../utils/generate-preset-id.js";
|
|
31
|
+
import { category, FIELD_ARIA_LABEL, FIELD_DATASET_FIELD, FIELD_FULL_WIDTH, FIELD_INPUT_SIZE, FIELD_NAME, FIELD_REQUIRED, FieldIdContext, FORM_CONTROL_ID, FORM_LABEL_ID, node, nodeSxList, preset, useFilledInputs } from "./form-controls.js";
|
|
32
|
+
/*
|
|
33
|
+
* THE DATE AND TIME FIELDS OF A FORM: MUI X's pickers, one element each.
|
|
34
|
+
*
|
|
35
|
+
* They are their own module, loaded only on a page that places one, because
|
|
36
|
+
* the pickers and their date library are the heaviest thing a form can hold
|
|
37
|
+
* and most forms hold neither. They sit in a Form Control beside a Form
|
|
38
|
+
* Label exactly like an Input, and like every form element they know nothing
|
|
39
|
+
* about the form around them: each carries a NAME, and the form sends what
|
|
40
|
+
* the browser sends.
|
|
41
|
+
*
|
|
42
|
+
* What the browser sends is the part that needed care. A picker's own field
|
|
43
|
+
* would post the day as the visitor reads it ("10/15/2026"), which changes
|
|
44
|
+
* with the language and cannot be sorted or stored as a date. So the picker
|
|
45
|
+
* is left unnamed and the answer is posted from an input of our own, as the
|
|
46
|
+
* ISO day (`2026-10-15`) or the 24-hour time (`14:30`) — the same text the
|
|
47
|
+
* browser's own date and time inputs post, and what a dataset date stores.
|
|
48
|
+
*
|
|
49
|
+
* Component ids are persisted in documents; never rename them.
|
|
50
|
+
*/ export const DATE_PICKER_ID = 'muiDatePicker';
|
|
51
|
+
export const TIME_PICKER_ID = 'muiTimePicker';
|
|
52
|
+
const DAY = 'YYYY-MM-DD';
|
|
53
|
+
const CLOCK = 'HH:mm';
|
|
54
|
+
/** `2026-10-15` as a picker value, or `null` for anything that is not a day. */ export function parsePickerDay(value) {
|
|
55
|
+
const text = typeof value === 'string' ? value.trim() : '';
|
|
56
|
+
if (!/^\d{4}-\d{2}-\d{2}$/.test(text)) return null;
|
|
57
|
+
const day = dayjs(text);
|
|
58
|
+
return day.isValid() && day.format(DAY) === text ? day : null;
|
|
59
|
+
}
|
|
60
|
+
/** `14:30` as a picker value (today at that time), or `null`. */ export function parsePickerTime(value) {
|
|
61
|
+
const match = /^([01]\d|2[0-3]):([0-5]\d)$/.exec(typeof value === 'string' ? value.trim() : '');
|
|
62
|
+
if (!match) return null;
|
|
63
|
+
return dayjs().hour(Number(match[1])).minute(Number(match[2])).second(0).millisecond(0);
|
|
64
|
+
}
|
|
65
|
+
/** What a picked value posts as: the ISO day or the 24-hour time, or nothing. */ export function pickerAnswer(value, kind) {
|
|
66
|
+
if (!value || !value.isValid()) return '';
|
|
67
|
+
return value.format(kind === 'date' ? DAY : CLOCK);
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* The input a picker's answer is posted from. It is a real text input, not
|
|
71
|
+
* `type="hidden"` and not read-only, because those two are exactly the inputs
|
|
72
|
+
* the browser never checks: a required picker left empty must stop the form
|
|
73
|
+
* like any other required field. It sits invisibly under the picker, so the
|
|
74
|
+
* browser's "Please fill in this field" points at the picker, and it hands
|
|
75
|
+
* focus to the picker when that happens.
|
|
76
|
+
*
|
|
77
|
+
* Every change is announced as a bubbling `change` event, so anything that
|
|
78
|
+
* listens on the field — a form, a calculator — hears a picked date the way
|
|
79
|
+
* it hears a typed word.
|
|
80
|
+
*/ function PickerAnswer(props) {
|
|
81
|
+
const { name, value, required, onInvalid } = props;
|
|
82
|
+
const ref = useRef(null);
|
|
83
|
+
const announced = useRef(value);
|
|
84
|
+
useEffect(()=>{
|
|
85
|
+
var _ref_current;
|
|
86
|
+
if (announced.current === value) return;
|
|
87
|
+
announced.current = value;
|
|
88
|
+
(_ref_current = ref.current) == null ? void 0 : _ref_current.dispatchEvent(new Event('change', {
|
|
89
|
+
bubbles: true
|
|
90
|
+
}));
|
|
91
|
+
}, [
|
|
92
|
+
value
|
|
93
|
+
]);
|
|
94
|
+
return /*#__PURE__*/ _jsx("input", {
|
|
95
|
+
ref: ref,
|
|
96
|
+
type: "text",
|
|
97
|
+
name: name || undefined,
|
|
98
|
+
value: value,
|
|
99
|
+
required: Boolean(required),
|
|
100
|
+
onChange: ()=>undefined,
|
|
101
|
+
onInvalid: onInvalid,
|
|
102
|
+
tabIndex: -1,
|
|
103
|
+
"aria-hidden": "true",
|
|
104
|
+
autoComplete: "off",
|
|
105
|
+
style: {
|
|
106
|
+
position: 'absolute',
|
|
107
|
+
left: 0,
|
|
108
|
+
bottom: 0,
|
|
109
|
+
width: '100%',
|
|
110
|
+
height: 1,
|
|
111
|
+
padding: 0,
|
|
112
|
+
border: 0,
|
|
113
|
+
opacity: 0,
|
|
114
|
+
pointerEvents: 'none'
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
/** The frame both pickers share: the adapter, the answer input, the field. */ function PickerFrame(props) {
|
|
119
|
+
const { name, required, answer, fullWidth, rest, focusPicker, children } = props;
|
|
120
|
+
return /*#__PURE__*/ _jsx(LocalizationProvider, {
|
|
121
|
+
dateAdapter: AdapterDayjs,
|
|
122
|
+
children: /*#__PURE__*/ _jsxs(Box, _extends({}, rest, {
|
|
123
|
+
sx: [
|
|
124
|
+
{
|
|
125
|
+
position: 'relative',
|
|
126
|
+
display: fullWidth ? 'flex' : 'inline-flex',
|
|
127
|
+
flexDirection: 'column',
|
|
128
|
+
width: fullWidth ? '100%' : undefined
|
|
129
|
+
},
|
|
130
|
+
...nodeSxList(rest['sx'])
|
|
131
|
+
],
|
|
132
|
+
children: [
|
|
133
|
+
children,
|
|
134
|
+
/*#__PURE__*/ _jsx(PickerAnswer, {
|
|
135
|
+
name: name,
|
|
136
|
+
value: answer,
|
|
137
|
+
required: required,
|
|
138
|
+
onInvalid: focusPicker
|
|
139
|
+
})
|
|
140
|
+
]
|
|
141
|
+
}))
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
/** The text field slot both pickers draw: linked to the Form Label, never floating a label of its own. */ function textFieldSlot(options) {
|
|
145
|
+
const { id, ariaLabel, size, fullWidth, filled, required } = options;
|
|
146
|
+
return _extends({}, id ? {
|
|
147
|
+
id
|
|
148
|
+
} : {}, {
|
|
149
|
+
size: size === 'small' ? 'small' : 'medium',
|
|
150
|
+
fullWidth: Boolean(fullWidth),
|
|
151
|
+
variant: filled ? 'filled' : 'outlined',
|
|
152
|
+
// The Form Label above is the label; the picker's own would float inside.
|
|
153
|
+
label: undefined,
|
|
154
|
+
required: Boolean(required)
|
|
155
|
+
}, ariaLabel ? {
|
|
156
|
+
inputProps: {
|
|
157
|
+
'aria-label': ariaLabel
|
|
158
|
+
}
|
|
159
|
+
} : {});
|
|
160
|
+
}
|
|
161
|
+
/** A day a visitor picks from a calendar; sends it as `2026-10-15`. */ export const AglynDatePicker = /*#__PURE__*/ forwardRef((props, ref)=>{
|
|
162
|
+
const _dropClearedProps = dropClearedProps(props), { name, required, ariaLabel, fullWidth, size, disablePast, disableFuture, minDate, maxDate, defaultToday, datasetFieldId: _datasetFieldId } = _dropClearedProps, rest = _object_without_properties_loose(_dropClearedProps, [
|
|
163
|
+
"name",
|
|
164
|
+
"required",
|
|
165
|
+
"ariaLabel",
|
|
166
|
+
"fullWidth",
|
|
167
|
+
"size",
|
|
168
|
+
"disablePast",
|
|
169
|
+
"disableFuture",
|
|
170
|
+
"minDate",
|
|
171
|
+
"maxDate",
|
|
172
|
+
"defaultToday",
|
|
173
|
+
"datasetFieldId"
|
|
174
|
+
]);
|
|
175
|
+
const id = useContext(FieldIdContext);
|
|
176
|
+
const filled = useFilledInputs();
|
|
177
|
+
const [value, setValue] = useState(null);
|
|
178
|
+
// Today is filled in after the page opens: the server's today and the
|
|
179
|
+
// visitor's are not always the same day.
|
|
180
|
+
useEffect(()=>{
|
|
181
|
+
if (defaultToday) setValue((current)=>current != null ? current : dayjs().startOf('day'));
|
|
182
|
+
}, [
|
|
183
|
+
defaultToday
|
|
184
|
+
]);
|
|
185
|
+
const fieldRef = useRef(null);
|
|
186
|
+
return /*#__PURE__*/ _jsx(PickerFrame, {
|
|
187
|
+
name: name,
|
|
188
|
+
required: required,
|
|
189
|
+
answer: pickerAnswer(value, 'date'),
|
|
190
|
+
fullWidth: fullWidth,
|
|
191
|
+
rest: _extends({}, rest, {
|
|
192
|
+
ref
|
|
193
|
+
}),
|
|
194
|
+
focusPicker: ()=>{
|
|
195
|
+
var _fieldRef_current_querySelector, _fieldRef_current;
|
|
196
|
+
return (_fieldRef_current = fieldRef.current) == null ? void 0 : (_fieldRef_current_querySelector = _fieldRef_current.querySelector('[tabindex="0"], input')) == null ? void 0 : _fieldRef_current_querySelector.focus();
|
|
197
|
+
},
|
|
198
|
+
children: /*#__PURE__*/ _jsx(DatePicker, _extends({
|
|
199
|
+
value: value,
|
|
200
|
+
onChange: (next)=>setValue(next && next.isValid() ? next : null),
|
|
201
|
+
disablePast: Boolean(disablePast),
|
|
202
|
+
disableFuture: Boolean(disableFuture)
|
|
203
|
+
}, parsePickerDay(minDate) ? {
|
|
204
|
+
minDate: parsePickerDay(minDate)
|
|
205
|
+
} : {}, parsePickerDay(maxDate) ? {
|
|
206
|
+
maxDate: parsePickerDay(maxDate)
|
|
207
|
+
} : {}, {
|
|
208
|
+
slotProps: {
|
|
209
|
+
textField: _extends({}, textFieldSlot({
|
|
210
|
+
id,
|
|
211
|
+
ariaLabel,
|
|
212
|
+
size,
|
|
213
|
+
fullWidth,
|
|
214
|
+
filled,
|
|
215
|
+
required
|
|
216
|
+
}), {
|
|
217
|
+
ref: fieldRef
|
|
218
|
+
})
|
|
219
|
+
}
|
|
220
|
+
}))
|
|
221
|
+
});
|
|
222
|
+
});
|
|
223
|
+
AglynDatePicker.displayName = 'AglynDatePicker';
|
|
224
|
+
/** A time a visitor picks from a clock; sends it as `14:30`. */ export const AglynTimePicker = /*#__PURE__*/ forwardRef((props, ref)=>{
|
|
225
|
+
const _dropClearedProps = dropClearedProps(props), { name, required, ariaLabel, fullWidth, size, minutesStep, minTime, maxTime, clock, datasetFieldId: _datasetFieldId } = _dropClearedProps, rest = _object_without_properties_loose(_dropClearedProps, [
|
|
226
|
+
"name",
|
|
227
|
+
"required",
|
|
228
|
+
"ariaLabel",
|
|
229
|
+
"fullWidth",
|
|
230
|
+
"size",
|
|
231
|
+
"minutesStep",
|
|
232
|
+
"minTime",
|
|
233
|
+
"maxTime",
|
|
234
|
+
"clock",
|
|
235
|
+
"datasetFieldId"
|
|
236
|
+
]);
|
|
237
|
+
const id = useContext(FieldIdContext);
|
|
238
|
+
const filled = useFilledInputs();
|
|
239
|
+
const [value, setValue] = useState(null);
|
|
240
|
+
const fieldRef = useRef(null);
|
|
241
|
+
const step = Number(minutesStep);
|
|
242
|
+
return /*#__PURE__*/ _jsx(PickerFrame, {
|
|
243
|
+
name: name,
|
|
244
|
+
required: required,
|
|
245
|
+
answer: pickerAnswer(value, 'time'),
|
|
246
|
+
fullWidth: fullWidth,
|
|
247
|
+
rest: _extends({}, rest, {
|
|
248
|
+
ref
|
|
249
|
+
}),
|
|
250
|
+
focusPicker: ()=>{
|
|
251
|
+
var _fieldRef_current_querySelector, _fieldRef_current;
|
|
252
|
+
return (_fieldRef_current = fieldRef.current) == null ? void 0 : (_fieldRef_current_querySelector = _fieldRef_current.querySelector('[tabindex="0"], input')) == null ? void 0 : _fieldRef_current_querySelector.focus();
|
|
253
|
+
},
|
|
254
|
+
children: /*#__PURE__*/ _jsx(TimePicker, _extends({
|
|
255
|
+
value: value,
|
|
256
|
+
onChange: (next)=>setValue(next && next.isValid() ? next : null)
|
|
257
|
+
}, Number.isInteger(step) && step > 1 ? {
|
|
258
|
+
minutesStep: step
|
|
259
|
+
} : {}, parsePickerTime(minTime) ? {
|
|
260
|
+
minTime: parsePickerTime(minTime)
|
|
261
|
+
} : {}, parsePickerTime(maxTime) ? {
|
|
262
|
+
maxTime: parsePickerTime(maxTime)
|
|
263
|
+
} : {}, clock === '12' ? {
|
|
264
|
+
ampm: true
|
|
265
|
+
} : clock === '24' ? {
|
|
266
|
+
ampm: false
|
|
267
|
+
} : {}, {
|
|
268
|
+
slotProps: {
|
|
269
|
+
textField: _extends({}, textFieldSlot({
|
|
270
|
+
id,
|
|
271
|
+
ariaLabel,
|
|
272
|
+
size,
|
|
273
|
+
fullWidth,
|
|
274
|
+
filled,
|
|
275
|
+
required
|
|
276
|
+
}), {
|
|
277
|
+
ref: fieldRef
|
|
278
|
+
})
|
|
279
|
+
}
|
|
280
|
+
}))
|
|
281
|
+
});
|
|
282
|
+
});
|
|
283
|
+
AglynTimePicker.displayName = 'AglynTimePicker';
|
|
284
|
+
// ── Schemas ─────────────────────────────────────────────────────────────────
|
|
285
|
+
export const datePickerSchema = {
|
|
286
|
+
$id: DATE_PICKER_ID,
|
|
287
|
+
pluginId: BUNDLE_ID,
|
|
288
|
+
displayName: 'Date Picker',
|
|
289
|
+
description: 'A date a visitor picks from a calendar. It is sent as the day, such as 2026-10-15.',
|
|
290
|
+
category,
|
|
291
|
+
icon: {
|
|
292
|
+
path: mdiCalendarMonthOutline.path,
|
|
293
|
+
sx: {
|
|
294
|
+
color: 'info.main'
|
|
295
|
+
}
|
|
296
|
+
},
|
|
297
|
+
flags: {
|
|
298
|
+
selfClosing: Aglyn.FEATURE_FLAG.ENABLED
|
|
299
|
+
},
|
|
300
|
+
attributes: [
|
|
301
|
+
FIELD_NAME,
|
|
302
|
+
FIELD_DATASET_FIELD,
|
|
303
|
+
FIELD_REQUIRED,
|
|
304
|
+
{
|
|
305
|
+
name: 'disablePast',
|
|
306
|
+
label: 'No past dates?',
|
|
307
|
+
description: 'Days before today cannot be picked: for a booking, a delivery or a job date.',
|
|
308
|
+
component: Aglyn.FieldComponentType.SWITCH
|
|
309
|
+
},
|
|
310
|
+
{
|
|
311
|
+
name: 'disableFuture',
|
|
312
|
+
label: 'No future dates?',
|
|
313
|
+
description: 'Days after today cannot be picked: for a date of birth or the day something happened.',
|
|
314
|
+
component: Aglyn.FieldComponentType.SWITCH
|
|
315
|
+
},
|
|
316
|
+
{
|
|
317
|
+
name: 'minDate',
|
|
318
|
+
label: 'Earliest date',
|
|
319
|
+
description: 'The first day that can be picked. Leave it empty for no limit.',
|
|
320
|
+
component: Aglyn.FieldComponentType.DATE_PICKER
|
|
321
|
+
},
|
|
322
|
+
{
|
|
323
|
+
name: 'maxDate',
|
|
324
|
+
label: 'Latest date',
|
|
325
|
+
description: 'The last day that can be picked. Leave it empty for no limit.',
|
|
326
|
+
component: Aglyn.FieldComponentType.DATE_PICKER
|
|
327
|
+
},
|
|
328
|
+
{
|
|
329
|
+
name: 'defaultToday',
|
|
330
|
+
label: 'Start on today?',
|
|
331
|
+
description: 'Fills in today’s date when the page opens; the visitor can still change it.',
|
|
332
|
+
component: Aglyn.FieldComponentType.SWITCH
|
|
333
|
+
},
|
|
334
|
+
FIELD_INPUT_SIZE,
|
|
335
|
+
FIELD_FULL_WIDTH,
|
|
336
|
+
FIELD_ARIA_LABEL
|
|
337
|
+
]
|
|
338
|
+
};
|
|
339
|
+
export const timePickerSchema = {
|
|
340
|
+
$id: TIME_PICKER_ID,
|
|
341
|
+
pluginId: BUNDLE_ID,
|
|
342
|
+
displayName: 'Time Picker',
|
|
343
|
+
description: 'A time of day a visitor picks from a clock. It is sent in 24-hour time, such as 14:30.',
|
|
344
|
+
category,
|
|
345
|
+
icon: {
|
|
346
|
+
path: mdiClockOutline.path,
|
|
347
|
+
sx: {
|
|
348
|
+
color: 'info.main'
|
|
349
|
+
}
|
|
350
|
+
},
|
|
351
|
+
flags: {
|
|
352
|
+
selfClosing: Aglyn.FEATURE_FLAG.ENABLED
|
|
353
|
+
},
|
|
354
|
+
attributes: [
|
|
355
|
+
FIELD_NAME,
|
|
356
|
+
FIELD_DATASET_FIELD,
|
|
357
|
+
FIELD_REQUIRED,
|
|
358
|
+
{
|
|
359
|
+
name: 'minutesStep',
|
|
360
|
+
label: 'Minutes in steps of',
|
|
361
|
+
description: 'The minutes offered: every minute, or every 5, 10, 15 or 30.',
|
|
362
|
+
component: Aglyn.FieldComponentType.SELECT,
|
|
363
|
+
options: [
|
|
364
|
+
{
|
|
365
|
+
value: '1',
|
|
366
|
+
label: '1 minute'
|
|
367
|
+
},
|
|
368
|
+
{
|
|
369
|
+
value: '5',
|
|
370
|
+
label: '5 minutes'
|
|
371
|
+
},
|
|
372
|
+
{
|
|
373
|
+
value: '10',
|
|
374
|
+
label: '10 minutes'
|
|
375
|
+
},
|
|
376
|
+
{
|
|
377
|
+
value: '15',
|
|
378
|
+
label: '15 minutes'
|
|
379
|
+
},
|
|
380
|
+
{
|
|
381
|
+
value: '30',
|
|
382
|
+
label: '30 minutes'
|
|
383
|
+
}
|
|
384
|
+
]
|
|
385
|
+
},
|
|
386
|
+
{
|
|
387
|
+
name: 'minTime',
|
|
388
|
+
label: 'Earliest time',
|
|
389
|
+
description: 'The earliest time that can be picked, such as your opening time.',
|
|
390
|
+
component: Aglyn.FieldComponentType.TIME_PICKER
|
|
391
|
+
},
|
|
392
|
+
{
|
|
393
|
+
name: 'maxTime',
|
|
394
|
+
label: 'Latest time',
|
|
395
|
+
description: 'The latest time that can be picked, such as your closing time.',
|
|
396
|
+
component: Aglyn.FieldComponentType.TIME_PICKER
|
|
397
|
+
},
|
|
398
|
+
{
|
|
399
|
+
name: 'clock',
|
|
400
|
+
label: 'Clock',
|
|
401
|
+
description: 'How the time is shown to the visitor. Automatic follows their language; it is always sent in 24-hour time.',
|
|
402
|
+
component: Aglyn.FieldComponentType.SELECT,
|
|
403
|
+
options: [
|
|
404
|
+
{
|
|
405
|
+
value: 'auto',
|
|
406
|
+
label: 'Automatic'
|
|
407
|
+
},
|
|
408
|
+
{
|
|
409
|
+
value: '12',
|
|
410
|
+
label: '12-hour (2:30 PM)'
|
|
411
|
+
},
|
|
412
|
+
{
|
|
413
|
+
value: '24',
|
|
414
|
+
label: '24-hour (14:30)'
|
|
415
|
+
}
|
|
416
|
+
]
|
|
417
|
+
},
|
|
418
|
+
FIELD_INPUT_SIZE,
|
|
419
|
+
FIELD_FULL_WIDTH,
|
|
420
|
+
FIELD_ARIA_LABEL
|
|
421
|
+
]
|
|
422
|
+
};
|
|
423
|
+
// ── Presets ─────────────────────────────────────────────────────────────────
|
|
424
|
+
export const formPickerPresets = [
|
|
425
|
+
preset(generatePresetId(DATE_PICKER_ID), 'Date Picker', 'A calendar date on its own.', mdiCalendarMonthOutline.path, node(DATE_PICKER_ID, {
|
|
426
|
+
name: 'date'
|
|
427
|
+
})),
|
|
428
|
+
preset(generatePresetId(TIME_PICKER_ID), 'Time Picker', 'A time of day on its own.', mdiClockOutline.path, node(TIME_PICKER_ID, {
|
|
429
|
+
name: 'time'
|
|
430
|
+
})),
|
|
431
|
+
preset(generatePresetId(FORM_CONTROL_ID, 'date'), 'Date field', 'A label and a date picked from a calendar.', mdiCalendarMonthOutline.path, node(FORM_CONTROL_ID, {
|
|
432
|
+
fullWidth: true
|
|
433
|
+
}, [
|
|
434
|
+
node(FORM_LABEL_ID, {
|
|
435
|
+
children: 'Date'
|
|
436
|
+
}),
|
|
437
|
+
node(DATE_PICKER_ID, {
|
|
438
|
+
name: 'date',
|
|
439
|
+
fullWidth: true
|
|
440
|
+
})
|
|
441
|
+
])),
|
|
442
|
+
preset(generatePresetId(FORM_CONTROL_ID, 'time'), 'Time field', 'A label and a time picked from a clock.', mdiClockOutline.path, node(FORM_CONTROL_ID, {
|
|
443
|
+
fullWidth: true
|
|
444
|
+
}, [
|
|
445
|
+
node(FORM_LABEL_ID, {
|
|
446
|
+
children: 'Time'
|
|
447
|
+
}),
|
|
448
|
+
node(TIME_PICKER_ID, {
|
|
449
|
+
name: 'time',
|
|
450
|
+
fullWidth: true
|
|
451
|
+
})
|
|
452
|
+
]))
|
|
453
|
+
];
|
|
454
|
+
|
|
455
|
+
//# sourceMappingURL=form-pickers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/mui/src/lib/components/form-pickers.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport * as Aglyn from '@aglyn/aglyn'\nimport { mdiCalendarMonthOutline, mdiClockOutline } from '@aglyn/shared-data-mdi'\nimport Box from '@mui/material/Box'\nimport type { SxProps, Theme } from '@mui/material/styles'\nimport { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'\nimport { DatePicker } from '@mui/x-date-pickers/DatePicker'\nimport { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider'\nimport { TimePicker } from '@mui/x-date-pickers/TimePicker'\nimport dayjs, { type Dayjs } from 'dayjs'\nimport {\n forwardRef,\n type ReactNode,\n useContext,\n useEffect,\n useRef,\n useState,\n} from 'react'\nimport { BUNDLE_ID } from '../constants/bundle-common'\nimport { dropClearedProps } from '../utils/drop-cleared-props'\nimport { generatePresetId } from '../utils/generate-preset-id'\nimport {\n category,\n FIELD_ARIA_LABEL,\n FIELD_DATASET_FIELD,\n FIELD_FULL_WIDTH,\n FIELD_INPUT_SIZE,\n FIELD_NAME,\n FIELD_REQUIRED,\n FieldIdContext,\n FORM_CONTROL_ID,\n FORM_LABEL_ID,\n node,\n nodeSxList,\n preset,\n useFilledInputs,\n} from './form-controls'\n\n/*\n * THE DATE AND TIME FIELDS OF A FORM: MUI X's pickers, one element each.\n *\n * They are their own module, loaded only on a page that places one, because\n * the pickers and their date library are the heaviest thing a form can hold\n * and most forms hold neither. They sit in a Form Control beside a Form\n * Label exactly like an Input, and like every form element they know nothing\n * about the form around them: each carries a NAME, and the form sends what\n * the browser sends.\n *\n * What the browser sends is the part that needed care. A picker's own field\n * would post the day as the visitor reads it (\"10/15/2026\"), which changes\n * with the language and cannot be sorted or stored as a date. So the picker\n * is left unnamed and the answer is posted from an input of our own, as the\n * ISO day (`2026-10-15`) or the 24-hour time (`14:30`) — the same text the\n * browser's own date and time inputs post, and what a dataset date stores.\n *\n * Component ids are persisted in documents; never rename them.\n */\nexport const DATE_PICKER_ID: Aglyn.ComponentId = 'muiDatePicker'\nexport const TIME_PICKER_ID: Aglyn.ComponentId = 'muiTimePicker'\n\nconst DAY = 'YYYY-MM-DD'\nconst CLOCK = 'HH:mm'\n\n/** `2026-10-15` as a picker value, or `null` for anything that is not a day. */\nexport function parsePickerDay(value: unknown): Dayjs | null {\n const text = typeof value === 'string' ? value.trim() : ''\n if (!/^\\d{4}-\\d{2}-\\d{2}$/.test(text)) return null\n const day = dayjs(text)\n return day.isValid() && day.format(DAY) === text ? day : null\n}\n\n/** `14:30` as a picker value (today at that time), or `null`. */\nexport function parsePickerTime(value: unknown): Dayjs | null {\n const match = /^([01]\\d|2[0-3]):([0-5]\\d)$/.exec(typeof value === 'string' ? value.trim() : '')\n if (!match) return null\n return dayjs().hour(Number(match[1])).minute(Number(match[2])).second(0).millisecond(0)\n}\n\n/** What a picked value posts as: the ISO day or the 24-hour time, or nothing. */\nexport function pickerAnswer(value: Dayjs | null, kind: 'date' | 'time'): string {\n if (!value || !value.isValid()) return ''\n return value.format(kind === 'date' ? DAY : CLOCK)\n}\n\ninterface PickerFieldProps {\n /** What the answer is sent as. */\n name?: string\n required?: boolean\n /**\n * A name for assistive technology when no Form Label sits beside the\n * picker.\n */\n ariaLabel?: string\n fullWidth?: boolean\n size?: 'small' | 'medium'\n [key: string]: any\n}\n\nexport interface AglynDatePickerProps extends PickerFieldProps {\n /** Days before today cannot be picked. */\n disablePast?: boolean\n /** Days after today cannot be picked. */\n disableFuture?: boolean\n /** The first day that can be picked, as `YYYY-MM-DD`. */\n minDate?: string\n /** The last day that can be picked, as `YYYY-MM-DD`. */\n maxDate?: string\n /** Starts filled in with today. */\n defaultToday?: boolean\n}\n\nexport interface AglynTimePickerProps extends PickerFieldProps {\n /** The minutes the clock steps by. */\n minutesStep?: number | string\n /** The earliest time that can be picked, as `HH:mm`. */\n minTime?: string\n /** The latest time that can be picked, as `HH:mm`. */\n maxTime?: string\n /** `12` or `24`; anything else follows the visitor's language. */\n clock?: string\n}\n\n/**\n * The input a picker's answer is posted from. It is a real text input, not\n * `type=\"hidden\"` and not read-only, because those two are exactly the inputs\n * the browser never checks: a required picker left empty must stop the form\n * like any other required field. It sits invisibly under the picker, so the\n * browser's \"Please fill in this field\" points at the picker, and it hands\n * focus to the picker when that happens.\n *\n * Every change is announced as a bubbling `change` event, so anything that\n * listens on the field — a form, a calculator — hears a picked date the way\n * it hears a typed word.\n */\nfunction PickerAnswer(props: {\n name?: string\n value: string\n required?: boolean\n onInvalid: () => void\n}) {\n const { name, value, required, onInvalid } = props\n const ref = useRef<HTMLInputElement>(null)\n const announced = useRef(value)\n useEffect(() => {\n if (announced.current === value) return\n announced.current = value\n ref.current?.dispatchEvent(new Event('change', { bubbles: true }))\n }, [value])\n return (\n <input\n ref={ref}\n type=\"text\"\n name={name || undefined}\n value={value}\n required={Boolean(required)}\n onChange={() => undefined}\n onInvalid={onInvalid}\n tabIndex={-1}\n aria-hidden=\"true\"\n autoComplete=\"off\"\n style={{\n position: 'absolute',\n left: 0,\n bottom: 0,\n width: '100%',\n height: 1,\n padding: 0,\n border: 0,\n opacity: 0,\n pointerEvents: 'none',\n }}\n />\n )\n}\n\n/** The frame both pickers share: the adapter, the answer input, the field. */\nfunction PickerFrame(props: {\n name?: string\n required?: boolean\n answer: string\n fullWidth?: boolean\n rest: Record<string, unknown>\n focusPicker: () => void\n children: ReactNode\n}) {\n const { name, required, answer, fullWidth, rest, focusPicker, children } = props\n return (\n <LocalizationProvider dateAdapter={AdapterDayjs}>\n <Box\n {...rest}\n sx={[\n {\n position: 'relative',\n display: fullWidth ? 'flex' : 'inline-flex',\n flexDirection: 'column',\n width: fullWidth ? '100%' : undefined,\n },\n ...nodeSxList(rest['sx']),\n ] as SxProps<Theme>}\n >\n {children}\n <PickerAnswer name={name} value={answer} required={required} onInvalid={focusPicker} />\n </Box>\n </LocalizationProvider>\n )\n}\n\n/** The text field slot both pickers draw: linked to the Form Label, never floating a label of its own. */\nfunction textFieldSlot(options: {\n id?: string\n ariaLabel?: string\n size?: 'small' | 'medium'\n fullWidth?: boolean\n filled: boolean\n required?: boolean\n}) {\n const { id, ariaLabel, size, fullWidth, filled, required } = options\n return {\n ...(id ? { id } : {}),\n size: size === 'small' ? ('small' as const) : ('medium' as const),\n fullWidth: Boolean(fullWidth),\n variant: filled ? ('filled' as const) : ('outlined' as const),\n // The Form Label above is the label; the picker's own would float inside.\n label: undefined,\n required: Boolean(required),\n ...(ariaLabel ? { inputProps: { 'aria-label': ariaLabel } } : {}),\n }\n}\n\n/** A day a visitor picks from a calendar; sends it as `2026-10-15`. */\nexport const AglynDatePicker = forwardRef<HTMLDivElement, AglynDatePickerProps>(\n (props, ref) => {\n const {\n name,\n required,\n ariaLabel,\n fullWidth,\n size,\n disablePast,\n disableFuture,\n minDate,\n maxDate,\n defaultToday,\n datasetFieldId: _datasetFieldId,\n ...rest\n } = dropClearedProps(props)\n const id = useContext(FieldIdContext)\n const filled = useFilledInputs()\n const [value, setValue] = useState<Dayjs | null>(null)\n // Today is filled in after the page opens: the server's today and the\n // visitor's are not always the same day.\n useEffect(() => {\n if (defaultToday) setValue((current) => current ?? dayjs().startOf('day'))\n }, [defaultToday])\n const fieldRef = useRef<HTMLDivElement>(null)\n return (\n <PickerFrame\n name={name}\n required={required}\n answer={pickerAnswer(value, 'date')}\n fullWidth={fullWidth}\n rest={{ ...rest, ref }}\n focusPicker={() => fieldRef.current?.querySelector<HTMLElement>('[tabindex=\"0\"], input')?.focus()}\n >\n <DatePicker\n value={value}\n onChange={(next) => setValue(next && next.isValid() ? next : null)}\n disablePast={Boolean(disablePast)}\n disableFuture={Boolean(disableFuture)}\n {...(parsePickerDay(minDate) ? { minDate: parsePickerDay(minDate) as Dayjs } : {})}\n {...(parsePickerDay(maxDate) ? { maxDate: parsePickerDay(maxDate) as Dayjs } : {})}\n slotProps={{\n textField: {\n ...textFieldSlot({ id, ariaLabel, size, fullWidth, filled, required }),\n ref: fieldRef,\n },\n }}\n />\n </PickerFrame>\n )\n },\n)\nAglynDatePicker.displayName = 'AglynDatePicker'\n\n/** A time a visitor picks from a clock; sends it as `14:30`. */\nexport const AglynTimePicker = forwardRef<HTMLDivElement, AglynTimePickerProps>(\n (props, ref) => {\n const {\n name,\n required,\n ariaLabel,\n fullWidth,\n size,\n minutesStep,\n minTime,\n maxTime,\n clock,\n datasetFieldId: _datasetFieldId,\n ...rest\n } = dropClearedProps(props)\n const id = useContext(FieldIdContext)\n const filled = useFilledInputs()\n const [value, setValue] = useState<Dayjs | null>(null)\n const fieldRef = useRef<HTMLDivElement>(null)\n const step = Number(minutesStep)\n return (\n <PickerFrame\n name={name}\n required={required}\n answer={pickerAnswer(value, 'time')}\n fullWidth={fullWidth}\n rest={{ ...rest, ref }}\n focusPicker={() => fieldRef.current?.querySelector<HTMLElement>('[tabindex=\"0\"], input')?.focus()}\n >\n <TimePicker\n value={value}\n onChange={(next) => setValue(next && next.isValid() ? next : null)}\n {...(Number.isInteger(step) && step > 1 ? { minutesStep: step } : {})}\n {...(parsePickerTime(minTime) ? { minTime: parsePickerTime(minTime) as Dayjs } : {})}\n {...(parsePickerTime(maxTime) ? { maxTime: parsePickerTime(maxTime) as Dayjs } : {})}\n {...(clock === '12' ? { ampm: true } : clock === '24' ? { ampm: false } : {})}\n slotProps={{\n textField: {\n ...textFieldSlot({ id, ariaLabel, size, fullWidth, filled, required }),\n ref: fieldRef,\n },\n }}\n />\n </PickerFrame>\n )\n },\n)\nAglynTimePicker.displayName = 'AglynTimePicker'\n\n// ── Schemas ─────────────────────────────────────────────────────────────────\n\nexport const datePickerSchema: Aglyn.ComponentSchema<AglynDatePickerProps> = {\n $id: DATE_PICKER_ID,\n pluginId: BUNDLE_ID,\n displayName: 'Date Picker',\n description:\n 'A date a visitor picks from a calendar. It is sent as the day, such as 2026-10-15.',\n category,\n icon: { path: mdiCalendarMonthOutline.path, sx: { color: 'info.main' } },\n flags: { selfClosing: Aglyn.FEATURE_FLAG.ENABLED },\n attributes: [\n FIELD_NAME,\n FIELD_DATASET_FIELD,\n FIELD_REQUIRED,\n {\n name: 'disablePast',\n label: 'No past dates?',\n description:\n 'Days before today cannot be picked: for a booking, a delivery or a job date.',\n component: Aglyn.FieldComponentType.SWITCH,\n },\n {\n name: 'disableFuture',\n label: 'No future dates?',\n description:\n 'Days after today cannot be picked: for a date of birth or the day something happened.',\n component: Aglyn.FieldComponentType.SWITCH,\n },\n {\n name: 'minDate',\n label: 'Earliest date',\n description: 'The first day that can be picked. Leave it empty for no limit.',\n component: Aglyn.FieldComponentType.DATE_PICKER,\n },\n {\n name: 'maxDate',\n label: 'Latest date',\n description: 'The last day that can be picked. Leave it empty for no limit.',\n component: Aglyn.FieldComponentType.DATE_PICKER,\n },\n {\n name: 'defaultToday',\n label: 'Start on today?',\n description:\n 'Fills in today’s date when the page opens; the visitor can still change it.',\n component: Aglyn.FieldComponentType.SWITCH,\n },\n FIELD_INPUT_SIZE,\n FIELD_FULL_WIDTH,\n FIELD_ARIA_LABEL,\n ],\n}\n\nexport const timePickerSchema: Aglyn.ComponentSchema<AglynTimePickerProps> = {\n $id: TIME_PICKER_ID,\n pluginId: BUNDLE_ID,\n displayName: 'Time Picker',\n description:\n 'A time of day a visitor picks from a clock. It is sent in 24-hour time, such as 14:30.',\n category,\n icon: { path: mdiClockOutline.path, sx: { color: 'info.main' } },\n flags: { selfClosing: Aglyn.FEATURE_FLAG.ENABLED },\n attributes: [\n FIELD_NAME,\n FIELD_DATASET_FIELD,\n FIELD_REQUIRED,\n {\n name: 'minutesStep',\n label: 'Minutes in steps of',\n description: 'The minutes offered: every minute, or every 5, 10, 15 or 30.',\n component: Aglyn.FieldComponentType.SELECT,\n options: [\n { value: '1', label: '1 minute' },\n { value: '5', label: '5 minutes' },\n { value: '10', label: '10 minutes' },\n { value: '15', label: '15 minutes' },\n { value: '30', label: '30 minutes' },\n ],\n },\n {\n name: 'minTime',\n label: 'Earliest time',\n description: 'The earliest time that can be picked, such as your opening time.',\n component: Aglyn.FieldComponentType.TIME_PICKER,\n },\n {\n name: 'maxTime',\n label: 'Latest time',\n description: 'The latest time that can be picked, such as your closing time.',\n component: Aglyn.FieldComponentType.TIME_PICKER,\n },\n {\n name: 'clock',\n label: 'Clock',\n description:\n 'How the time is shown to the visitor. Automatic follows their language; it is always sent in 24-hour time.',\n component: Aglyn.FieldComponentType.SELECT,\n options: [\n { value: 'auto', label: 'Automatic' },\n { value: '12', label: '12-hour (2:30 PM)' },\n { value: '24', label: '24-hour (14:30)' },\n ],\n },\n FIELD_INPUT_SIZE,\n FIELD_FULL_WIDTH,\n FIELD_ARIA_LABEL,\n ],\n}\n\n// ── Presets ─────────────────────────────────────────────────────────────────\n\nexport const formPickerPresets: Aglyn.PresetSchema[] = [\n preset(\n generatePresetId(DATE_PICKER_ID),\n 'Date Picker',\n 'A calendar date on its own.',\n mdiCalendarMonthOutline.path,\n node(DATE_PICKER_ID, { name: 'date' }),\n ),\n preset(\n generatePresetId(TIME_PICKER_ID),\n 'Time Picker',\n 'A time of day on its own.',\n mdiClockOutline.path,\n node(TIME_PICKER_ID, { name: 'time' }),\n ),\n preset(\n generatePresetId(FORM_CONTROL_ID, 'date'),\n 'Date field',\n 'A label and a date picked from a calendar.',\n mdiCalendarMonthOutline.path,\n node(FORM_CONTROL_ID, { fullWidth: true }, [\n node(FORM_LABEL_ID, { children: 'Date' }),\n node(DATE_PICKER_ID, { name: 'date', fullWidth: true }),\n ]),\n ),\n preset(\n generatePresetId(FORM_CONTROL_ID, 'time'),\n 'Time field',\n 'A label and a time picked from a clock.',\n mdiClockOutline.path,\n node(FORM_CONTROL_ID, { fullWidth: true }, [\n node(FORM_LABEL_ID, { children: 'Time' }),\n node(TIME_PICKER_ID, { name: 'time', fullWidth: true }),\n ]),\n ),\n]\n"],"names":["Aglyn","mdiCalendarMonthOutline","mdiClockOutline","Box","AdapterDayjs","DatePicker","LocalizationProvider","TimePicker","dayjs","forwardRef","useContext","useEffect","useRef","useState","BUNDLE_ID","dropClearedProps","generatePresetId","category","FIELD_ARIA_LABEL","FIELD_DATASET_FIELD","FIELD_FULL_WIDTH","FIELD_INPUT_SIZE","FIELD_NAME","FIELD_REQUIRED","FieldIdContext","FORM_CONTROL_ID","FORM_LABEL_ID","node","nodeSxList","preset","useFilledInputs","DATE_PICKER_ID","TIME_PICKER_ID","DAY","CLOCK","parsePickerDay","value","text","trim","test","day","isValid","format","parsePickerTime","match","exec","hour","Number","minute","second","millisecond","pickerAnswer","kind","PickerAnswer","props","name","required","onInvalid","ref","announced","current","dispatchEvent","Event","bubbles","input","type","undefined","Boolean","onChange","tabIndex","aria-hidden","autoComplete","style","position","left","bottom","width","height","padding","border","opacity","pointerEvents","PickerFrame","answer","fullWidth","rest","focusPicker","children","dateAdapter","sx","display","flexDirection","textFieldSlot","options","id","ariaLabel","size","filled","variant","label","inputProps","AglynDatePicker","disablePast","disableFuture","minDate","maxDate","defaultToday","datasetFieldId","_datasetFieldId","setValue","startOf","fieldRef","querySelector","focus","next","slotProps","textField","displayName","AglynTimePicker","minutesStep","minTime","maxTime","clock","step","isInteger","ampm","datePickerSchema","$id","pluginId","description","icon","path","color","flags","selfClosing","FEATURE_FLAG","ENABLED","attributes","component","FieldComponentType","SWITCH","DATE_PICKER","timePickerSchema","SELECT","TIME_PICKER","formPickerPresets"],"mappings":"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"}
|
|
@@ -95,6 +95,7 @@ export declare const list: () => Promise<typeof import("./components/list")>;
|
|
|
95
95
|
export declare const listItem: () => Promise<typeof import("./components/list-item")>;
|
|
96
96
|
export declare const listItemText: () => Promise<typeof import("./components/list-item-text")>;
|
|
97
97
|
export declare const formControls: () => Promise<typeof import("./components/form-controls")>;
|
|
98
|
+
export declare const formPickers: () => Promise<typeof import("./components/form-pickers")>;
|
|
98
99
|
export declare const blocks: () => Promise<typeof import("./components/blocks")>;
|
|
99
100
|
export declare const dataTable: () => Promise<typeof import("./components/data-table")>;
|
|
100
101
|
export declare const collectionEntries: () => Promise<typeof import("./components/collection-entries")>;
|
package/src/lib/element-tier.js
CHANGED
|
@@ -91,7 +91,23 @@ import * as icon from "./components/icon.js";
|
|
|
91
91
|
/** The elements a page places less often, each loaded when it is placed. */ export const list = ()=>import("./components/list.js");
|
|
92
92
|
export const listItem = ()=>import("./components/list-item.js");
|
|
93
93
|
export const listItemText = ()=>import("./components/list-item-text.js");
|
|
94
|
-
|
|
94
|
+
// One import shared by every caller: the form elements' entries and the
|
|
95
|
+
// pickers' (below) each ask for it, and a loader that hands a second caller a
|
|
96
|
+
// module still being evaluated (jiti, in the AI palette and axe guards) would
|
|
97
|
+
// give it empty exports. A browser's module map shares the promise anyway; a
|
|
98
|
+
// failed load is forgotten so the next placement retries.
|
|
99
|
+
let formControlsLoad;
|
|
100
|
+
export const formControls = ()=>formControlsLoad != null ? formControlsLoad : formControlsLoad = import("./components/form-controls.js").catch((error)=>{
|
|
101
|
+
formControlsLoad = undefined;
|
|
102
|
+
throw error;
|
|
103
|
+
});
|
|
104
|
+
// The date and time pickers carry MUI X and a date library: their own chunk,
|
|
105
|
+
// fetched only by a page that places one (AGL-3825). It is imported FROM
|
|
106
|
+
// form-controls, never beside it: a picker always sits in a Form Control, and
|
|
107
|
+
// a chunk loaded as a child of form-controls' group is handed only what that
|
|
108
|
+
// group lacks, where a sibling import would carry its own copy of the Form
|
|
109
|
+
// Control, the input pieces and everything they share.
|
|
110
|
+
export const formPickers = ()=>formControls().then((module)=>module.loadFormPickers());
|
|
95
111
|
export const blocks = ()=>import("./components/blocks.js");
|
|
96
112
|
export const dataTable = ()=>import("./components/data-table.js");
|
|
97
113
|
// The collection family is one module per element (AGL-3401): a page placing
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../libs/plugins/mui/src/lib/element-tier.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport * as appBar from './components/app-bar'\nimport * as toolbar from './components/toolbar'\nimport * as layoutSlot from './components/layout-slot'\nimport * as documentRoot from './components/document-root'\nimport * as section from './components/section'\nimport * as container from './components/container'\nimport * as box from './components/box'\nimport * as stack from './components/stack'\nimport * as grid from './components/grid'\nimport * as paper from './components/paper'\nimport * as typography from './components/typography'\nimport * as inlineText from './components/inline-text'\nimport * as button from './components/button'\nimport * as screenLink from './components/screen-link'\nimport * as linkBox from './components/link-box'\nimport * as image from './components/image'\nimport * as icon from './components/icon'\n\n/**\n * The element tier: how a page's mui elements reach it (AGL-3401).\n *\n * `plugin.ts` imports this module lazily, and every element module is reached\n * through it — the common ones as part of it, the rest by an `import()` written\n * HERE. Where that `import()` is written is the whole point.\n *\n * ## Why not sixty sibling imports\n *\n * Each `import()` is a chunk group, and Turbopack gives each group every\n * module it needs that its PARENT group has not already loaded. Sixty element\n * imports written side by side in `plugin.ts` were sixty siblings: none of\n * them could count on another having loaded `@mui/material/Button`, so the\n * chunker merged a copy of Button, ButtonBase and its ripple, SvgIcon, Paper,\n * Stack, the link helpers and the field presets into group after group. On a\n * production aglyn.com build 30% of the module code a page downloaded was a\n * second-or-later copy (Button eight times, SvgIcon fifteen).\n *\n * No chunking switch fixes that without trading it for requests: turning the\n * merge off removed the copies and cost a page 280 files, which compress\n * worse than the bytes they saved (measured in AGL-3401).\n *\n * ## What this module does instead\n *\n * `CORE_ELEMENTS` are the elements nearly every page places — its frame, its\n * layout, its text, its links, buttons and images. They load together as one\n * chunk, and so do the MUI modules they share. Every other element's\n * `import()` is written below, which makes its group a CHILD of this one:\n * whatever this chunk carries is available to it, so none of it is copied.\n *\n * The price is on either side of that line. A page that places none of the\n * core elements but the frame still downloads them (a six-element page paid\n * 3.7 KB more), and an element outside the core arrives one request later,\n * after this chunk. Moving an element into the core is right when most pages\n * place it; `check:tenant-wire-weight`'s duplication measure is what says\n * whether the copies are coming back.\n *\n * Each core element is held as a module namespace, because the registry reads\n * a component, its schema and its presets off it by export name.\n */\n\nexport const CORE_ELEMENTS = {\n appBar,\n toolbar,\n layoutSlot,\n documentRoot,\n section,\n container,\n box,\n stack,\n grid,\n paper,\n typography,\n inlineText,\n button,\n screenLink,\n linkBox,\n image,\n icon,\n}\n\n/** The elements a page places less often, each loaded when it is placed. */\nexport const list = () => import('./components/list')\nexport const listItem = () => import('./components/list-item')\nexport const listItemText = () => import('./components/list-item-text')\nexport const formControls = () => import('./components/form-controls')\nexport const blocks = () => import('./components/blocks')\nexport const dataTable = () => import('./components/data-table')\n// The collection family is one module per element (AGL-3401): a page placing\n// an entries list does not download the entry body's markdown renderer, the\n// author cards' platform marks or the search box.\nexport const collectionEntries = () => import('./components/collection-entries')\nexport const collectionEntryBody = () =>\n import('./components/collection-entry-body')\nexport const collectionRelated = () => import('./components/collection-related')\nexport const collectionShare = () => import('./components/collection-share')\nexport const collectionEntryMeta = () =>\n import('./components/collection-entry-meta')\nexport const collectionEntryAuthor = () =>\n import('./components/collection-entry-author')\nexport const collectionAuthorProfile = () =>\n import('./components/collection-author-profile')\nexport const collectionCategories = () =>\n import('./components/collection-categories')\nexport const collectionSearch = () => import('./components/collection-search')\nexport const video = () => import('./components/video')\nexport const languageSwitcher = () => import('./components/language-switcher')\nexport const reusableInstance = () => import('./components/reusable-instance')\nexport const navMenu = () => import('./components/nav-menu')\nexport const drawer = () => import('./components/drawer')\nexport const product = () => import('./components/product')\nexport const plugin = () => import('./components/plugin')\nexport const customHtml = () => import('./components/custom-html')\nexport const searchBox = () => import('./components/search-box')\nexport const markdown = () => import('./components/markdown')\nexport const card = () => import('./components/card')\nexport const accordion = () => import('./components/accordion')\nexport const tabs = () => import('./components/tabs')\nexport const imageList = () => import('./components/image-list')\nexport const pagination = () => import('./components/pagination')\nexport const breadcrumbs = () => import('./components/breadcrumbs')\nexport const themeModeSwitcher = () =>\n import('./components/theme-mode-switcher')\n"],"names":["appBar","toolbar","layoutSlot","documentRoot","section","container","box","stack","grid","paper","typography","inlineText","button","screenLink","linkBox","image","icon","CORE_ELEMENTS","list","listItem","listItemText","formControls","blocks","dataTable","collectionEntries","collectionEntryBody","collectionRelated","collectionShare","collectionEntryMeta","collectionEntryAuthor","collectionAuthorProfile","collectionCategories","collectionSearch","video","languageSwitcher","reusableInstance","navMenu","drawer","product","plugin","customHtml","searchBox","markdown","card","accordion","tabs","imageList","pagination","breadcrumbs","themeModeSwitcher"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../libs/plugins/mui/src/lib/element-tier.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport * as appBar from './components/app-bar'\nimport * as toolbar from './components/toolbar'\nimport * as layoutSlot from './components/layout-slot'\nimport * as documentRoot from './components/document-root'\nimport * as section from './components/section'\nimport * as container from './components/container'\nimport * as box from './components/box'\nimport * as stack from './components/stack'\nimport * as grid from './components/grid'\nimport * as paper from './components/paper'\nimport * as typography from './components/typography'\nimport * as inlineText from './components/inline-text'\nimport * as button from './components/button'\nimport * as screenLink from './components/screen-link'\nimport * as linkBox from './components/link-box'\nimport * as image from './components/image'\nimport * as icon from './components/icon'\n\n/**\n * The element tier: how a page's mui elements reach it (AGL-3401).\n *\n * `plugin.ts` imports this module lazily, and every element module is reached\n * through it — the common ones as part of it, the rest by an `import()` written\n * HERE. Where that `import()` is written is the whole point.\n *\n * ## Why not sixty sibling imports\n *\n * Each `import()` is a chunk group, and Turbopack gives each group every\n * module it needs that its PARENT group has not already loaded. Sixty element\n * imports written side by side in `plugin.ts` were sixty siblings: none of\n * them could count on another having loaded `@mui/material/Button`, so the\n * chunker merged a copy of Button, ButtonBase and its ripple, SvgIcon, Paper,\n * Stack, the link helpers and the field presets into group after group. On a\n * production aglyn.com build 30% of the module code a page downloaded was a\n * second-or-later copy (Button eight times, SvgIcon fifteen).\n *\n * No chunking switch fixes that without trading it for requests: turning the\n * merge off removed the copies and cost a page 280 files, which compress\n * worse than the bytes they saved (measured in AGL-3401).\n *\n * ## What this module does instead\n *\n * `CORE_ELEMENTS` are the elements nearly every page places — its frame, its\n * layout, its text, its links, buttons and images. They load together as one\n * chunk, and so do the MUI modules they share. Every other element's\n * `import()` is written below, which makes its group a CHILD of this one:\n * whatever this chunk carries is available to it, so none of it is copied.\n *\n * The price is on either side of that line. A page that places none of the\n * core elements but the frame still downloads them (a six-element page paid\n * 3.7 KB more), and an element outside the core arrives one request later,\n * after this chunk. Moving an element into the core is right when most pages\n * place it; `check:tenant-wire-weight`'s duplication measure is what says\n * whether the copies are coming back.\n *\n * Each core element is held as a module namespace, because the registry reads\n * a component, its schema and its presets off it by export name.\n */\n\nexport const CORE_ELEMENTS = {\n appBar,\n toolbar,\n layoutSlot,\n documentRoot,\n section,\n container,\n box,\n stack,\n grid,\n paper,\n typography,\n inlineText,\n button,\n screenLink,\n linkBox,\n image,\n icon,\n}\n\n/** The elements a page places less often, each loaded when it is placed. */\nexport const list = () => import('./components/list')\nexport const listItem = () => import('./components/list-item')\nexport const listItemText = () => import('./components/list-item-text')\n// One import shared by every caller: the form elements' entries and the\n// pickers' (below) each ask for it, and a loader that hands a second caller a\n// module still being evaluated (jiti, in the AI palette and axe guards) would\n// give it empty exports. A browser's module map shares the promise anyway; a\n// failed load is forgotten so the next placement retries.\nlet formControlsLoad: Promise<typeof import('./components/form-controls')> | undefined\nexport const formControls = () =>\n (formControlsLoad ??= import('./components/form-controls').catch((error: unknown) => {\n formControlsLoad = undefined\n throw error\n }))\n// The date and time pickers carry MUI X and a date library: their own chunk,\n// fetched only by a page that places one (AGL-3825). It is imported FROM\n// form-controls, never beside it: a picker always sits in a Form Control, and\n// a chunk loaded as a child of form-controls' group is handed only what that\n// group lacks, where a sibling import would carry its own copy of the Form\n// Control, the input pieces and everything they share.\nexport const formPickers = () =>\n formControls().then((module) => module.loadFormPickers())\nexport const blocks = () => import('./components/blocks')\nexport const dataTable = () => import('./components/data-table')\n// The collection family is one module per element (AGL-3401): a page placing\n// an entries list does not download the entry body's markdown renderer, the\n// author cards' platform marks or the search box.\nexport const collectionEntries = () => import('./components/collection-entries')\nexport const collectionEntryBody = () =>\n import('./components/collection-entry-body')\nexport const collectionRelated = () => import('./components/collection-related')\nexport const collectionShare = () => import('./components/collection-share')\nexport const collectionEntryMeta = () =>\n import('./components/collection-entry-meta')\nexport const collectionEntryAuthor = () =>\n import('./components/collection-entry-author')\nexport const collectionAuthorProfile = () =>\n import('./components/collection-author-profile')\nexport const collectionCategories = () =>\n import('./components/collection-categories')\nexport const collectionSearch = () => import('./components/collection-search')\nexport const video = () => import('./components/video')\nexport const languageSwitcher = () => import('./components/language-switcher')\nexport const reusableInstance = () => import('./components/reusable-instance')\nexport const navMenu = () => import('./components/nav-menu')\nexport const drawer = () => import('./components/drawer')\nexport const product = () => import('./components/product')\nexport const plugin = () => import('./components/plugin')\nexport const customHtml = () => import('./components/custom-html')\nexport const searchBox = () => import('./components/search-box')\nexport const markdown = () => import('./components/markdown')\nexport const card = () => import('./components/card')\nexport const accordion = () => import('./components/accordion')\nexport const tabs = () => import('./components/tabs')\nexport const imageList = () => import('./components/image-list')\nexport const pagination = () => import('./components/pagination')\nexport const breadcrumbs = () => import('./components/breadcrumbs')\nexport const themeModeSwitcher = () =>\n import('./components/theme-mode-switcher')\n"],"names":["appBar","toolbar","layoutSlot","documentRoot","section","container","box","stack","grid","paper","typography","inlineText","button","screenLink","linkBox","image","icon","CORE_ELEMENTS","list","listItem","listItemText","formControlsLoad","formControls","catch","error","undefined","formPickers","then","module","loadFormPickers","blocks","dataTable","collectionEntries","collectionEntryBody","collectionRelated","collectionShare","collectionEntryMeta","collectionEntryAuthor","collectionAuthorProfile","collectionCategories","collectionSearch","video","languageSwitcher","reusableInstance","navMenu","drawer","product","plugin","customHtml","searchBox","markdown","card","accordion","tabs","imageList","pagination","breadcrumbs","themeModeSwitcher"],"mappings":"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"}
|
package/src/lib/plugin.js
CHANGED
|
@@ -51,6 +51,7 @@ const list = viaTier('list');
|
|
|
51
51
|
const listItem = viaTier('listItem');
|
|
52
52
|
const listItemText = viaTier('listItemText');
|
|
53
53
|
const formControls = viaTier('formControls');
|
|
54
|
+
const formPickers = viaTier('formPickers');
|
|
54
55
|
const blocks = viaTier('blocks');
|
|
55
56
|
const dataTable = viaTier('dataTable');
|
|
56
57
|
const collectionEntries = viaTier('collectionEntries');
|
|
@@ -212,6 +213,18 @@ const themeModeSwitcher = viaTier('themeModeSwitcher');
|
|
|
212
213
|
component: 'AglynRating',
|
|
213
214
|
schema: 'ratingSchema'
|
|
214
215
|
},
|
|
216
|
+
// The date and time pickers (form-pickers.tsx), in a chunk of their own.
|
|
217
|
+
muiDatePicker: {
|
|
218
|
+
module: formPickers,
|
|
219
|
+
component: 'AglynDatePicker',
|
|
220
|
+
schema: 'datePickerSchema',
|
|
221
|
+
presets: 'formPickerPresets'
|
|
222
|
+
},
|
|
223
|
+
muiTimePicker: {
|
|
224
|
+
module: formPickers,
|
|
225
|
+
component: 'AglynTimePicker',
|
|
226
|
+
schema: 'timePickerSchema'
|
|
227
|
+
},
|
|
215
228
|
// form, formField moved to @aglyn/plugins-forms (AGL-395).
|
|
216
229
|
videoEmbed: {
|
|
217
230
|
module: blocks,
|