@assistant-ui/react-generative-ui 0.0.23 → 0.0.24
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/temporal.d.ts.map +1 -1
- package/dist/temporal.js +1 -0
- package/dist/temporal.js.map +1 -1
- package/dist/vocabulary/interactive.d.ts.map +1 -1
- package/dist/vocabulary/interactive.js +66 -12
- package/dist/vocabulary/interactive.js.map +1 -1
- package/package.json +4 -3
- package/src/temporal.test.ts +18 -0
- package/src/temporal.ts +4 -0
- package/src/vocabulary/datepicker.dom.test.tsx +315 -6
- package/src/vocabulary/interactive.tsx +73 -12
package/dist/temporal.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"temporal.d.ts","sourceRoot":"","sources":["../src/temporal.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,iBAAiB,GAAG,SAAS,GAAG,SAAS,GAAG,MAAM,CAAC;AAE/D,MAAM,MAAM,eAAe,GAAG;IAC5B,IAAI,EAAE,SAAS,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,iBAAiB,CAAC;IAC7B,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC/B,CAAC;AAEF,MAAM,MAAM,aAAa,GACrB;IAAE,IAAI,EAAE,MAAM,GAAG,aAAa,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAC/C;IACE,IAAI,EAAE,MAAM,GAAG,UAAU,CAAC;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,iBAAiB,CAAC;CAC9B,GACD,eAAe,CAAC;AAUpB,eAAO,MAAM,2BAA2B,GAAI,OAAO,MAAM,KAAG,MAS3D,CAAC;AASF,eAAO,MAAM,gBAAgB,GAAI,OAAO,MAAM,KAAG,aA8BhD,CAAC;AAEF,eAAO,MAAM,oBAAoB,GAC/B,GAAG,QAAQ,CAAC,MAAM,GAAG,SAAS,CAAC,EAAE,KAChC,KAAK,GAAG,CAAC,GAAG,SAYd,CAAC;AAEF,eAAO,MAAM,oBAAoB,GAC/B,OAAO,MAAM,EACb,SAAS,MAAM,KACd;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,gBAAgB,EAAE,OAAO,CAAA;CAAE,GAAG,SAiB/D,CAAC;AAEF,eAAO,MAAM,oBAAoB,GAAI,MAAM,MAAM,EAAE,MAAM,MAAM,KAAG,MAOjE,CAAC;AAWF,eAAO,MAAM,qBAAqB,GAChC,SAAS,eAAe,EACxB,eAAuB,EACvB,6BAA6B,KAC5B,MAUF,CAAC;AAEF,eAAO,MAAM,yBAAyB,GACpC,SAAS,MAAM,EACf,eAAY,KACX,MAAM,GAAG,SAaX,CAAC;AAEF,eAAO,MAAM,mBAAmB,GAAI,OAAO,MAAM,KAAG,MAAM,GAAG,SAI5D,CAAC;AAEF,eAAO,MAAM,kBAAkB,GAC7B,OAAO,MAAM,EACb,gBAAgB,MAAM,KACrB,MAcF,CAAC;AAEF,eAAO,MAAM,eAAe,GAC1B,OAAO,MAAM,EACb,YAAY,iBAAiB,KAC5B,MAQF,CAAC;AAEF,eAAO,MAAM,qBAAqB,GAAI,OAAO,MAAM,KAAG,MAUrD,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAC5B,OAAO,MAAM,EACb,gBAAgB,MAAM,KACrB,
|
|
1
|
+
{"version":3,"file":"temporal.d.ts","sourceRoot":"","sources":["../src/temporal.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,iBAAiB,GAAG,SAAS,GAAG,SAAS,GAAG,MAAM,CAAC;AAE/D,MAAM,MAAM,eAAe,GAAG;IAC5B,IAAI,EAAE,SAAS,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,iBAAiB,CAAC;IAC7B,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC/B,CAAC;AAEF,MAAM,MAAM,aAAa,GACrB;IAAE,IAAI,EAAE,MAAM,GAAG,aAAa,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAC/C;IACE,IAAI,EAAE,MAAM,GAAG,UAAU,CAAC;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,iBAAiB,CAAC;CAC9B,GACD,eAAe,CAAC;AAUpB,eAAO,MAAM,2BAA2B,GAAI,OAAO,MAAM,KAAG,MAS3D,CAAC;AASF,eAAO,MAAM,gBAAgB,GAAI,OAAO,MAAM,KAAG,aA8BhD,CAAC;AAEF,eAAO,MAAM,oBAAoB,GAC/B,GAAG,QAAQ,CAAC,MAAM,GAAG,SAAS,CAAC,EAAE,KAChC,KAAK,GAAG,CAAC,GAAG,SAYd,CAAC;AAEF,eAAO,MAAM,oBAAoB,GAC/B,OAAO,MAAM,EACb,SAAS,MAAM,KACd;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,gBAAgB,EAAE,OAAO,CAAA;CAAE,GAAG,SAiB/D,CAAC;AAEF,eAAO,MAAM,oBAAoB,GAAI,MAAM,MAAM,EAAE,MAAM,MAAM,KAAG,MAOjE,CAAC;AAWF,eAAO,MAAM,qBAAqB,GAChC,SAAS,eAAe,EACxB,eAAuB,EACvB,6BAA6B,KAC5B,MAUF,CAAC;AAEF,eAAO,MAAM,yBAAyB,GACpC,SAAS,MAAM,EACf,eAAY,KACX,MAAM,GAAG,SAaX,CAAC;AAEF,eAAO,MAAM,mBAAmB,GAAI,OAAO,MAAM,KAAG,MAAM,GAAG,SAI5D,CAAC;AAEF,eAAO,MAAM,kBAAkB,GAC7B,OAAO,MAAM,EACb,gBAAgB,MAAM,KACrB,MAcF,CAAC;AAEF,eAAO,MAAM,eAAe,GAC1B,OAAO,MAAM,EACb,YAAY,iBAAiB,KAC5B,MAQF,CAAC;AAEF,eAAO,MAAM,qBAAqB,GAAI,OAAO,MAAM,KAAG,MAUrD,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAC5B,OAAO,MAAM,EACb,gBAAgB,MAAM,KACrB,MA0CF,CAAC"}
|
package/dist/temporal.js
CHANGED
|
@@ -140,6 +140,7 @@ const fromLocalDateTime = (value, previousValue) => {
|
|
|
140
140
|
return formatTemporalInstant({
|
|
141
141
|
...previous,
|
|
142
142
|
epochMs: date.getTime(),
|
|
143
|
+
...previous.precision === "minutes" && (temporal.precision === "seconds" || date.getUTCSeconds() !== 0) ? { precision: "seconds" } : {},
|
|
143
144
|
...date.getMilliseconds() === new Date(previous.epochMs).getUTCMilliseconds() ? {} : typeof temporal.precision === "number" ? {
|
|
144
145
|
precision: Math.max(temporal.precision, typeof previous.precision === "number" ? previous.precision : 0),
|
|
145
146
|
subMillisecondDigits: value.slice(value.indexOf(".") + 4)
|
package/dist/temporal.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"temporal.js","names":[],"sources":["../src/temporal.ts"],"sourcesContent":["export type TemporalPrecision = \"minutes\" | \"seconds\" | number;\n\nexport type TemporalInstant = {\n kind: \"instant\";\n epochMs: number;\n offset: string;\n precision: TemporalPrecision;\n subMillisecondDigits?: string;\n};\n\nexport type TemporalValue =\n | { kind: \"date\" | \"unparseable\"; value: string }\n | {\n kind: \"time\" | \"floating\";\n value: string;\n precision: TemporalPrecision;\n }\n | TemporalInstant;\n\nconst DATE_PATTERN = /^\\d{4}-\\d{2}-\\d{2}$/;\nconst TIME_PATTERN =\n /^([01]\\d|2[0-3]):([0-5]\\d)(?::([0-5]\\d)(?:\\.(\\d{1,9}))?)?$/;\nconst DATETIME_PATTERN =\n /^(\\d{4}-\\d{2}-\\d{2})T([01]\\d|2[0-3]):([0-5]\\d)(?::([0-5]\\d)(?:\\.(\\d+))?)?(Z|[+-](?:[01]\\d|2[0-3]):[0-5]\\d)?$/;\nconst INPUT_PATTERN =\n /^((?:\\d{4}-\\d{2}-\\d{2}T)?(?:[01]\\d|2[0-3]):[0-5]\\d)(?::([0-5]\\d)(?:\\.(\\d{1,9}))?)?$/;\n\nexport const normalizeTemporalInputValue = (value: string): string => {\n const match = INPUT_PATTERN.exec(value);\n if (!match || match[2] === undefined) return value;\n const digits = match[3]?.slice(0, 3) ?? \"\";\n let end = digits.length;\n while (end > 0 && digits[end - 1] === \"0\") end--;\n const fraction = digits.slice(0, end);\n if (match[2] === \"00\" && !fraction) return match[1]!;\n return `${match[1]}:${match[2]}${fraction ? `.${fraction}` : \"\"}`;\n};\n\nconst isCalendarDate = (value: string): boolean => {\n const date = new Date(`${value}T00:00:00Z`);\n return (\n Number.isFinite(date.getTime()) && date.toISOString().slice(0, 10) === value\n );\n};\n\nexport const classifyTemporal = (value: string): TemporalValue => {\n if (DATE_PATTERN.test(value) && isCalendarDate(value)) {\n return { kind: \"date\", value };\n }\n const time = TIME_PATTERN.exec(value);\n if (time) {\n return {\n kind: \"time\",\n value,\n precision:\n time[4]?.length ?? (time[3] === undefined ? \"minutes\" : \"seconds\"),\n };\n }\n const datetime = DATETIME_PATTERN.exec(value);\n if (!datetime || !isCalendarDate(datetime[1]!)) {\n return { kind: \"unparseable\", value };\n }\n const precision =\n datetime[5]?.length ?? (datetime[4] === undefined ? \"minutes\" : \"seconds\");\n const offset = datetime[6];\n if (offset === undefined) return { kind: \"floating\", value, precision };\n return {\n kind: \"instant\",\n epochMs: Date.parse(value),\n offset,\n precision,\n ...(datetime[5] && datetime[5].length > 3\n ? { subMillisecondDigits: datetime[5].slice(3) }\n : {}),\n };\n};\n\nexport const getTemporalInputStep = (\n ...values: (string | undefined)[]\n): \"any\" | 1 | undefined => {\n let step: 1 | undefined;\n for (const value of values) {\n const temporal = classifyTemporal(value ?? \"\");\n const precision =\n \"precision\" in temporal\n ? temporal.precision\n : INPUT_PATTERN.exec(value ?? \"\")?.[3]?.length;\n if (typeof precision === \"number\") return \"any\";\n if (precision === \"seconds\") step = 1;\n }\n return step;\n};\n\nexport const splitTemporalMinutes = (\n value: string,\n offset?: string,\n): { date?: string; time: string; droppedPrecision: boolean } | undefined => {\n const temporal = classifyTemporal(value);\n if (temporal.kind === \"date\" || temporal.kind === \"unparseable\")\n return undefined;\n const wallTime =\n temporal.kind === \"instant\"\n ? formatTemporalInstant(temporal, offset).slice(\n 0,\n -(offset ?? temporal.offset).length,\n )\n : temporal.value;\n const end = temporal.kind === \"time\" ? 5 : 16;\n return {\n ...(temporal.kind === \"time\" ? {} : { date: wallTime.slice(0, 10) }),\n time: wallTime.slice(end - 5, end),\n droppedPrecision: /[1-9]/.test(wallTime.slice(end)),\n };\n};\n\nexport const mergeTemporalMinutes = (date: string, time: string): string => {\n if (\n classifyTemporal(date).kind !== \"date\" ||\n classifyTemporal(time).kind !== \"time\"\n )\n return \"\";\n return `${date}T${time.slice(0, 5)}`;\n};\n\nconst pad = (value: number, digits = 2): string =>\n String(value).padStart(digits, \"0\");\n\nconst offsetMinutes = (offset: string): number =>\n offset === \"Z\"\n ? 0\n : (offset.startsWith(\"-\") ? -1 : 1) *\n (Number(offset.slice(1, 3)) * 60 + Number(offset.slice(4, 6)));\n\nexport const formatTemporalInstant = (\n instant: TemporalInstant,\n offset = instant.offset,\n precision = instant.precision,\n): string => {\n const date = new Date(instant.epochMs + offsetMinutes(offset) * 60_000);\n const iso = date.toISOString();\n const wallTime =\n precision === \"minutes\"\n ? iso.slice(0, 16)\n : precision === \"seconds\"\n ? iso.slice(0, 19)\n : `${iso.slice(0, 19)}.${(pad(date.getUTCMilliseconds(), 3) + (instant.subMillisecondDigits ?? \"\")).padEnd(precision, \"0\").slice(0, precision)}`;\n return `${wallTime}${offset}`;\n};\n\nexport const formatTemporalUnixSeconds = (\n seconds: number,\n offset = \"Z\",\n): string | undefined => {\n if (!Number.isInteger(seconds)) return undefined;\n try {\n const value = formatTemporalInstant({\n kind: \"instant\",\n epochMs: seconds * 1000,\n offset,\n precision: \"seconds\",\n });\n return classifyTemporal(value).kind === \"instant\" ? value : undefined;\n } catch {\n return undefined;\n }\n};\n\nexport const temporalOffsetLabel = (value: string): string | undefined => {\n const temporal = classifyTemporal(value);\n if (temporal.kind !== \"instant\") return undefined;\n return temporal.offset === \"Z\" ? \"UTC\" : `UTC${temporal.offset}`;\n};\n\nexport const fromOffsetDateTime = (\n value: string,\n previousValue?: string,\n): string => {\n if (value === \"\") return \"\";\n if (classifyTemporal(value).kind !== \"floating\") return value;\n const previous = classifyTemporal(previousValue ?? \"\");\n if (previous.kind !== \"instant\") return value;\n const epochMs = Date.parse(`${value}${previous.offset}`);\n const minuteRemainder =\n previous.precision === \"minutes\"\n ? 0\n : ((previous.epochMs % 60_000) + 60_000) % 60_000;\n return formatTemporalInstant({\n ...previous,\n epochMs: epochMs + minuteRemainder,\n });\n};\n\nexport const toLocalDateTime = (\n value: string,\n precision?: TemporalPrecision,\n): string => {\n const temporal = classifyTemporal(value);\n if (temporal.kind !== \"instant\") return value;\n const date = new Date(temporal.epochMs);\n const wallTime = `${pad(date.getFullYear(), 4)}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}T${pad(date.getHours())}:${pad(date.getMinutes())}`;\n return (precision ?? temporal.precision) === \"minutes\"\n ? wallTime\n : `${wallTime}:${pad(date.getSeconds())}${typeof precision === \"number\" ? formatTemporalInstant(temporal, \"Z\", precision).slice(19, -1) : \"\"}`;\n};\n\nexport const toPickerLocalDateTime = (value: string): string => {\n const temporal = classifyTemporal(value);\n if (temporal.kind !== \"instant\") return normalizeTemporalInputValue(value);\n const local = toLocalDateTime(\n value,\n typeof temporal.precision === \"number\" ? 3 : temporal.precision,\n );\n return typeof temporal.precision !== \"number\" || local.endsWith(\".000\")\n ? normalizeTemporalInputValue(local)\n : local;\n};\n\nexport const fromLocalDateTime = (\n value: string,\n previousValue?: string,\n): string => {\n const temporal = classifyTemporal(value);\n const previous = classifyTemporal(previousValue ?? \"\");\n if (temporal.kind !== \"floating\" || previous.kind === \"floating\") {\n return value;\n }\n const date = new Date(\n value.replace(\n /\\.(\\d{1,2})$/,\n (_, digits: string) => `.${digits.padEnd(3, \"0\")}`,\n ),\n );\n const minutes = -date.getTimezoneOffset();\n const offset = `${minutes < 0 ? \"-\" : \"+\"}${pad(Math.floor(Math.abs(minutes) / 60))}:${pad(Math.abs(minutes) % 60)}`;\n if (previous.kind !== \"instant\") {\n return formatTemporalInstant({\n kind: \"instant\",\n epochMs: date.getTime(),\n offset,\n precision: \"seconds\",\n });\n }\n return formatTemporalInstant({\n ...previous,\n epochMs: date.getTime(),\n ...(date.getMilliseconds() ===\n new Date(previous.epochMs).getUTCMilliseconds()\n ? {}\n : typeof temporal.precision === \"number\"\n ? {\n precision: Math.max(\n temporal.precision,\n typeof previous.precision === \"number\" ? previous.precision : 0,\n ),\n subMillisecondDigits: value.slice(value.indexOf(\".\") + 4),\n }\n : { subMillisecondDigits: \"\" }),\n });\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"temporal.js","names":[],"sources":["../src/temporal.ts"],"sourcesContent":["export type TemporalPrecision = \"minutes\" | \"seconds\" | number;\n\nexport type TemporalInstant = {\n kind: \"instant\";\n epochMs: number;\n offset: string;\n precision: TemporalPrecision;\n subMillisecondDigits?: string;\n};\n\nexport type TemporalValue =\n | { kind: \"date\" | \"unparseable\"; value: string }\n | {\n kind: \"time\" | \"floating\";\n value: string;\n precision: TemporalPrecision;\n }\n | TemporalInstant;\n\nconst DATE_PATTERN = /^\\d{4}-\\d{2}-\\d{2}$/;\nconst TIME_PATTERN =\n /^([01]\\d|2[0-3]):([0-5]\\d)(?::([0-5]\\d)(?:\\.(\\d{1,9}))?)?$/;\nconst DATETIME_PATTERN =\n /^(\\d{4}-\\d{2}-\\d{2})T([01]\\d|2[0-3]):([0-5]\\d)(?::([0-5]\\d)(?:\\.(\\d+))?)?(Z|[+-](?:[01]\\d|2[0-3]):[0-5]\\d)?$/;\nconst INPUT_PATTERN =\n /^((?:\\d{4}-\\d{2}-\\d{2}T)?(?:[01]\\d|2[0-3]):[0-5]\\d)(?::([0-5]\\d)(?:\\.(\\d{1,9}))?)?$/;\n\nexport const normalizeTemporalInputValue = (value: string): string => {\n const match = INPUT_PATTERN.exec(value);\n if (!match || match[2] === undefined) return value;\n const digits = match[3]?.slice(0, 3) ?? \"\";\n let end = digits.length;\n while (end > 0 && digits[end - 1] === \"0\") end--;\n const fraction = digits.slice(0, end);\n if (match[2] === \"00\" && !fraction) return match[1]!;\n return `${match[1]}:${match[2]}${fraction ? `.${fraction}` : \"\"}`;\n};\n\nconst isCalendarDate = (value: string): boolean => {\n const date = new Date(`${value}T00:00:00Z`);\n return (\n Number.isFinite(date.getTime()) && date.toISOString().slice(0, 10) === value\n );\n};\n\nexport const classifyTemporal = (value: string): TemporalValue => {\n if (DATE_PATTERN.test(value) && isCalendarDate(value)) {\n return { kind: \"date\", value };\n }\n const time = TIME_PATTERN.exec(value);\n if (time) {\n return {\n kind: \"time\",\n value,\n precision:\n time[4]?.length ?? (time[3] === undefined ? \"minutes\" : \"seconds\"),\n };\n }\n const datetime = DATETIME_PATTERN.exec(value);\n if (!datetime || !isCalendarDate(datetime[1]!)) {\n return { kind: \"unparseable\", value };\n }\n const precision =\n datetime[5]?.length ?? (datetime[4] === undefined ? \"minutes\" : \"seconds\");\n const offset = datetime[6];\n if (offset === undefined) return { kind: \"floating\", value, precision };\n return {\n kind: \"instant\",\n epochMs: Date.parse(value),\n offset,\n precision,\n ...(datetime[5] && datetime[5].length > 3\n ? { subMillisecondDigits: datetime[5].slice(3) }\n : {}),\n };\n};\n\nexport const getTemporalInputStep = (\n ...values: (string | undefined)[]\n): \"any\" | 1 | undefined => {\n let step: 1 | undefined;\n for (const value of values) {\n const temporal = classifyTemporal(value ?? \"\");\n const precision =\n \"precision\" in temporal\n ? temporal.precision\n : INPUT_PATTERN.exec(value ?? \"\")?.[3]?.length;\n if (typeof precision === \"number\") return \"any\";\n if (precision === \"seconds\") step = 1;\n }\n return step;\n};\n\nexport const splitTemporalMinutes = (\n value: string,\n offset?: string,\n): { date?: string; time: string; droppedPrecision: boolean } | undefined => {\n const temporal = classifyTemporal(value);\n if (temporal.kind === \"date\" || temporal.kind === \"unparseable\")\n return undefined;\n const wallTime =\n temporal.kind === \"instant\"\n ? formatTemporalInstant(temporal, offset).slice(\n 0,\n -(offset ?? temporal.offset).length,\n )\n : temporal.value;\n const end = temporal.kind === \"time\" ? 5 : 16;\n return {\n ...(temporal.kind === \"time\" ? {} : { date: wallTime.slice(0, 10) }),\n time: wallTime.slice(end - 5, end),\n droppedPrecision: /[1-9]/.test(wallTime.slice(end)),\n };\n};\n\nexport const mergeTemporalMinutes = (date: string, time: string): string => {\n if (\n classifyTemporal(date).kind !== \"date\" ||\n classifyTemporal(time).kind !== \"time\"\n )\n return \"\";\n return `${date}T${time.slice(0, 5)}`;\n};\n\nconst pad = (value: number, digits = 2): string =>\n String(value).padStart(digits, \"0\");\n\nconst offsetMinutes = (offset: string): number =>\n offset === \"Z\"\n ? 0\n : (offset.startsWith(\"-\") ? -1 : 1) *\n (Number(offset.slice(1, 3)) * 60 + Number(offset.slice(4, 6)));\n\nexport const formatTemporalInstant = (\n instant: TemporalInstant,\n offset = instant.offset,\n precision = instant.precision,\n): string => {\n const date = new Date(instant.epochMs + offsetMinutes(offset) * 60_000);\n const iso = date.toISOString();\n const wallTime =\n precision === \"minutes\"\n ? iso.slice(0, 16)\n : precision === \"seconds\"\n ? iso.slice(0, 19)\n : `${iso.slice(0, 19)}.${(pad(date.getUTCMilliseconds(), 3) + (instant.subMillisecondDigits ?? \"\")).padEnd(precision, \"0\").slice(0, precision)}`;\n return `${wallTime}${offset}`;\n};\n\nexport const formatTemporalUnixSeconds = (\n seconds: number,\n offset = \"Z\",\n): string | undefined => {\n if (!Number.isInteger(seconds)) return undefined;\n try {\n const value = formatTemporalInstant({\n kind: \"instant\",\n epochMs: seconds * 1000,\n offset,\n precision: \"seconds\",\n });\n return classifyTemporal(value).kind === \"instant\" ? value : undefined;\n } catch {\n return undefined;\n }\n};\n\nexport const temporalOffsetLabel = (value: string): string | undefined => {\n const temporal = classifyTemporal(value);\n if (temporal.kind !== \"instant\") return undefined;\n return temporal.offset === \"Z\" ? \"UTC\" : `UTC${temporal.offset}`;\n};\n\nexport const fromOffsetDateTime = (\n value: string,\n previousValue?: string,\n): string => {\n if (value === \"\") return \"\";\n if (classifyTemporal(value).kind !== \"floating\") return value;\n const previous = classifyTemporal(previousValue ?? \"\");\n if (previous.kind !== \"instant\") return value;\n const epochMs = Date.parse(`${value}${previous.offset}`);\n const minuteRemainder =\n previous.precision === \"minutes\"\n ? 0\n : ((previous.epochMs % 60_000) + 60_000) % 60_000;\n return formatTemporalInstant({\n ...previous,\n epochMs: epochMs + minuteRemainder,\n });\n};\n\nexport const toLocalDateTime = (\n value: string,\n precision?: TemporalPrecision,\n): string => {\n const temporal = classifyTemporal(value);\n if (temporal.kind !== \"instant\") return value;\n const date = new Date(temporal.epochMs);\n const wallTime = `${pad(date.getFullYear(), 4)}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}T${pad(date.getHours())}:${pad(date.getMinutes())}`;\n return (precision ?? temporal.precision) === \"minutes\"\n ? wallTime\n : `${wallTime}:${pad(date.getSeconds())}${typeof precision === \"number\" ? formatTemporalInstant(temporal, \"Z\", precision).slice(19, -1) : \"\"}`;\n};\n\nexport const toPickerLocalDateTime = (value: string): string => {\n const temporal = classifyTemporal(value);\n if (temporal.kind !== \"instant\") return normalizeTemporalInputValue(value);\n const local = toLocalDateTime(\n value,\n typeof temporal.precision === \"number\" ? 3 : temporal.precision,\n );\n return typeof temporal.precision !== \"number\" || local.endsWith(\".000\")\n ? normalizeTemporalInputValue(local)\n : local;\n};\n\nexport const fromLocalDateTime = (\n value: string,\n previousValue?: string,\n): string => {\n const temporal = classifyTemporal(value);\n const previous = classifyTemporal(previousValue ?? \"\");\n if (temporal.kind !== \"floating\" || previous.kind === \"floating\") {\n return value;\n }\n const date = new Date(\n value.replace(\n /\\.(\\d{1,2})$/,\n (_, digits: string) => `.${digits.padEnd(3, \"0\")}`,\n ),\n );\n const minutes = -date.getTimezoneOffset();\n const offset = `${minutes < 0 ? \"-\" : \"+\"}${pad(Math.floor(Math.abs(minutes) / 60))}:${pad(Math.abs(minutes) % 60)}`;\n if (previous.kind !== \"instant\") {\n return formatTemporalInstant({\n kind: \"instant\",\n epochMs: date.getTime(),\n offset,\n precision: \"seconds\",\n });\n }\n return formatTemporalInstant({\n ...previous,\n epochMs: date.getTime(),\n ...(previous.precision === \"minutes\" &&\n (temporal.precision === \"seconds\" || date.getUTCSeconds() !== 0)\n ? { precision: \"seconds\" }\n : {}),\n ...(date.getMilliseconds() ===\n new Date(previous.epochMs).getUTCMilliseconds()\n ? {}\n : typeof temporal.precision === \"number\"\n ? {\n precision: Math.max(\n temporal.precision,\n typeof previous.precision === \"number\" ? previous.precision : 0,\n ),\n subMillisecondDigits: value.slice(value.indexOf(\".\") + 4),\n }\n : { subMillisecondDigits: \"\" }),\n });\n};\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"interactive.d.ts","sourceRoot":"","sources":["../../src/vocabulary/interactive.tsx"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"interactive.d.ts","sourceRoot":"","sources":["../../src/vocabulary/interactive.tsx"],"names":[],"mappings":"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"}
|
|
@@ -178,13 +178,20 @@ function DatePickerRender({ inputType = "date", value, min, max, label, name, $a
|
|
|
178
178
|
const [selection, setSelection] = React.useState({
|
|
179
179
|
initialValue,
|
|
180
180
|
value: initialValue,
|
|
181
|
-
anchor: initialValue
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
initialValue,
|
|
185
|
-
value: initialValue,
|
|
186
|
-
anchor: isBound && initialValue === selection.value ? selection.anchor : initialValue
|
|
181
|
+
anchor: initialValue,
|
|
182
|
+
committedValue: initialValue,
|
|
183
|
+
edited: false
|
|
187
184
|
});
|
|
185
|
+
if (selection.initialValue !== initialValue) {
|
|
186
|
+
const isEcho = isBound && initialValue === selection.value;
|
|
187
|
+
setSelection({
|
|
188
|
+
initialValue,
|
|
189
|
+
value: initialValue,
|
|
190
|
+
anchor: isEcho ? selection.anchor : initialValue,
|
|
191
|
+
committedValue: isEcho ? selection.committedValue : initialValue,
|
|
192
|
+
edited: isEcho && selection.edited
|
|
193
|
+
});
|
|
194
|
+
}
|
|
188
195
|
const currentValue = isBound ? initialValue : selection.value;
|
|
189
196
|
const temporal = classifyTemporal(currentValue ?? "");
|
|
190
197
|
const anchor = classifyTemporal(selection.anchor ?? "");
|
|
@@ -203,6 +210,21 @@ function DatePickerRender({ inputType = "date", value, min, max, label, name, $a
|
|
|
203
210
|
const hydrated = React.useSyncExternalStore(subscribeToHydration, clientHydrationSnapshot, serverHydrationSnapshot);
|
|
204
211
|
const showRawValue = inputType === "datetime" && temporal.kind === "instant" && !hydrated;
|
|
205
212
|
const displayValue = currentValue === void 0 ? void 0 : inputType === "datetime" && hydrated ? toPickerLocalDateTime(currentValue) : normalizeTemporalInputValue(currentValue);
|
|
213
|
+
const keyboardActive = React.useRef(false);
|
|
214
|
+
const valueFromInput = (input) => {
|
|
215
|
+
const inputValue = normalizeTemporalInputValue(input.value);
|
|
216
|
+
return inputType === "datetime" ? fromLocalDateTime(inputValue, selection.anchor) : inputValue;
|
|
217
|
+
};
|
|
218
|
+
const commit = (input) => {
|
|
219
|
+
if (!selection.edited) return;
|
|
220
|
+
const nextValue = valueFromInput(input);
|
|
221
|
+
setSelection({
|
|
222
|
+
...selection,
|
|
223
|
+
committedValue: nextValue,
|
|
224
|
+
edited: false
|
|
225
|
+
});
|
|
226
|
+
if (nextValue !== selection.committedValue) fire($action, $dispatch, nextValue, input);
|
|
227
|
+
};
|
|
206
228
|
return /* @__PURE__ */ jsx("input", {
|
|
207
229
|
type: showRawValue ? "text" : inputType === "datetime" ? "datetime-local" : inputType,
|
|
208
230
|
readOnly: showRawValue || void 0,
|
|
@@ -214,20 +236,52 @@ function DatePickerRender({ inputType = "date", value, min, max, label, name, $a
|
|
|
214
236
|
...isBound || inputType === "datetime" ? { value: displayValue ?? "" } : { defaultValue: displayValue },
|
|
215
237
|
min: inputType === "datetime" && hydrated && min !== void 0 ? normalizeTemporalInputValue(toLocalDateTime(min, minimum.kind === "instant" ? minimum.precision : void 0)) : min === void 0 ? void 0 : normalizeTemporalInputValue(min),
|
|
216
238
|
max: inputType === "datetime" && hydrated && max !== void 0 ? normalizeTemporalInputValue(toLocalDateTime(max, maximum.kind === "instant" ? maximum.precision : void 0)) : max === void 0 ? void 0 : normalizeTemporalInputValue(max),
|
|
217
|
-
step: inputType !== "date" ? getTemporalInputStep(selection.anchor, currentValue, min, max) : void 0,
|
|
239
|
+
step: inputType !== "date" ? getTemporalInputStep(selection.anchor, hasInstantAnchor ? displayValue : currentValue, min, max) : void 0,
|
|
218
240
|
onChange: (e) => {
|
|
219
|
-
const
|
|
220
|
-
const nextValue = inputType === "datetime" ? fromLocalDateTime(inputValue, selection.anchor) : inputValue;
|
|
241
|
+
const nextValue = valueFromInput(e.currentTarget);
|
|
221
242
|
const canonical = canonicalFieldValue(nextValue);
|
|
222
243
|
if (canonical !== void 0) e.currentTarget.setAttribute(FIELD_VALUE_ATTR, canonical);
|
|
223
244
|
else e.currentTarget.removeAttribute(FIELD_VALUE_ATTR);
|
|
245
|
+
const typed = keyboardActive.current;
|
|
246
|
+
keyboardActive.current = false;
|
|
224
247
|
setSelection({
|
|
225
248
|
initialValue,
|
|
226
249
|
value: nextValue,
|
|
227
|
-
anchor: selection.anchor
|
|
250
|
+
anchor: selection.anchor,
|
|
251
|
+
committedValue: typed ? selection.committedValue : nextValue,
|
|
252
|
+
edited: typed
|
|
228
253
|
});
|
|
229
254
|
updateBinding?.(nextValue);
|
|
230
|
-
fire($action, $dispatch, nextValue, e.currentTarget);
|
|
255
|
+
if (!typed && nextValue !== selection.committedValue) fire($action, $dispatch, nextValue, e.currentTarget);
|
|
256
|
+
},
|
|
257
|
+
onBlur: (e) => {
|
|
258
|
+
keyboardActive.current = false;
|
|
259
|
+
commit(e.currentTarget);
|
|
260
|
+
},
|
|
261
|
+
onKeyDown: (e) => {
|
|
262
|
+
if (e.key === "Enter" && !e.nativeEvent.isComposing && !e.currentTarget.form) {
|
|
263
|
+
commit(e.currentTarget);
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
keyboardActive.current = !e.altKey && !e.ctrlKey && !e.metaKey && (e.key.length === 1 || [
|
|
267
|
+
"ArrowDown",
|
|
268
|
+
"ArrowUp",
|
|
269
|
+
"Backspace",
|
|
270
|
+
"Delete",
|
|
271
|
+
"End",
|
|
272
|
+
"Home",
|
|
273
|
+
"PageDown",
|
|
274
|
+
"PageUp"
|
|
275
|
+
].includes(e.key));
|
|
276
|
+
},
|
|
277
|
+
onKeyUp: () => {
|
|
278
|
+
keyboardActive.current = false;
|
|
279
|
+
},
|
|
280
|
+
onPaste: () => {
|
|
281
|
+
keyboardActive.current = true;
|
|
282
|
+
},
|
|
283
|
+
onCut: () => {
|
|
284
|
+
keyboardActive.current = true;
|
|
231
285
|
}
|
|
232
286
|
}, isBound ? void 0 : initialValue);
|
|
233
287
|
}
|
|
@@ -465,7 +519,7 @@ const interactiveVocabulary = {
|
|
|
465
519
|
render: InputRender
|
|
466
520
|
},
|
|
467
521
|
DatePicker: {
|
|
468
|
-
description: "A date, datetime, or time input. Carries `$action
|
|
522
|
+
description: "A date, datetime, or time input. Carries `$action`, which runs once per committed value: a pick from the picker, or a typed value on blur or Enter.",
|
|
469
523
|
properties: z.object({
|
|
470
524
|
inputType: z.enum([
|
|
471
525
|
"date",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"interactive.js","names":[],"sources":["../../src/vocabulary/interactive.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { z } from \"zod\";\nimport {\n CHECKBOX_GROUP_ATTR,\n FIELD_NAME_ATTR,\n FIELD_VALUE_ATTR,\n GENERATED_NAME_ATTR,\n} from \"../constants\";\nimport type { Action } from \"../ir\";\nimport { BUTTON_STYLES } from \"../ir\";\nimport type {\n GenerativeUIDispatch,\n GenerativeUILibrary,\n GenerativeUIStatus,\n} from \"../types\";\nimport { A2uiBindingContext, useA2uiBinding } from \"../a2ui/BindingContext\";\nimport { useAnsweredValue } from \"../answeredValues\";\nimport { actionAttr, fire } from \"./dispatch\";\nimport { toTextContent } from \"./toTextContent\";\nimport { useRadioGroupName } from \"../RadioGroupScope\";\nimport {\n classifyTemporal,\n fromLocalDateTime,\n getTemporalInputStep,\n normalizeTemporalInputValue,\n toLocalDateTime,\n toPickerLocalDateTime,\n} from \"../temporal\";\n\nconst optionSchema = z.object({\n label: z.string(),\n value: z.string(),\n});\n\nconst describedOptionSchema = optionSchema.extend({\n description: z.string().optional(),\n});\n\ntype Option = { label: string; description?: string; value: string };\n\nconst isOption = (option: unknown): option is Option =>\n option !== null &&\n typeof option === \"object\" &&\n \"label\" in option &&\n typeof option.label === \"string\" &&\n (!(\"description\" in option) || typeof option.description === \"string\") &&\n \"value\" in option &&\n typeof option.value === \"string\";\n\nconst mapOptions = (\n options: unknown,\n render: (option: Option, key: string) => React.ReactNode,\n) => {\n const occurrences = new Map<string, number>();\n return (Array.isArray(options) ? options : []).map((option) => {\n if (!isOption(option)) return null;\n const occurrence = occurrences.get(option.value) ?? 0;\n occurrences.set(option.value, occurrence + 1);\n return render(option, JSON.stringify([option.value, occurrence]));\n });\n};\n\ntype RadioGroupRenderProps = {\n value?: string;\n options: Option[];\n name?: string;\n label?: string;\n defaultValue?: string;\n children?: React.ReactNode;\n $status: GenerativeUIStatus;\n $action?: Action;\n $dispatch?: GenerativeUIDispatch;\n};\n\nfunction RadioGroupRender({\n value,\n options,\n name,\n label,\n defaultValue,\n children,\n $action,\n $dispatch,\n}: RadioGroupRenderProps) {\n const groupName = useRadioGroupName(name);\n const answeredValue = useAnsweredValue(name);\n const updateBinding = useA2uiBinding(name);\n const isBound = updateBinding !== undefined;\n const initialValue =\n typeof answeredValue === \"string\" ? answeredValue : defaultValue;\n return (\n <fieldset\n key={initialValue}\n data-aui=\"radiogroup\"\n data-aui-action={actionAttr($action)}\n aria-label={label}\n >\n {mapOptions(options, (option, key) => (\n <label key={key} data-aui=\"radiogroup-option\">\n <input\n type=\"radio\"\n name={groupName}\n {...{ [FIELD_NAME_ATTR]: name }}\n {...(name == null ? { [GENERATED_NAME_ATTR]: \"\" } : {})}\n value={option.value}\n {...(isBound\n ? { checked: (answeredValue ?? value) === option.value }\n : { defaultChecked: initialValue === option.value })}\n onChange={(e) => {\n updateBinding?.(option.value);\n fire($action, $dispatch, option.value, e.currentTarget);\n }}\n />\n {option.description ? (\n <span data-aui=\"option-content\">\n <span data-aui=\"option-label\">{option.label}</span>\n <span data-aui=\"option-description\">{option.description}</span>\n </span>\n ) : (\n option.label\n )}\n </label>\n ))}\n {children}\n </fieldset>\n );\n}\n\ntype CheckboxGroupRenderProps = {\n value?: string[];\n options: Option[];\n name?: string;\n label?: string;\n defaultValue?: string[];\n children?: React.ReactNode;\n $status: GenerativeUIStatus;\n $action?: Action;\n $dispatch?: GenerativeUIDispatch;\n};\n\nconst checkedGroupValues = (input: HTMLInputElement): string[] =>\n Array.from(\n input\n .closest(\"fieldset\")\n ?.querySelectorAll<HTMLInputElement>(\n `:scope > label > input[${CHECKBOX_GROUP_ATTR}]:checked`,\n ) ?? [],\n (checkbox) => checkbox.value,\n );\n\nfunction CheckboxGroupRender({\n value,\n options,\n name,\n label,\n defaultValue,\n children,\n $action,\n $dispatch,\n}: CheckboxGroupRenderProps) {\n const generatedName = React.useId();\n const fieldName = name ?? generatedName;\n const answeredValue = useAnsweredValue(name);\n const updateBinding = useA2uiBinding(name);\n const isBound = updateBinding !== undefined;\n const checkedValues =\n Array.isArray(answeredValue) &&\n answeredValue.every((value): value is string => typeof value === \"string\")\n ? answeredValue\n : Array.isArray(defaultValue)\n ? defaultValue\n : [];\n return (\n <fieldset\n key={JSON.stringify(checkedValues)}\n data-aui=\"checkboxgroup\"\n data-aui-action={actionAttr($action)}\n aria-label={label}\n >\n {mapOptions(options, (option, key) => (\n <label key={key} data-aui=\"checkboxgroup-option\">\n <input\n type=\"checkbox\"\n name={fieldName}\n {...{ [CHECKBOX_GROUP_ATTR]: \"\" }}\n {...(name == null ? { [GENERATED_NAME_ATTR]: \"\" } : {})}\n value={option.value}\n {...(isBound\n ? {\n checked: (Array.isArray(answeredValue)\n ? checkedValues\n : (value ?? [])\n ).includes(option.value),\n }\n : { defaultChecked: checkedValues.includes(option.value) })}\n onChange={(e) => {\n const selected = checkedGroupValues(e.currentTarget);\n updateBinding?.(selected);\n fire($action, $dispatch, selected, e.currentTarget);\n }}\n />\n {option.description ? (\n <span data-aui=\"option-content\">\n <span data-aui=\"option-label\">{option.label}</span>\n <span data-aui=\"option-description\">{option.description}</span>\n </span>\n ) : (\n option.label\n )}\n </label>\n ))}\n {children}\n </fieldset>\n );\n}\n\ntype SelectRenderProps = {\n value?: string;\n options: Option[];\n placeholder?: string;\n label?: string;\n name?: string;\n defaultValue?: string;\n children?: React.ReactNode;\n $status: GenerativeUIStatus;\n $action?: Action;\n $dispatch?: GenerativeUIDispatch;\n};\n\nfunction SelectRender({\n value,\n options,\n placeholder,\n label,\n name,\n defaultValue,\n $action,\n $dispatch,\n children,\n}: SelectRenderProps) {\n const answeredValue = useAnsweredValue(name);\n const updateBinding = useA2uiBinding(name);\n const isBound = updateBinding !== undefined;\n const placeholderText = toTextContent(placeholder);\n const initialValue =\n typeof answeredValue === \"string\"\n ? answeredValue\n : typeof defaultValue === \"string\"\n ? defaultValue\n : \"\";\n return (\n <select\n key={initialValue}\n data-aui=\"select\"\n data-aui-action={actionAttr($action)}\n name={name}\n aria-label={label}\n {...(isBound\n ? {\n value:\n typeof answeredValue === \"string\" ? answeredValue : (value ?? \"\"),\n }\n : { defaultValue: initialValue })}\n onChange={(e) => {\n updateBinding?.(e.currentTarget.value);\n fire($action, $dispatch, e.currentTarget.value, e.currentTarget);\n }}\n >\n {placeholderText ? (\n <option value=\"\" disabled>\n {placeholderText}\n </option>\n ) : null}\n {mapOptions(options, (option, key) => (\n <option key={key} value={option.value}>\n {option.label}\n </option>\n ))}\n {children}\n </select>\n );\n}\n\ntype InputRenderProps = {\n value?: string;\n inputType?: \"text\" | \"password\" | \"number\";\n placeholder?: string;\n multiline?: boolean;\n label?: string;\n name?: string;\n defaultValue?: string;\n $status: GenerativeUIStatus;\n $action?: Action;\n $dispatch?: GenerativeUIDispatch;\n};\n\nfunction InputRender({\n value,\n inputType,\n placeholder,\n multiline,\n label,\n name,\n defaultValue,\n $action,\n $dispatch,\n}: InputRenderProps) {\n const answeredValue = useAnsweredValue(name);\n const updateBinding = useA2uiBinding(name);\n const isBound = updateBinding !== undefined;\n const initialValue =\n typeof answeredValue === \"string\" ? answeredValue : defaultValue;\n const submit = (control: HTMLInputElement | HTMLTextAreaElement) =>\n fire($action, $dispatch, control.value, control);\n return multiline && (inputType === undefined || inputType === \"text\") ? (\n <textarea\n key={initialValue}\n data-aui=\"input\"\n data-aui-multiline\n data-aui-action={actionAttr($action)}\n name={name}\n aria-label={label}\n placeholder={placeholder}\n {...(isBound\n ? {\n value:\n typeof answeredValue === \"string\" ? answeredValue : (value ?? \"\"),\n }\n : { defaultValue: initialValue })}\n onChange={\n updateBinding ? (e) => updateBinding(e.currentTarget.value) : undefined\n }\n onKeyDown={(e) => {\n if (\n e.key !== \"Enter\" ||\n !(e.ctrlKey || e.metaKey) ||\n e.nativeEvent.isComposing\n )\n return;\n if (e.currentTarget.form) {\n e.preventDefault();\n HTMLFormElement.prototype.requestSubmit.call(e.currentTarget.form);\n } else {\n submit(e.currentTarget);\n }\n }}\n />\n ) : (\n <input\n key={initialValue}\n type={inputType}\n step={inputType === \"number\" ? \"any\" : undefined}\n data-aui=\"input\"\n data-aui-action={actionAttr($action)}\n name={name}\n aria-label={label}\n placeholder={placeholder}\n {...(isBound\n ? {\n value:\n typeof answeredValue === \"string\" ? answeredValue : (value ?? \"\"),\n }\n : { defaultValue: initialValue })}\n onChange={\n updateBinding ? (e) => updateBinding(e.currentTarget.value) : undefined\n }\n onKeyDown={(e) => {\n if (\n e.key === \"Enter\" &&\n !e.nativeEvent.isComposing &&\n !e.currentTarget.form\n )\n submit(e.currentTarget);\n }}\n />\n );\n}\n\ntype DatePickerRenderProps = {\n inputType?: \"date\" | \"datetime\" | \"time\";\n value?: string;\n min?: string;\n max?: string;\n label?: string;\n name?: string;\n $status: GenerativeUIStatus;\n $action?: Action;\n $dispatch?: GenerativeUIDispatch;\n};\n\nconst subscribeToHydration = () => () => {};\nconst clientHydrationSnapshot = () => true;\nconst serverHydrationSnapshot = () => false;\n\nfunction DatePickerRender({\n inputType = \"date\",\n value,\n min,\n max,\n label,\n name,\n $action,\n $dispatch,\n}: DatePickerRenderProps) {\n const answeredValue = useAnsweredValue(name);\n const updateBinding = useA2uiBinding(name);\n const isBound = updateBinding !== undefined;\n const initialValue =\n typeof answeredValue === \"string\" ? answeredValue : value;\n const [selection, setSelection] = React.useState({\n initialValue,\n value: initialValue,\n anchor: initialValue,\n });\n if (selection.initialValue !== initialValue) {\n setSelection({\n initialValue,\n value: initialValue,\n anchor:\n isBound && initialValue === selection.value\n ? selection.anchor\n : initialValue,\n });\n }\n const currentValue = isBound ? initialValue : selection.value;\n const temporal = classifyTemporal(currentValue ?? \"\");\n const anchor = classifyTemporal(selection.anchor ?? \"\");\n const hasInstantAnchor =\n inputType === \"datetime\" && anchor.kind === \"instant\";\n const canonicalFieldValue = (value: string | undefined) => {\n const current = value ?? \"\";\n const kind = classifyTemporal(current).kind;\n if (inputType === \"time\")\n return kind === \"time\" && normalizeTemporalInputValue(current) !== current\n ? value\n : undefined;\n if (inputType !== \"datetime\") return undefined;\n if (kind === \"instant\") return value;\n if (hasInstantAnchor) return selection.anchor;\n return kind === \"floating\" &&\n normalizeTemporalInputValue(current) !== current\n ? value\n : undefined;\n };\n const minimum = classifyTemporal(min ?? \"\");\n const maximum = classifyTemporal(max ?? \"\");\n const hydrated = React.useSyncExternalStore(\n subscribeToHydration,\n clientHydrationSnapshot,\n serverHydrationSnapshot,\n );\n const showRawValue =\n inputType === \"datetime\" && temporal.kind === \"instant\" && !hydrated;\n const displayValue =\n currentValue === undefined\n ? undefined\n : inputType === \"datetime\" && hydrated\n ? toPickerLocalDateTime(currentValue)\n : normalizeTemporalInputValue(currentValue);\n return (\n <input\n key={isBound ? undefined : initialValue}\n type={\n showRawValue\n ? \"text\"\n : inputType === \"datetime\"\n ? \"datetime-local\"\n : inputType\n }\n readOnly={showRawValue || undefined}\n data-aui=\"datepicker\"\n data-aui-action={actionAttr($action)}\n {...{\n [FIELD_VALUE_ATTR]: canonicalFieldValue(currentValue),\n }}\n name={name}\n aria-label={label}\n {...(isBound || inputType === \"datetime\"\n ? { value: displayValue ?? \"\" }\n : { defaultValue: displayValue })}\n min={\n inputType === \"datetime\" && hydrated && min !== undefined\n ? normalizeTemporalInputValue(\n toLocalDateTime(\n min,\n minimum.kind === \"instant\" ? minimum.precision : undefined,\n ),\n )\n : min === undefined\n ? undefined\n : normalizeTemporalInputValue(min)\n }\n max={\n inputType === \"datetime\" && hydrated && max !== undefined\n ? normalizeTemporalInputValue(\n toLocalDateTime(\n max,\n maximum.kind === \"instant\" ? maximum.precision : undefined,\n ),\n )\n : max === undefined\n ? undefined\n : normalizeTemporalInputValue(max)\n }\n step={\n inputType !== \"date\"\n ? getTemporalInputStep(selection.anchor, currentValue, min, max)\n : undefined\n }\n onChange={(e) => {\n const inputValue = normalizeTemporalInputValue(e.currentTarget.value);\n const nextValue =\n inputType === \"datetime\"\n ? fromLocalDateTime(inputValue, selection.anchor)\n : inputValue;\n const canonical = canonicalFieldValue(nextValue);\n if (canonical !== undefined) {\n e.currentTarget.setAttribute(FIELD_VALUE_ATTR, canonical);\n } else {\n e.currentTarget.removeAttribute(FIELD_VALUE_ATTR);\n }\n setSelection({\n initialValue,\n value: nextValue,\n anchor: selection.anchor,\n });\n updateBinding?.(nextValue);\n fire($action, $dispatch, nextValue, e.currentTarget);\n }}\n />\n );\n}\n\ntype CheckboxRenderProps = {\n checked?: boolean;\n label: string;\n name?: string;\n defaultChecked?: boolean;\n variant?: \"checkbox\" | \"switch\";\n $status: GenerativeUIStatus;\n $action?: Action;\n $dispatch?: GenerativeUIDispatch;\n};\n\nfunction CheckboxRender({\n checked,\n label,\n name,\n defaultChecked,\n variant = \"checkbox\",\n $action,\n $dispatch,\n}: CheckboxRenderProps) {\n const answeredValue = useAnsweredValue(name);\n const updateBinding = useA2uiBinding(name);\n const isBound = updateBinding !== undefined;\n const initialChecked =\n typeof answeredValue === \"boolean\" ? answeredValue : defaultChecked;\n return (\n <label\n data-aui=\"checkbox\"\n data-aui-variant={variant === \"switch\" ? variant : undefined}\n >\n <input\n key={String(initialChecked)}\n type=\"checkbox\"\n role={variant === \"switch\" ? \"switch\" : undefined}\n data-aui-action={actionAttr($action)}\n name={name}\n {...(isBound\n ? {\n checked:\n typeof answeredValue === \"boolean\"\n ? answeredValue\n : checked === true,\n }\n : { defaultChecked: initialChecked })}\n onChange={(e) => {\n updateBinding?.(e.currentTarget.checked);\n fire($action, $dispatch, e.currentTarget.checked, e.currentTarget);\n }}\n />\n <span data-aui=\"checkbox-label\">{toTextContent(label)}</span>\n </label>\n );\n}\n\nconst finiteNumber = (value: unknown): value is number =>\n typeof value === \"number\" && Number.isFinite(value);\n\nconst clamp = (value: number, min: number, max: number): number =>\n Math.min(Math.max(value, min), max);\n\ntype SliderRenderProps = {\n value?: number;\n name?: string;\n label?: string;\n min: number;\n max: number;\n step?: number;\n defaultValue?: number;\n unit?: string;\n $status: GenerativeUIStatus;\n $action?: Action;\n $dispatch?: GenerativeUIDispatch;\n};\n\ntype SliderControlProps = {\n controlledValue?: number;\n name?: string;\n label?: string;\n min: number;\n max: number;\n step: number;\n initialValue: number;\n unit?: string;\n $action?: Action;\n $dispatch?: GenerativeUIDispatch;\n};\n\nfunction SliderControl({\n controlledValue,\n name,\n label,\n min,\n max,\n step,\n initialValue,\n unit,\n $action,\n $dispatch,\n}: SliderControlProps) {\n const updateBinding = useA2uiBinding(name);\n const [localValue, setValue] = React.useState(initialValue);\n const value = controlledValue ?? localValue;\n const pointerActive = React.useRef(false);\n const keyboardActive = React.useRef(false);\n const lastCommitted = React.useRef(initialValue);\n const currentValue = (input: HTMLInputElement) =>\n clamp(Number(input.value), min, max);\n const commit = (input: HTMLInputElement) => {\n if (controlledValue !== undefined) return;\n const nextValue = currentValue(input);\n setValue(nextValue);\n if (lastCommitted.current === nextValue) return;\n lastCommitted.current = nextValue;\n fire($action, $dispatch, nextValue, input);\n };\n\n return (\n <label data-aui=\"slider-field\">\n {label ? <span data-aui=\"slider-label\">{label}</span> : null}\n <input\n type=\"range\"\n data-aui=\"slider\"\n data-aui-action={actionAttr($action)}\n name={name}\n aria-label={label}\n aria-valuetext={unit ? `${value} ${unit}` : String(value)}\n min={min}\n max={max}\n step={step}\n {...(controlledValue !== undefined\n ? { value }\n : { defaultValue: initialValue })}\n onInput={(e) => {\n const nextValue = currentValue(e.currentTarget);\n if (controlledValue !== undefined) updateBinding?.(nextValue);\n else setValue(nextValue);\n }}\n onPointerDown={() => {\n pointerActive.current = true;\n }}\n onPointerUp={(e) => {\n pointerActive.current = false;\n commit(e.currentTarget);\n }}\n onKeyDown={(e) => {\n keyboardActive.current = [\n \"ArrowDown\",\n \"ArrowLeft\",\n \"ArrowRight\",\n \"ArrowUp\",\n \"End\",\n \"Home\",\n \"PageDown\",\n \"PageUp\",\n ].includes(e.key);\n }}\n onKeyUp={(e) => {\n if (!keyboardActive.current) return;\n keyboardActive.current = false;\n commit(e.currentTarget);\n }}\n onChange={(e) => {\n if (!pointerActive.current && !keyboardActive.current)\n commit(e.currentTarget);\n }}\n />\n <output data-aui=\"slider-value\">\n {value}\n {unit ? ` ${unit}` : \"\"}\n </output>\n </label>\n );\n}\n\nfunction SliderRender({\n value,\n name,\n label,\n min,\n max,\n step,\n defaultValue,\n unit,\n $action,\n $dispatch,\n}: SliderRenderProps) {\n const answeredValue = useAnsweredValue(name);\n const updateBinding = useA2uiBinding(name);\n const isBound = updateBinding !== undefined;\n const safeMin = finiteNumber(min) ? min : 0;\n const safeMax = finiteNumber(max) ? Math.max(max, safeMin) : safeMin + 100;\n const safeStep = finiteNumber(step) && step > 0 ? step : 1;\n const defaultNumber = finiteNumber(isBound ? value : defaultValue)\n ? isBound\n ? value!\n : defaultValue!\n : safeMin;\n const initialValue = clamp(\n finiteNumber(answeredValue) ? answeredValue : defaultNumber,\n safeMin,\n safeMax,\n );\n return (\n <SliderControl\n key={`${safeMin}:${safeMax}:${safeStep}:${isBound ? \"bound\" : initialValue}`}\n {...(isBound ? { controlledValue: initialValue } : {})}\n min={safeMin}\n max={safeMax}\n step={safeStep}\n initialValue={initialValue}\n {...(name !== undefined ? { name } : {})}\n {...(label !== undefined ? { label } : {})}\n {...(unit !== undefined ? { unit } : {})}\n {...($action !== undefined ? { $action } : {})}\n {...($dispatch !== undefined ? { $dispatch } : {})}\n />\n );\n}\n\nconst UNDO_WINDOW_SECONDS = 5;\n\ntype ButtonRenderProps = {\n label: string;\n buttonStyle?: (typeof BUTTON_STYLES)[number];\n block?: boolean;\n submit?: boolean;\n undoable?: boolean;\n children?: React.ReactNode;\n $status: GenerativeUIStatus;\n $action?: Action;\n $dispatch?: GenerativeUIDispatch;\n};\n\nfunction ButtonRender({\n label,\n buttonStyle,\n block,\n submit,\n undoable,\n children,\n $action,\n $dispatch,\n}: ButtonRenderProps) {\n const bindings = React.useContext(A2uiBindingContext!)?.fields;\n const bindingsRef = React.useRef(bindings);\n bindingsRef.current = bindings;\n const [remaining, setRemaining] = React.useState<number | undefined>(\n undefined,\n );\n const labelText = String(toTextContent(label) ?? \"\");\n const undoLabel = labelText.trim();\n const pendingAction = React.useRef<\n | {\n $action: Action;\n $dispatch: GenerativeUIDispatch;\n source: HTMLButtonElement;\n }\n | undefined\n >(undefined);\n const canUndo =\n undoable && !submit && $action !== undefined && $dispatch !== undefined;\n\n React.useEffect(() => {\n if (remaining === undefined) return;\n const timer = setTimeout(() => {\n if (remaining > 1) {\n setRemaining(remaining - 1);\n return;\n }\n const action = pendingAction.current;\n pendingAction.current = undefined;\n setRemaining(undefined);\n if (action)\n fire(\n action.$action,\n action.$dispatch,\n undefined,\n action.source,\n bindingsRef.current,\n );\n }, 1_000);\n return () => clearTimeout(timer);\n }, [remaining]);\n\n const cancelUndo = () => {\n pendingAction.current = undefined;\n setRemaining(undefined);\n };\n\n return (\n <button\n type={submit ? \"submit\" : \"button\"}\n data-aui=\"button\"\n data-aui-style={buttonStyle}\n data-aui-block={block || undefined}\n data-aui-submit={submit || undefined}\n data-aui-state={remaining === undefined ? undefined : \"pending\"}\n data-aui-action={actionAttr($action)}\n aria-label={\n remaining === undefined\n ? undefined\n : undoLabel\n ? `Undo ${undoLabel}`\n : \"Undo\"\n }\n onClick={\n submit\n ? undefined\n : canUndo\n ? (e) => {\n if (remaining !== undefined) {\n cancelUndo();\n return;\n }\n pendingAction.current = {\n $action,\n $dispatch,\n source: e.currentTarget,\n };\n setRemaining(UNDO_WINDOW_SECONDS);\n }\n : (e) =>\n fire($action, $dispatch, undefined, e.currentTarget, bindings)\n }\n onKeyDown={\n canUndo\n ? (e) => {\n if (e.key !== \"Escape\" || remaining === undefined) return;\n e.preventDefault();\n cancelUndo();\n }\n : undefined\n }\n >\n {remaining === undefined ? (\n <>\n {labelText}\n {children}\n </>\n ) : (\n <>\n Undo <span data-aui=\"button-countdown\">{remaining}</span>\n <span data-aui=\"button-undo-status\" role=\"status\">\n {labelText} in {UNDO_WINDOW_SECONDS} seconds\n </span>\n </>\n )}\n </button>\n );\n}\n\nexport const interactiveVocabulary = {\n Button: {\n description:\n \"A clickable button. Carries `$action` describing the side effect or resume value. Set `submit` to submit an ancestor Form/Card instead of firing `$action` on click.\",\n properties: z.object({\n label: z.string().describe(\"Button label.\"),\n buttonStyle: z.enum(BUTTON_STYLES).optional().describe(\"Visual style.\"),\n block: z\n .boolean()\n .optional()\n .describe(\"Whether the button spans the full width.\"),\n submit: z\n .boolean()\n .optional()\n .describe(\n \"Render as a submit button for an ancestor Form/Card instead of a click button.\",\n ),\n undoable: z\n .boolean()\n .optional()\n .describe(\n \"Delay the action with an undo control, for sends and other hard-to-reverse actions.\",\n ),\n }),\n render: ButtonRender,\n },\n Select: {\n description:\n \"A dropdown selector. Carries `$action` describing the on-select behavior.\",\n properties: z.object({\n options: z.array(optionSchema).describe(\"Selectable options.\"),\n placeholder: z\n .string()\n .optional()\n .describe(\"Placeholder shown when nothing is selected.\"),\n label: z\n .string()\n .optional()\n .describe(\"Accessible label for the control.\"),\n name: z.string().optional().describe(\"Field name used inside a Form.\"),\n defaultValue: z.string().optional().describe(\"Initially selected value.\"),\n }),\n render: SelectRender,\n },\n Input: {\n description:\n \"A text input. Carries `$action` describing the on-submit behavior.\",\n properties: z.object({\n inputType: z\n .enum([\"text\", \"password\", \"number\"])\n .optional()\n .describe(\"Single-line input type. Values remain strings.\"),\n placeholder: z.string().optional().describe(\"Placeholder text.\"),\n multiline: z\n .boolean()\n .optional()\n .describe(\n 'Render a textarea instead of a single-line input; ignored when `inputType` is `\"password\"` or `\"number\"`.',\n ),\n label: z\n .string()\n .optional()\n .describe(\"Accessible label for the control.\"),\n name: z.string().optional().describe(\"Field name used inside a Form.\"),\n defaultValue: z.string().optional().describe(\"Initial text.\"),\n }),\n render: InputRender,\n },\n DatePicker: {\n description:\n \"A date, datetime, or time input. Carries `$action` describing the on-select behavior.\",\n properties: z.object({\n inputType: z\n .enum([\"date\", \"datetime\", \"time\"])\n .optional()\n .describe(\n 'Temporal input type. Defaults to \"date\". Values remain strings.',\n ),\n value: z\n .string()\n .optional()\n .describe(\n \"Initial value: YYYY-MM-DD for date, HH:mm or HH:mm:ss with optional fraction of at most 9 digits for time, YYYY-MM-DDTHH:mm with optional :ss and fraction of at most 9 digits for datetime. A datetime with Z or ±HH:mm is an instant, displayed in the viewer's time zone and submitted with the same offset and precision. A datetime without an offset is local and submitted unchanged. An empty datetime submits with the viewer's offset and seconds.\",\n ),\n min: z\n .string()\n .optional()\n .describe(\n \"Minimum value: YYYY-MM-DD for date, HH:mm or HH:mm:ss with optional fraction of at most 9 digits for time, YYYY-MM-DDTHH:mm with optional :ss and fraction of at most 9 digits for datetime. Datetimes with Z or ±HH:mm are converted to the viewer's time zone.\",\n ),\n max: z\n .string()\n .optional()\n .describe(\n \"Maximum value: YYYY-MM-DD for date, HH:mm or HH:mm:ss with optional fraction of at most 9 digits for time, YYYY-MM-DDTHH:mm with optional :ss and fraction of at most 9 digits for datetime. Datetimes with Z or ±HH:mm are converted to the viewer's time zone.\",\n ),\n label: z\n .string()\n .optional()\n .describe(\"Accessible label for the control.\"),\n name: z.string().optional().describe(\"Field name used inside a Form.\"),\n }),\n render: DatePickerRender,\n },\n Checkbox: {\n description:\n \"A checkbox with a label. Carries `$action` describing the on-change behavior.\",\n properties: z.object({\n label: z.string().describe(\"Label text next to the checkbox.\"),\n name: z.string().optional().describe(\"Field name used inside a Form.\"),\n defaultChecked: z\n .boolean()\n .optional()\n .describe(\"Whether the checkbox starts checked.\"),\n variant: z\n .enum([\"checkbox\", \"switch\"])\n .optional()\n .describe(\"Whether to render a checkbox or switch.\"),\n }),\n render: CheckboxRender,\n },\n Slider: {\n description:\n \"A numeric range control. Carries `$action` describing the committed value.\",\n properties: z.object({\n name: z.string().optional().describe(\"Field name used inside a Form.\"),\n label: z\n .string()\n .optional()\n .describe(\"Accessible label for the control.\"),\n min: z.number().describe(\"Minimum value.\"),\n max: z.number().describe(\"Maximum value.\"),\n step: z.number().optional().describe(\"Value increment. Defaults to 1.\"),\n defaultValue: z.number().optional().describe(\"Initial value.\"),\n unit: z.string().optional().describe(\"Unit shown with the value.\"),\n }),\n render: SliderRender,\n },\n RadioGroup: {\n description:\n \"A group of mutually exclusive radio options. Carries `$action` describing the on-change behavior.\",\n properties: z.object({\n options: z.array(describedOptionSchema).describe(\"Selectable options.\"),\n name: z.string().optional().describe(\"Field name used inside a Form.\"),\n label: z.string().optional().describe(\"Accessible name for the group.\"),\n defaultValue: z.string().optional().describe(\"Initially selected value.\"),\n }),\n render: RadioGroupRender,\n },\n CheckboxGroup: {\n description:\n \"A group of checkbox options where any number can be checked. Carries `$action` describing the on-change behavior.\",\n properties: z.object({\n options: z.array(describedOptionSchema).describe(\"Selectable options.\"),\n name: z.string().optional().describe(\"Field name used inside a Form.\"),\n label: z.string().optional().describe(\"Accessible name for the group.\"),\n defaultValue: z\n .array(z.string())\n .optional()\n .describe(\"Initially checked values.\"),\n }),\n render: CheckboxGroupRender,\n },\n} satisfies GenerativeUILibrary;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"interactive.js","names":[],"sources":["../../src/vocabulary/interactive.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { z } from \"zod\";\nimport {\n CHECKBOX_GROUP_ATTR,\n FIELD_NAME_ATTR,\n FIELD_VALUE_ATTR,\n GENERATED_NAME_ATTR,\n} from \"../constants\";\nimport type { Action } from \"../ir\";\nimport { BUTTON_STYLES } from \"../ir\";\nimport type {\n GenerativeUIDispatch,\n GenerativeUILibrary,\n GenerativeUIStatus,\n} from \"../types\";\nimport { A2uiBindingContext, useA2uiBinding } from \"../a2ui/BindingContext\";\nimport { useAnsweredValue } from \"../answeredValues\";\nimport { actionAttr, fire } from \"./dispatch\";\nimport { toTextContent } from \"./toTextContent\";\nimport { useRadioGroupName } from \"../RadioGroupScope\";\nimport {\n classifyTemporal,\n fromLocalDateTime,\n getTemporalInputStep,\n normalizeTemporalInputValue,\n toLocalDateTime,\n toPickerLocalDateTime,\n} from \"../temporal\";\n\nconst optionSchema = z.object({\n label: z.string(),\n value: z.string(),\n});\n\nconst describedOptionSchema = optionSchema.extend({\n description: z.string().optional(),\n});\n\ntype Option = { label: string; description?: string; value: string };\n\nconst isOption = (option: unknown): option is Option =>\n option !== null &&\n typeof option === \"object\" &&\n \"label\" in option &&\n typeof option.label === \"string\" &&\n (!(\"description\" in option) || typeof option.description === \"string\") &&\n \"value\" in option &&\n typeof option.value === \"string\";\n\nconst mapOptions = (\n options: unknown,\n render: (option: Option, key: string) => React.ReactNode,\n) => {\n const occurrences = new Map<string, number>();\n return (Array.isArray(options) ? options : []).map((option) => {\n if (!isOption(option)) return null;\n const occurrence = occurrences.get(option.value) ?? 0;\n occurrences.set(option.value, occurrence + 1);\n return render(option, JSON.stringify([option.value, occurrence]));\n });\n};\n\ntype RadioGroupRenderProps = {\n value?: string;\n options: Option[];\n name?: string;\n label?: string;\n defaultValue?: string;\n children?: React.ReactNode;\n $status: GenerativeUIStatus;\n $action?: Action;\n $dispatch?: GenerativeUIDispatch;\n};\n\nfunction RadioGroupRender({\n value,\n options,\n name,\n label,\n defaultValue,\n children,\n $action,\n $dispatch,\n}: RadioGroupRenderProps) {\n const groupName = useRadioGroupName(name);\n const answeredValue = useAnsweredValue(name);\n const updateBinding = useA2uiBinding(name);\n const isBound = updateBinding !== undefined;\n const initialValue =\n typeof answeredValue === \"string\" ? answeredValue : defaultValue;\n return (\n <fieldset\n key={initialValue}\n data-aui=\"radiogroup\"\n data-aui-action={actionAttr($action)}\n aria-label={label}\n >\n {mapOptions(options, (option, key) => (\n <label key={key} data-aui=\"radiogroup-option\">\n <input\n type=\"radio\"\n name={groupName}\n {...{ [FIELD_NAME_ATTR]: name }}\n {...(name == null ? { [GENERATED_NAME_ATTR]: \"\" } : {})}\n value={option.value}\n {...(isBound\n ? { checked: (answeredValue ?? value) === option.value }\n : { defaultChecked: initialValue === option.value })}\n onChange={(e) => {\n updateBinding?.(option.value);\n fire($action, $dispatch, option.value, e.currentTarget);\n }}\n />\n {option.description ? (\n <span data-aui=\"option-content\">\n <span data-aui=\"option-label\">{option.label}</span>\n <span data-aui=\"option-description\">{option.description}</span>\n </span>\n ) : (\n option.label\n )}\n </label>\n ))}\n {children}\n </fieldset>\n );\n}\n\ntype CheckboxGroupRenderProps = {\n value?: string[];\n options: Option[];\n name?: string;\n label?: string;\n defaultValue?: string[];\n children?: React.ReactNode;\n $status: GenerativeUIStatus;\n $action?: Action;\n $dispatch?: GenerativeUIDispatch;\n};\n\nconst checkedGroupValues = (input: HTMLInputElement): string[] =>\n Array.from(\n input\n .closest(\"fieldset\")\n ?.querySelectorAll<HTMLInputElement>(\n `:scope > label > input[${CHECKBOX_GROUP_ATTR}]:checked`,\n ) ?? [],\n (checkbox) => checkbox.value,\n );\n\nfunction CheckboxGroupRender({\n value,\n options,\n name,\n label,\n defaultValue,\n children,\n $action,\n $dispatch,\n}: CheckboxGroupRenderProps) {\n const generatedName = React.useId();\n const fieldName = name ?? generatedName;\n const answeredValue = useAnsweredValue(name);\n const updateBinding = useA2uiBinding(name);\n const isBound = updateBinding !== undefined;\n const checkedValues =\n Array.isArray(answeredValue) &&\n answeredValue.every((value): value is string => typeof value === \"string\")\n ? answeredValue\n : Array.isArray(defaultValue)\n ? defaultValue\n : [];\n return (\n <fieldset\n key={JSON.stringify(checkedValues)}\n data-aui=\"checkboxgroup\"\n data-aui-action={actionAttr($action)}\n aria-label={label}\n >\n {mapOptions(options, (option, key) => (\n <label key={key} data-aui=\"checkboxgroup-option\">\n <input\n type=\"checkbox\"\n name={fieldName}\n {...{ [CHECKBOX_GROUP_ATTR]: \"\" }}\n {...(name == null ? { [GENERATED_NAME_ATTR]: \"\" } : {})}\n value={option.value}\n {...(isBound\n ? {\n checked: (Array.isArray(answeredValue)\n ? checkedValues\n : (value ?? [])\n ).includes(option.value),\n }\n : { defaultChecked: checkedValues.includes(option.value) })}\n onChange={(e) => {\n const selected = checkedGroupValues(e.currentTarget);\n updateBinding?.(selected);\n fire($action, $dispatch, selected, e.currentTarget);\n }}\n />\n {option.description ? (\n <span data-aui=\"option-content\">\n <span data-aui=\"option-label\">{option.label}</span>\n <span data-aui=\"option-description\">{option.description}</span>\n </span>\n ) : (\n option.label\n )}\n </label>\n ))}\n {children}\n </fieldset>\n );\n}\n\ntype SelectRenderProps = {\n value?: string;\n options: Option[];\n placeholder?: string;\n label?: string;\n name?: string;\n defaultValue?: string;\n children?: React.ReactNode;\n $status: GenerativeUIStatus;\n $action?: Action;\n $dispatch?: GenerativeUIDispatch;\n};\n\nfunction SelectRender({\n value,\n options,\n placeholder,\n label,\n name,\n defaultValue,\n $action,\n $dispatch,\n children,\n}: SelectRenderProps) {\n const answeredValue = useAnsweredValue(name);\n const updateBinding = useA2uiBinding(name);\n const isBound = updateBinding !== undefined;\n const placeholderText = toTextContent(placeholder);\n const initialValue =\n typeof answeredValue === \"string\"\n ? answeredValue\n : typeof defaultValue === \"string\"\n ? defaultValue\n : \"\";\n return (\n <select\n key={initialValue}\n data-aui=\"select\"\n data-aui-action={actionAttr($action)}\n name={name}\n aria-label={label}\n {...(isBound\n ? {\n value:\n typeof answeredValue === \"string\" ? answeredValue : (value ?? \"\"),\n }\n : { defaultValue: initialValue })}\n onChange={(e) => {\n updateBinding?.(e.currentTarget.value);\n fire($action, $dispatch, e.currentTarget.value, e.currentTarget);\n }}\n >\n {placeholderText ? (\n <option value=\"\" disabled>\n {placeholderText}\n </option>\n ) : null}\n {mapOptions(options, (option, key) => (\n <option key={key} value={option.value}>\n {option.label}\n </option>\n ))}\n {children}\n </select>\n );\n}\n\ntype InputRenderProps = {\n value?: string;\n inputType?: \"text\" | \"password\" | \"number\";\n placeholder?: string;\n multiline?: boolean;\n label?: string;\n name?: string;\n defaultValue?: string;\n $status: GenerativeUIStatus;\n $action?: Action;\n $dispatch?: GenerativeUIDispatch;\n};\n\nfunction InputRender({\n value,\n inputType,\n placeholder,\n multiline,\n label,\n name,\n defaultValue,\n $action,\n $dispatch,\n}: InputRenderProps) {\n const answeredValue = useAnsweredValue(name);\n const updateBinding = useA2uiBinding(name);\n const isBound = updateBinding !== undefined;\n const initialValue =\n typeof answeredValue === \"string\" ? answeredValue : defaultValue;\n const submit = (control: HTMLInputElement | HTMLTextAreaElement) =>\n fire($action, $dispatch, control.value, control);\n return multiline && (inputType === undefined || inputType === \"text\") ? (\n <textarea\n key={initialValue}\n data-aui=\"input\"\n data-aui-multiline\n data-aui-action={actionAttr($action)}\n name={name}\n aria-label={label}\n placeholder={placeholder}\n {...(isBound\n ? {\n value:\n typeof answeredValue === \"string\" ? answeredValue : (value ?? \"\"),\n }\n : { defaultValue: initialValue })}\n onChange={\n updateBinding ? (e) => updateBinding(e.currentTarget.value) : undefined\n }\n onKeyDown={(e) => {\n if (\n e.key !== \"Enter\" ||\n !(e.ctrlKey || e.metaKey) ||\n e.nativeEvent.isComposing\n )\n return;\n if (e.currentTarget.form) {\n e.preventDefault();\n HTMLFormElement.prototype.requestSubmit.call(e.currentTarget.form);\n } else {\n submit(e.currentTarget);\n }\n }}\n />\n ) : (\n <input\n key={initialValue}\n type={inputType}\n step={inputType === \"number\" ? \"any\" : undefined}\n data-aui=\"input\"\n data-aui-action={actionAttr($action)}\n name={name}\n aria-label={label}\n placeholder={placeholder}\n {...(isBound\n ? {\n value:\n typeof answeredValue === \"string\" ? answeredValue : (value ?? \"\"),\n }\n : { defaultValue: initialValue })}\n onChange={\n updateBinding ? (e) => updateBinding(e.currentTarget.value) : undefined\n }\n onKeyDown={(e) => {\n if (\n e.key === \"Enter\" &&\n !e.nativeEvent.isComposing &&\n !e.currentTarget.form\n )\n submit(e.currentTarget);\n }}\n />\n );\n}\n\ntype DatePickerRenderProps = {\n inputType?: \"date\" | \"datetime\" | \"time\";\n value?: string;\n min?: string;\n max?: string;\n label?: string;\n name?: string;\n $status: GenerativeUIStatus;\n $action?: Action;\n $dispatch?: GenerativeUIDispatch;\n};\n\nconst subscribeToHydration = () => () => {};\nconst clientHydrationSnapshot = () => true;\nconst serverHydrationSnapshot = () => false;\n\nfunction DatePickerRender({\n inputType = \"date\",\n value,\n min,\n max,\n label,\n name,\n $action,\n $dispatch,\n}: DatePickerRenderProps) {\n const answeredValue = useAnsweredValue(name);\n const updateBinding = useA2uiBinding(name);\n const isBound = updateBinding !== undefined;\n const initialValue =\n typeof answeredValue === \"string\" ? answeredValue : value;\n const [selection, setSelection] = React.useState({\n initialValue,\n value: initialValue,\n anchor: initialValue,\n committedValue: initialValue,\n edited: false,\n });\n if (selection.initialValue !== initialValue) {\n const isEcho = isBound && initialValue === selection.value;\n setSelection({\n initialValue,\n value: initialValue,\n anchor: isEcho ? selection.anchor : initialValue,\n committedValue: isEcho ? selection.committedValue : initialValue,\n edited: isEcho && selection.edited,\n });\n }\n const currentValue = isBound ? initialValue : selection.value;\n const temporal = classifyTemporal(currentValue ?? \"\");\n const anchor = classifyTemporal(selection.anchor ?? \"\");\n const hasInstantAnchor =\n inputType === \"datetime\" && anchor.kind === \"instant\";\n const canonicalFieldValue = (value: string | undefined) => {\n const current = value ?? \"\";\n const kind = classifyTemporal(current).kind;\n if (inputType === \"time\")\n return kind === \"time\" && normalizeTemporalInputValue(current) !== current\n ? value\n : undefined;\n if (inputType !== \"datetime\") return undefined;\n if (kind === \"instant\") return value;\n if (hasInstantAnchor) return selection.anchor;\n return kind === \"floating\" &&\n normalizeTemporalInputValue(current) !== current\n ? value\n : undefined;\n };\n const minimum = classifyTemporal(min ?? \"\");\n const maximum = classifyTemporal(max ?? \"\");\n const hydrated = React.useSyncExternalStore(\n subscribeToHydration,\n clientHydrationSnapshot,\n serverHydrationSnapshot,\n );\n const showRawValue =\n inputType === \"datetime\" && temporal.kind === \"instant\" && !hydrated;\n const displayValue =\n currentValue === undefined\n ? undefined\n : inputType === \"datetime\" && hydrated\n ? toPickerLocalDateTime(currentValue)\n : normalizeTemporalInputValue(currentValue);\n const keyboardActive = React.useRef(false);\n const valueFromInput = (input: HTMLInputElement) => {\n const inputValue = normalizeTemporalInputValue(input.value);\n return inputType === \"datetime\"\n ? fromLocalDateTime(inputValue, selection.anchor)\n : inputValue;\n };\n const commit = (input: HTMLInputElement) => {\n if (!selection.edited) return;\n const nextValue = valueFromInput(input);\n setSelection({ ...selection, committedValue: nextValue, edited: false });\n if (nextValue !== selection.committedValue)\n fire($action, $dispatch, nextValue, input);\n };\n return (\n <input\n key={isBound ? undefined : initialValue}\n type={\n showRawValue\n ? \"text\"\n : inputType === \"datetime\"\n ? \"datetime-local\"\n : inputType\n }\n readOnly={showRawValue || undefined}\n data-aui=\"datepicker\"\n data-aui-action={actionAttr($action)}\n {...{\n [FIELD_VALUE_ATTR]: canonicalFieldValue(currentValue),\n }}\n name={name}\n aria-label={label}\n {...(isBound || inputType === \"datetime\"\n ? { value: displayValue ?? \"\" }\n : { defaultValue: displayValue })}\n min={\n inputType === \"datetime\" && hydrated && min !== undefined\n ? normalizeTemporalInputValue(\n toLocalDateTime(\n min,\n minimum.kind === \"instant\" ? minimum.precision : undefined,\n ),\n )\n : min === undefined\n ? undefined\n : normalizeTemporalInputValue(min)\n }\n max={\n inputType === \"datetime\" && hydrated && max !== undefined\n ? normalizeTemporalInputValue(\n toLocalDateTime(\n max,\n maximum.kind === \"instant\" ? maximum.precision : undefined,\n ),\n )\n : max === undefined\n ? undefined\n : normalizeTemporalInputValue(max)\n }\n step={\n inputType !== \"date\"\n ? getTemporalInputStep(\n selection.anchor,\n hasInstantAnchor ? displayValue : currentValue,\n min,\n max,\n )\n : undefined\n }\n onChange={(e) => {\n const nextValue = valueFromInput(e.currentTarget);\n const canonical = canonicalFieldValue(nextValue);\n if (canonical !== undefined) {\n e.currentTarget.setAttribute(FIELD_VALUE_ATTR, canonical);\n } else {\n e.currentTarget.removeAttribute(FIELD_VALUE_ATTR);\n }\n // A change without an editing key down is a pick from the native picker, which commits at once; a typed value waits for blur or Enter.\n const typed = keyboardActive.current;\n keyboardActive.current = false;\n setSelection({\n initialValue,\n value: nextValue,\n anchor: selection.anchor,\n committedValue: typed ? selection.committedValue : nextValue,\n edited: typed,\n });\n updateBinding?.(nextValue);\n if (!typed && nextValue !== selection.committedValue)\n fire($action, $dispatch, nextValue, e.currentTarget);\n }}\n onBlur={(e) => {\n keyboardActive.current = false;\n commit(e.currentTarget);\n }}\n onKeyDown={(e) => {\n if (\n e.key === \"Enter\" &&\n !e.nativeEvent.isComposing &&\n !e.currentTarget.form\n ) {\n commit(e.currentTarget);\n return;\n }\n keyboardActive.current =\n !e.altKey &&\n !e.ctrlKey &&\n !e.metaKey &&\n (e.key.length === 1 ||\n [\n \"ArrowDown\",\n \"ArrowUp\",\n \"Backspace\",\n \"Delete\",\n \"End\",\n \"Home\",\n \"PageDown\",\n \"PageUp\",\n ].includes(e.key));\n }}\n onKeyUp={() => {\n keyboardActive.current = false;\n }}\n onPaste={() => {\n keyboardActive.current = true;\n }}\n onCut={() => {\n keyboardActive.current = true;\n }}\n />\n );\n}\n\ntype CheckboxRenderProps = {\n checked?: boolean;\n label: string;\n name?: string;\n defaultChecked?: boolean;\n variant?: \"checkbox\" | \"switch\";\n $status: GenerativeUIStatus;\n $action?: Action;\n $dispatch?: GenerativeUIDispatch;\n};\n\nfunction CheckboxRender({\n checked,\n label,\n name,\n defaultChecked,\n variant = \"checkbox\",\n $action,\n $dispatch,\n}: CheckboxRenderProps) {\n const answeredValue = useAnsweredValue(name);\n const updateBinding = useA2uiBinding(name);\n const isBound = updateBinding !== undefined;\n const initialChecked =\n typeof answeredValue === \"boolean\" ? answeredValue : defaultChecked;\n return (\n <label\n data-aui=\"checkbox\"\n data-aui-variant={variant === \"switch\" ? variant : undefined}\n >\n <input\n key={String(initialChecked)}\n type=\"checkbox\"\n role={variant === \"switch\" ? \"switch\" : undefined}\n data-aui-action={actionAttr($action)}\n name={name}\n {...(isBound\n ? {\n checked:\n typeof answeredValue === \"boolean\"\n ? answeredValue\n : checked === true,\n }\n : { defaultChecked: initialChecked })}\n onChange={(e) => {\n updateBinding?.(e.currentTarget.checked);\n fire($action, $dispatch, e.currentTarget.checked, e.currentTarget);\n }}\n />\n <span data-aui=\"checkbox-label\">{toTextContent(label)}</span>\n </label>\n );\n}\n\nconst finiteNumber = (value: unknown): value is number =>\n typeof value === \"number\" && Number.isFinite(value);\n\nconst clamp = (value: number, min: number, max: number): number =>\n Math.min(Math.max(value, min), max);\n\ntype SliderRenderProps = {\n value?: number;\n name?: string;\n label?: string;\n min: number;\n max: number;\n step?: number;\n defaultValue?: number;\n unit?: string;\n $status: GenerativeUIStatus;\n $action?: Action;\n $dispatch?: GenerativeUIDispatch;\n};\n\ntype SliderControlProps = {\n controlledValue?: number;\n name?: string;\n label?: string;\n min: number;\n max: number;\n step: number;\n initialValue: number;\n unit?: string;\n $action?: Action;\n $dispatch?: GenerativeUIDispatch;\n};\n\nfunction SliderControl({\n controlledValue,\n name,\n label,\n min,\n max,\n step,\n initialValue,\n unit,\n $action,\n $dispatch,\n}: SliderControlProps) {\n const updateBinding = useA2uiBinding(name);\n const [localValue, setValue] = React.useState(initialValue);\n const value = controlledValue ?? localValue;\n const pointerActive = React.useRef(false);\n const keyboardActive = React.useRef(false);\n const lastCommitted = React.useRef(initialValue);\n const currentValue = (input: HTMLInputElement) =>\n clamp(Number(input.value), min, max);\n const commit = (input: HTMLInputElement) => {\n if (controlledValue !== undefined) return;\n const nextValue = currentValue(input);\n setValue(nextValue);\n if (lastCommitted.current === nextValue) return;\n lastCommitted.current = nextValue;\n fire($action, $dispatch, nextValue, input);\n };\n\n return (\n <label data-aui=\"slider-field\">\n {label ? <span data-aui=\"slider-label\">{label}</span> : null}\n <input\n type=\"range\"\n data-aui=\"slider\"\n data-aui-action={actionAttr($action)}\n name={name}\n aria-label={label}\n aria-valuetext={unit ? `${value} ${unit}` : String(value)}\n min={min}\n max={max}\n step={step}\n {...(controlledValue !== undefined\n ? { value }\n : { defaultValue: initialValue })}\n onInput={(e) => {\n const nextValue = currentValue(e.currentTarget);\n if (controlledValue !== undefined) updateBinding?.(nextValue);\n else setValue(nextValue);\n }}\n onPointerDown={() => {\n pointerActive.current = true;\n }}\n onPointerUp={(e) => {\n pointerActive.current = false;\n commit(e.currentTarget);\n }}\n onKeyDown={(e) => {\n keyboardActive.current = [\n \"ArrowDown\",\n \"ArrowLeft\",\n \"ArrowRight\",\n \"ArrowUp\",\n \"End\",\n \"Home\",\n \"PageDown\",\n \"PageUp\",\n ].includes(e.key);\n }}\n onKeyUp={(e) => {\n if (!keyboardActive.current) return;\n keyboardActive.current = false;\n commit(e.currentTarget);\n }}\n onChange={(e) => {\n if (!pointerActive.current && !keyboardActive.current)\n commit(e.currentTarget);\n }}\n />\n <output data-aui=\"slider-value\">\n {value}\n {unit ? ` ${unit}` : \"\"}\n </output>\n </label>\n );\n}\n\nfunction SliderRender({\n value,\n name,\n label,\n min,\n max,\n step,\n defaultValue,\n unit,\n $action,\n $dispatch,\n}: SliderRenderProps) {\n const answeredValue = useAnsweredValue(name);\n const updateBinding = useA2uiBinding(name);\n const isBound = updateBinding !== undefined;\n const safeMin = finiteNumber(min) ? min : 0;\n const safeMax = finiteNumber(max) ? Math.max(max, safeMin) : safeMin + 100;\n const safeStep = finiteNumber(step) && step > 0 ? step : 1;\n const defaultNumber = finiteNumber(isBound ? value : defaultValue)\n ? isBound\n ? value!\n : defaultValue!\n : safeMin;\n const initialValue = clamp(\n finiteNumber(answeredValue) ? answeredValue : defaultNumber,\n safeMin,\n safeMax,\n );\n return (\n <SliderControl\n key={`${safeMin}:${safeMax}:${safeStep}:${isBound ? \"bound\" : initialValue}`}\n {...(isBound ? { controlledValue: initialValue } : {})}\n min={safeMin}\n max={safeMax}\n step={safeStep}\n initialValue={initialValue}\n {...(name !== undefined ? { name } : {})}\n {...(label !== undefined ? { label } : {})}\n {...(unit !== undefined ? { unit } : {})}\n {...($action !== undefined ? { $action } : {})}\n {...($dispatch !== undefined ? { $dispatch } : {})}\n />\n );\n}\n\nconst UNDO_WINDOW_SECONDS = 5;\n\ntype ButtonRenderProps = {\n label: string;\n buttonStyle?: (typeof BUTTON_STYLES)[number];\n block?: boolean;\n submit?: boolean;\n undoable?: boolean;\n children?: React.ReactNode;\n $status: GenerativeUIStatus;\n $action?: Action;\n $dispatch?: GenerativeUIDispatch;\n};\n\nfunction ButtonRender({\n label,\n buttonStyle,\n block,\n submit,\n undoable,\n children,\n $action,\n $dispatch,\n}: ButtonRenderProps) {\n const bindings = React.useContext(A2uiBindingContext!)?.fields;\n const bindingsRef = React.useRef(bindings);\n bindingsRef.current = bindings;\n const [remaining, setRemaining] = React.useState<number | undefined>(\n undefined,\n );\n const labelText = String(toTextContent(label) ?? \"\");\n const undoLabel = labelText.trim();\n const pendingAction = React.useRef<\n | {\n $action: Action;\n $dispatch: GenerativeUIDispatch;\n source: HTMLButtonElement;\n }\n | undefined\n >(undefined);\n const canUndo =\n undoable && !submit && $action !== undefined && $dispatch !== undefined;\n\n React.useEffect(() => {\n if (remaining === undefined) return;\n const timer = setTimeout(() => {\n if (remaining > 1) {\n setRemaining(remaining - 1);\n return;\n }\n const action = pendingAction.current;\n pendingAction.current = undefined;\n setRemaining(undefined);\n if (action)\n fire(\n action.$action,\n action.$dispatch,\n undefined,\n action.source,\n bindingsRef.current,\n );\n }, 1_000);\n return () => clearTimeout(timer);\n }, [remaining]);\n\n const cancelUndo = () => {\n pendingAction.current = undefined;\n setRemaining(undefined);\n };\n\n return (\n <button\n type={submit ? \"submit\" : \"button\"}\n data-aui=\"button\"\n data-aui-style={buttonStyle}\n data-aui-block={block || undefined}\n data-aui-submit={submit || undefined}\n data-aui-state={remaining === undefined ? undefined : \"pending\"}\n data-aui-action={actionAttr($action)}\n aria-label={\n remaining === undefined\n ? undefined\n : undoLabel\n ? `Undo ${undoLabel}`\n : \"Undo\"\n }\n onClick={\n submit\n ? undefined\n : canUndo\n ? (e) => {\n if (remaining !== undefined) {\n cancelUndo();\n return;\n }\n pendingAction.current = {\n $action,\n $dispatch,\n source: e.currentTarget,\n };\n setRemaining(UNDO_WINDOW_SECONDS);\n }\n : (e) =>\n fire($action, $dispatch, undefined, e.currentTarget, bindings)\n }\n onKeyDown={\n canUndo\n ? (e) => {\n if (e.key !== \"Escape\" || remaining === undefined) return;\n e.preventDefault();\n cancelUndo();\n }\n : undefined\n }\n >\n {remaining === undefined ? (\n <>\n {labelText}\n {children}\n </>\n ) : (\n <>\n Undo <span data-aui=\"button-countdown\">{remaining}</span>\n <span data-aui=\"button-undo-status\" role=\"status\">\n {labelText} in {UNDO_WINDOW_SECONDS} seconds\n </span>\n </>\n )}\n </button>\n );\n}\n\nexport const interactiveVocabulary = {\n Button: {\n description:\n \"A clickable button. Carries `$action` describing the side effect or resume value. Set `submit` to submit an ancestor Form/Card instead of firing `$action` on click.\",\n properties: z.object({\n label: z.string().describe(\"Button label.\"),\n buttonStyle: z.enum(BUTTON_STYLES).optional().describe(\"Visual style.\"),\n block: z\n .boolean()\n .optional()\n .describe(\"Whether the button spans the full width.\"),\n submit: z\n .boolean()\n .optional()\n .describe(\n \"Render as a submit button for an ancestor Form/Card instead of a click button.\",\n ),\n undoable: z\n .boolean()\n .optional()\n .describe(\n \"Delay the action with an undo control, for sends and other hard-to-reverse actions.\",\n ),\n }),\n render: ButtonRender,\n },\n Select: {\n description:\n \"A dropdown selector. Carries `$action` describing the on-select behavior.\",\n properties: z.object({\n options: z.array(optionSchema).describe(\"Selectable options.\"),\n placeholder: z\n .string()\n .optional()\n .describe(\"Placeholder shown when nothing is selected.\"),\n label: z\n .string()\n .optional()\n .describe(\"Accessible label for the control.\"),\n name: z.string().optional().describe(\"Field name used inside a Form.\"),\n defaultValue: z.string().optional().describe(\"Initially selected value.\"),\n }),\n render: SelectRender,\n },\n Input: {\n description:\n \"A text input. Carries `$action` describing the on-submit behavior.\",\n properties: z.object({\n inputType: z\n .enum([\"text\", \"password\", \"number\"])\n .optional()\n .describe(\"Single-line input type. Values remain strings.\"),\n placeholder: z.string().optional().describe(\"Placeholder text.\"),\n multiline: z\n .boolean()\n .optional()\n .describe(\n 'Render a textarea instead of a single-line input; ignored when `inputType` is `\"password\"` or `\"number\"`.',\n ),\n label: z\n .string()\n .optional()\n .describe(\"Accessible label for the control.\"),\n name: z.string().optional().describe(\"Field name used inside a Form.\"),\n defaultValue: z.string().optional().describe(\"Initial text.\"),\n }),\n render: InputRender,\n },\n DatePicker: {\n description:\n \"A date, datetime, or time input. Carries `$action`, which runs once per committed value: a pick from the picker, or a typed value on blur or Enter.\",\n properties: z.object({\n inputType: z\n .enum([\"date\", \"datetime\", \"time\"])\n .optional()\n .describe(\n 'Temporal input type. Defaults to \"date\". Values remain strings.',\n ),\n value: z\n .string()\n .optional()\n .describe(\n \"Initial value: YYYY-MM-DD for date, HH:mm or HH:mm:ss with optional fraction of at most 9 digits for time, YYYY-MM-DDTHH:mm with optional :ss and fraction of at most 9 digits for datetime. A datetime with Z or ±HH:mm is an instant, displayed in the viewer's time zone and submitted with the same offset and precision. A datetime without an offset is local and submitted unchanged. An empty datetime submits with the viewer's offset and seconds.\",\n ),\n min: z\n .string()\n .optional()\n .describe(\n \"Minimum value: YYYY-MM-DD for date, HH:mm or HH:mm:ss with optional fraction of at most 9 digits for time, YYYY-MM-DDTHH:mm with optional :ss and fraction of at most 9 digits for datetime. Datetimes with Z or ±HH:mm are converted to the viewer's time zone.\",\n ),\n max: z\n .string()\n .optional()\n .describe(\n \"Maximum value: YYYY-MM-DD for date, HH:mm or HH:mm:ss with optional fraction of at most 9 digits for time, YYYY-MM-DDTHH:mm with optional :ss and fraction of at most 9 digits for datetime. Datetimes with Z or ±HH:mm are converted to the viewer's time zone.\",\n ),\n label: z\n .string()\n .optional()\n .describe(\"Accessible label for the control.\"),\n name: z.string().optional().describe(\"Field name used inside a Form.\"),\n }),\n render: DatePickerRender,\n },\n Checkbox: {\n description:\n \"A checkbox with a label. Carries `$action` describing the on-change behavior.\",\n properties: z.object({\n label: z.string().describe(\"Label text next to the checkbox.\"),\n name: z.string().optional().describe(\"Field name used inside a Form.\"),\n defaultChecked: z\n .boolean()\n .optional()\n .describe(\"Whether the checkbox starts checked.\"),\n variant: z\n .enum([\"checkbox\", \"switch\"])\n .optional()\n .describe(\"Whether to render a checkbox or switch.\"),\n }),\n render: CheckboxRender,\n },\n Slider: {\n description:\n \"A numeric range control. Carries `$action` describing the committed value.\",\n properties: z.object({\n name: z.string().optional().describe(\"Field name used inside a Form.\"),\n label: z\n .string()\n .optional()\n .describe(\"Accessible label for the control.\"),\n min: z.number().describe(\"Minimum value.\"),\n max: z.number().describe(\"Maximum value.\"),\n step: z.number().optional().describe(\"Value increment. Defaults to 1.\"),\n defaultValue: z.number().optional().describe(\"Initial value.\"),\n unit: z.string().optional().describe(\"Unit shown with the value.\"),\n }),\n render: SliderRender,\n },\n RadioGroup: {\n description:\n \"A group of mutually exclusive radio options. Carries `$action` describing the on-change behavior.\",\n properties: z.object({\n options: z.array(describedOptionSchema).describe(\"Selectable options.\"),\n name: z.string().optional().describe(\"Field name used inside a Form.\"),\n label: z.string().optional().describe(\"Accessible name for the group.\"),\n defaultValue: z.string().optional().describe(\"Initially selected value.\"),\n }),\n render: RadioGroupRender,\n },\n CheckboxGroup: {\n description:\n \"A group of checkbox options where any number can be checked. Carries `$action` describing the on-change behavior.\",\n properties: z.object({\n options: z.array(describedOptionSchema).describe(\"Selectable options.\"),\n name: z.string().optional().describe(\"Field name used inside a Form.\"),\n label: z.string().optional().describe(\"Accessible name for the group.\"),\n defaultValue: z\n .array(z.string())\n .optional()\n .describe(\"Initially checked values.\"),\n }),\n render: CheckboxGroupRender,\n },\n} satisfies GenerativeUILibrary;\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@assistant-ui/react-generative-ui",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.24",
|
|
4
4
|
"description": "Generative UI components for assistant-ui",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"generative-ui",
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
"sideEffects": false,
|
|
52
52
|
"dependencies": {
|
|
53
53
|
"@types/json-schema": "^7.0.15",
|
|
54
|
-
"assistant-stream": "^0.3.
|
|
54
|
+
"assistant-stream": "^0.3.48"
|
|
55
55
|
},
|
|
56
56
|
"peerDependencies": {
|
|
57
57
|
"@assistant-ui/react": "^0.15.0",
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
}
|
|
66
66
|
},
|
|
67
67
|
"devDependencies": {
|
|
68
|
-
"@assistant-ui/react": "0.15.
|
|
68
|
+
"@assistant-ui/react": "0.15.25",
|
|
69
69
|
"@assistant-ui/x-buildutils": "0.0.30",
|
|
70
70
|
"@assistant-ui/x-react18": "0.0.0",
|
|
71
71
|
"@types/react": "^19.3.0",
|
|
@@ -91,6 +91,7 @@
|
|
|
91
91
|
"scripts": {
|
|
92
92
|
"build": "aui-build",
|
|
93
93
|
"test": "vitest run",
|
|
94
|
+
"test:coverage": "vitest run --coverage --coverage.reporter=text --coverage.reporter=html --coverage.reporter=json-summary",
|
|
94
95
|
"typecheck": "tsc --noEmit",
|
|
95
96
|
"test:peer-react18": "vitest run --config vitest.peer-react18.config.ts",
|
|
96
97
|
"test:types:peer-react18": "aui-react18-types",
|
package/src/temporal.test.ts
CHANGED
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
splitTemporalMinutes,
|
|
11
11
|
temporalOffsetLabel,
|
|
12
12
|
toLocalDateTime,
|
|
13
|
+
toPickerLocalDateTime,
|
|
13
14
|
} from "./temporal";
|
|
14
15
|
|
|
15
16
|
describe("temporal values", () => {
|
|
@@ -139,6 +140,23 @@ describe("temporal values", () => {
|
|
|
139
140
|
expect(new Date("2026-07-15T12:00Z").getTimezoneOffset()).toBe(240);
|
|
140
141
|
});
|
|
141
142
|
|
|
143
|
+
it.each(["0002", "0020", "0202"])(
|
|
144
|
+
"preserves minute edits in %s when the zone offset has seconds",
|
|
145
|
+
(year) => {
|
|
146
|
+
const local = `${year}-12-15T12:00`;
|
|
147
|
+
const value = fromLocalDateTime(local, "2025-12-15T17:00Z");
|
|
148
|
+
expect(value).toBe(`${year}-12-15T16:56:02Z`);
|
|
149
|
+
expect(toPickerLocalDateTime(value)).toBe(local);
|
|
150
|
+
},
|
|
151
|
+
);
|
|
152
|
+
|
|
153
|
+
it("preserves entered seconds with a minute precision anchor", () => {
|
|
154
|
+
const local = "2025-12-15T12:00:30";
|
|
155
|
+
const value = fromLocalDateTime(local, "2025-12-15T17:00Z");
|
|
156
|
+
expect(value).toBe("2025-12-15T17:00:30Z");
|
|
157
|
+
expect(toPickerLocalDateTime(value)).toBe(local);
|
|
158
|
+
});
|
|
159
|
+
|
|
142
160
|
it.each([
|
|
143
161
|
["2026-07-15", "date"],
|
|
144
162
|
["2024-02-29", "date"],
|
package/src/temporal.ts
CHANGED
|
@@ -243,6 +243,10 @@ export const fromLocalDateTime = (
|
|
|
243
243
|
return formatTemporalInstant({
|
|
244
244
|
...previous,
|
|
245
245
|
epochMs: date.getTime(),
|
|
246
|
+
...(previous.precision === "minutes" &&
|
|
247
|
+
(temporal.precision === "seconds" || date.getUTCSeconds() !== 0)
|
|
248
|
+
? { precision: "seconds" }
|
|
249
|
+
: {}),
|
|
246
250
|
...(date.getMilliseconds() ===
|
|
247
251
|
new Date(previous.epochMs).getUTCMilliseconds()
|
|
248
252
|
? {}
|
|
@@ -55,6 +55,28 @@ const change = async (input: HTMLInputElement, value: string) => {
|
|
|
55
55
|
});
|
|
56
56
|
};
|
|
57
57
|
|
|
58
|
+
const type = async (input: HTMLInputElement, value: string) => {
|
|
59
|
+
await React.act(async () => {
|
|
60
|
+
input.dispatchEvent(
|
|
61
|
+
new KeyboardEvent("keydown", { key: "1", bubbles: true }),
|
|
62
|
+
);
|
|
63
|
+
Object.getOwnPropertyDescriptor(
|
|
64
|
+
HTMLInputElement.prototype,
|
|
65
|
+
"value",
|
|
66
|
+
)!.set!.call(input, value);
|
|
67
|
+
input.dispatchEvent(new Event("input", { bubbles: true }));
|
|
68
|
+
input.dispatchEvent(
|
|
69
|
+
new KeyboardEvent("keyup", { key: "1", bubbles: true }),
|
|
70
|
+
);
|
|
71
|
+
});
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
const blur = async (input: HTMLInputElement) => {
|
|
75
|
+
await React.act(async () => {
|
|
76
|
+
input.dispatchEvent(new FocusEvent("focusout", { bubbles: true }));
|
|
77
|
+
});
|
|
78
|
+
};
|
|
79
|
+
|
|
58
80
|
const nativeValue = (type: string, value: string) => {
|
|
59
81
|
const input = document.createElement("input");
|
|
60
82
|
input.type = type;
|
|
@@ -66,6 +88,249 @@ const normalizedNativeValue = (type: string, value: string) =>
|
|
|
66
88
|
normalizeTemporalInputValue(nativeValue(type, value));
|
|
67
89
|
|
|
68
90
|
describe("DatePicker temporal contract", () => {
|
|
91
|
+
it("dispatches once for the final value on blur and resolves current fields", async () => {
|
|
92
|
+
const dispatch = vi.fn();
|
|
93
|
+
const container = await mount(
|
|
94
|
+
<div data-aui="root">
|
|
95
|
+
{view(
|
|
96
|
+
{
|
|
97
|
+
inputType: "datetime",
|
|
98
|
+
value: "2025-12-15T17:00Z",
|
|
99
|
+
$action: {
|
|
100
|
+
type: "save",
|
|
101
|
+
when: { $field: "when" },
|
|
102
|
+
note: { $field: "note" },
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
dispatch,
|
|
106
|
+
)}
|
|
107
|
+
<input name="note" defaultValue="before" />
|
|
108
|
+
</div>,
|
|
109
|
+
);
|
|
110
|
+
const input = container.querySelector<HTMLInputElement>(
|
|
111
|
+
'[data-aui="datepicker"]',
|
|
112
|
+
)!;
|
|
113
|
+
const note = container.querySelector<HTMLInputElement>('[name="note"]')!;
|
|
114
|
+
await type(input, "0002-12-15T12:00");
|
|
115
|
+
expect(dispatch).not.toHaveBeenCalled();
|
|
116
|
+
await type(input, "0020-12-15T12:00");
|
|
117
|
+
expect(dispatch).not.toHaveBeenCalled();
|
|
118
|
+
await type(input, "2025-12-16T12:00");
|
|
119
|
+
note.value = "after";
|
|
120
|
+
expect(dispatch).not.toHaveBeenCalled();
|
|
121
|
+
await blur(input);
|
|
122
|
+
expect(dispatch).toHaveBeenCalledExactlyOnceWith({
|
|
123
|
+
type: "save",
|
|
124
|
+
when: "2025-12-16T17:00Z",
|
|
125
|
+
note: "after",
|
|
126
|
+
$input: "2025-12-16T17:00Z",
|
|
127
|
+
});
|
|
128
|
+
await blur(input);
|
|
129
|
+
expect(dispatch).toHaveBeenCalledTimes(1);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it("dispatches a pick from the native picker at once", async () => {
|
|
133
|
+
const dispatch = vi.fn();
|
|
134
|
+
const container = await mount(
|
|
135
|
+
view({ value: "2025-12-15", $action: { type: "save" } }, dispatch),
|
|
136
|
+
);
|
|
137
|
+
const input = container.querySelector("input")!;
|
|
138
|
+
await change(input, "2025-12-20");
|
|
139
|
+
expect(dispatch).toHaveBeenCalledExactlyOnceWith({
|
|
140
|
+
type: "save",
|
|
141
|
+
$input: "2025-12-20",
|
|
142
|
+
});
|
|
143
|
+
await blur(input);
|
|
144
|
+
expect(dispatch).toHaveBeenCalledTimes(1);
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it("dispatches a pick at once after the keyboard opened the picker", async () => {
|
|
148
|
+
const dispatch = vi.fn();
|
|
149
|
+
const container = await mount(
|
|
150
|
+
view({ value: "2025-12-15", $action: { type: "save" } }, dispatch),
|
|
151
|
+
);
|
|
152
|
+
const input = container.querySelector("input")!;
|
|
153
|
+
await React.act(async () => {
|
|
154
|
+
input.dispatchEvent(
|
|
155
|
+
new KeyboardEvent("keydown", {
|
|
156
|
+
key: "ArrowDown",
|
|
157
|
+
altKey: true,
|
|
158
|
+
bubbles: true,
|
|
159
|
+
}),
|
|
160
|
+
);
|
|
161
|
+
});
|
|
162
|
+
await change(input, "2025-12-21");
|
|
163
|
+
expect(dispatch).toHaveBeenCalledExactlyOnceWith({
|
|
164
|
+
type: "save",
|
|
165
|
+
$input: "2025-12-21",
|
|
166
|
+
});
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
it.each(["PageUp", "PageDown", "Home", "End"])(
|
|
170
|
+
"holds a value stepped with %s for blur",
|
|
171
|
+
async (key) => {
|
|
172
|
+
const dispatch = vi.fn();
|
|
173
|
+
const container = await mount(
|
|
174
|
+
view({ value: "2025-12-15", $action: { type: "save" } }, dispatch),
|
|
175
|
+
);
|
|
176
|
+
const input = container.querySelector("input")!;
|
|
177
|
+
await React.act(async () => {
|
|
178
|
+
input.dispatchEvent(
|
|
179
|
+
new KeyboardEvent("keydown", { key, bubbles: true }),
|
|
180
|
+
);
|
|
181
|
+
});
|
|
182
|
+
await change(input, "2025-12-25");
|
|
183
|
+
expect(dispatch).not.toHaveBeenCalled();
|
|
184
|
+
await blur(input);
|
|
185
|
+
expect(dispatch).toHaveBeenCalledExactlyOnceWith({
|
|
186
|
+
type: "save",
|
|
187
|
+
$input: "2025-12-25",
|
|
188
|
+
});
|
|
189
|
+
},
|
|
190
|
+
);
|
|
191
|
+
|
|
192
|
+
it.each(["paste", "cut"])("holds a %s for blur", async (type) => {
|
|
193
|
+
const dispatch = vi.fn();
|
|
194
|
+
const container = await mount(
|
|
195
|
+
view({ value: "2025-12-15", $action: { type: "save" } }, dispatch),
|
|
196
|
+
);
|
|
197
|
+
const input = container.querySelector("input")!;
|
|
198
|
+
await React.act(async () => {
|
|
199
|
+
input.dispatchEvent(new Event(type, { bubbles: true }));
|
|
200
|
+
});
|
|
201
|
+
await change(input, "2025-12-26");
|
|
202
|
+
expect(dispatch).not.toHaveBeenCalled();
|
|
203
|
+
await blur(input);
|
|
204
|
+
expect(dispatch).toHaveBeenCalledExactlyOnceWith({
|
|
205
|
+
type: "save",
|
|
206
|
+
$input: "2025-12-26",
|
|
207
|
+
});
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
it("dispatches a pick at once after a typed digit whose keyup never arrived", async () => {
|
|
211
|
+
const dispatch = vi.fn();
|
|
212
|
+
const container = await mount(
|
|
213
|
+
view({ value: "2025-12-15", $action: { type: "save" } }, dispatch),
|
|
214
|
+
);
|
|
215
|
+
const input = container.querySelector("input")!;
|
|
216
|
+
await React.act(async () => {
|
|
217
|
+
input.dispatchEvent(
|
|
218
|
+
new KeyboardEvent("keydown", { key: "1", bubbles: true }),
|
|
219
|
+
);
|
|
220
|
+
});
|
|
221
|
+
await change(input, "2025-12-11");
|
|
222
|
+
expect(dispatch).not.toHaveBeenCalled();
|
|
223
|
+
await change(input, "2025-12-24");
|
|
224
|
+
expect(dispatch).toHaveBeenCalledExactlyOnceWith({
|
|
225
|
+
type: "save",
|
|
226
|
+
$input: "2025-12-24",
|
|
227
|
+
});
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
it("lets a pick replace a typed value that was never committed", async () => {
|
|
231
|
+
const dispatch = vi.fn();
|
|
232
|
+
const container = await mount(
|
|
233
|
+
view({ value: "2025-12-15", $action: { type: "save" } }, dispatch),
|
|
234
|
+
);
|
|
235
|
+
const input = container.querySelector("input")!;
|
|
236
|
+
await type(input, "2025-12-16");
|
|
237
|
+
expect(dispatch).not.toHaveBeenCalled();
|
|
238
|
+
await change(input, "2025-12-22");
|
|
239
|
+
expect(dispatch).toHaveBeenCalledExactlyOnceWith({
|
|
240
|
+
type: "save",
|
|
241
|
+
$input: "2025-12-22",
|
|
242
|
+
});
|
|
243
|
+
await blur(input);
|
|
244
|
+
expect(dispatch).toHaveBeenCalledTimes(1);
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
it("commits nothing when the user leaves without editing", async () => {
|
|
248
|
+
const dispatch = vi.fn();
|
|
249
|
+
const container = await mount(
|
|
250
|
+
<div data-aui="root">
|
|
251
|
+
{view({ $action: { type: "save" } }, dispatch)}
|
|
252
|
+
{view({ inputType: "time", $action: { type: "save" } }, dispatch)}
|
|
253
|
+
{view(
|
|
254
|
+
{
|
|
255
|
+
inputType: "datetime",
|
|
256
|
+
value: "2025-12-15T17:00:00.000Z",
|
|
257
|
+
$action: { type: "save" },
|
|
258
|
+
},
|
|
259
|
+
dispatch,
|
|
260
|
+
)}
|
|
261
|
+
</div>,
|
|
262
|
+
);
|
|
263
|
+
for (const input of container.querySelectorAll<HTMLInputElement>(
|
|
264
|
+
'[data-aui="datepicker"]',
|
|
265
|
+
)) {
|
|
266
|
+
await blur(input);
|
|
267
|
+
await React.act(async () => {
|
|
268
|
+
input.dispatchEvent(
|
|
269
|
+
new KeyboardEvent("keydown", { key: "Enter", bubbles: true }),
|
|
270
|
+
);
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
expect(dispatch).not.toHaveBeenCalled();
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
it("commits on Enter outside a form unless composing", async () => {
|
|
277
|
+
const dispatch = vi.fn();
|
|
278
|
+
const container = await mount(
|
|
279
|
+
view({ value: "2025-12-15", $action: { type: "save" } }, dispatch),
|
|
280
|
+
);
|
|
281
|
+
const input = container.querySelector("input")!;
|
|
282
|
+
await type(input, "2025-12-16");
|
|
283
|
+
await React.act(async () => {
|
|
284
|
+
input.dispatchEvent(
|
|
285
|
+
new KeyboardEvent("keydown", {
|
|
286
|
+
key: "Enter",
|
|
287
|
+
isComposing: true,
|
|
288
|
+
bubbles: true,
|
|
289
|
+
}),
|
|
290
|
+
);
|
|
291
|
+
});
|
|
292
|
+
expect(dispatch).not.toHaveBeenCalled();
|
|
293
|
+
await React.act(async () => {
|
|
294
|
+
input.dispatchEvent(
|
|
295
|
+
new KeyboardEvent("keydown", { key: "Enter", bubbles: true }),
|
|
296
|
+
);
|
|
297
|
+
});
|
|
298
|
+
expect(dispatch).toHaveBeenCalledExactlyOnceWith({
|
|
299
|
+
type: "save",
|
|
300
|
+
$input: "2025-12-16",
|
|
301
|
+
});
|
|
302
|
+
await blur(input);
|
|
303
|
+
expect(dispatch).toHaveBeenCalledTimes(1);
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
it("resets the committed baseline when the initial value changes", async () => {
|
|
307
|
+
const dispatch = vi.fn();
|
|
308
|
+
const container = await mount(
|
|
309
|
+
view({ value: "2025-12-15", $action: { type: "save" } }, dispatch),
|
|
310
|
+
);
|
|
311
|
+
let input = container.querySelector("input")!;
|
|
312
|
+
await blur(input);
|
|
313
|
+
expect(dispatch).not.toHaveBeenCalled();
|
|
314
|
+
await type(input, "2025-12-16");
|
|
315
|
+
await blur(input);
|
|
316
|
+
expect(dispatch).toHaveBeenCalledTimes(1);
|
|
317
|
+
await React.act(async () =>
|
|
318
|
+
root!.render(
|
|
319
|
+
view({ value: "2025-12-17", $action: { type: "save" } }, dispatch),
|
|
320
|
+
),
|
|
321
|
+
);
|
|
322
|
+
input = container.querySelector("input")!;
|
|
323
|
+
await blur(input);
|
|
324
|
+
expect(dispatch).toHaveBeenCalledTimes(1);
|
|
325
|
+
await type(input, "2025-12-16");
|
|
326
|
+
await blur(input);
|
|
327
|
+
expect(dispatch).toHaveBeenLastCalledWith({
|
|
328
|
+
type: "save",
|
|
329
|
+
$input: "2025-12-16",
|
|
330
|
+
});
|
|
331
|
+
expect(dispatch).toHaveBeenCalledTimes(2);
|
|
332
|
+
});
|
|
333
|
+
|
|
69
334
|
it.each([
|
|
70
335
|
["2026-07-15T12:34Z", "2026-07-16T13:45Z", ""],
|
|
71
336
|
["2026-07-15T12:34:56+08:00", "2026-07-16T21:45:00+08:00", "1"],
|
|
@@ -88,10 +353,12 @@ describe("DatePicker temporal contract", () => {
|
|
|
88
353
|
await change(input, "2026-07-16T08:30");
|
|
89
354
|
expect(input.step).toBe(step);
|
|
90
355
|
await change(input, "");
|
|
356
|
+
await blur(input);
|
|
91
357
|
expect(dispatch).toHaveBeenLastCalledWith({ type: "save", $input: "" });
|
|
92
358
|
expect(collectFormValues([input])).toEqual({ when: "" });
|
|
93
359
|
expect(input.step).toBe(step);
|
|
94
360
|
await change(input, "2026-07-16T09:45");
|
|
361
|
+
await blur(input);
|
|
95
362
|
expect(dispatch).toHaveBeenLastCalledWith({
|
|
96
363
|
type: "save",
|
|
97
364
|
$input: emitted,
|
|
@@ -115,6 +382,7 @@ describe("DatePicker temporal contract", () => {
|
|
|
115
382
|
);
|
|
116
383
|
const input = container.querySelector("input")!;
|
|
117
384
|
await change(input, "2025-12-15T12:00:00.750");
|
|
385
|
+
await blur(input);
|
|
118
386
|
expect(dispatch).toHaveBeenLastCalledWith({
|
|
119
387
|
type: "save",
|
|
120
388
|
$input: "2025-12-15T17:00:00.750Z",
|
|
@@ -168,6 +436,7 @@ describe("DatePicker temporal contract", () => {
|
|
|
168
436
|
expect(collectFormValues([input])).toEqual({ when: value });
|
|
169
437
|
}
|
|
170
438
|
await change(input, edited);
|
|
439
|
+
await blur(input);
|
|
171
440
|
expect(dispatch).toHaveBeenLastCalledWith({
|
|
172
441
|
type: "save",
|
|
173
442
|
$input: emitted,
|
|
@@ -190,6 +459,7 @@ describe("DatePicker temporal contract", () => {
|
|
|
190
459
|
);
|
|
191
460
|
const input = container.querySelector("input")!;
|
|
192
461
|
await change(input, "2026-07-16T08:34:56.789");
|
|
462
|
+
await blur(input);
|
|
193
463
|
expect(dispatch).toHaveBeenLastCalledWith({
|
|
194
464
|
type: "save",
|
|
195
465
|
$input: "2026-07-16T12:34:56.789000Z",
|
|
@@ -279,8 +549,10 @@ describe("DatePicker temporal contract", () => {
|
|
|
279
549
|
const container = await mount(<Surface />);
|
|
280
550
|
const input = container.querySelector("input")!;
|
|
281
551
|
expect(input.getAttribute("value")).toBe(shown);
|
|
282
|
-
await
|
|
552
|
+
await type(input, edited);
|
|
283
553
|
expect(update).toHaveBeenLastCalledWith("when", emitted);
|
|
554
|
+
expect(dispatch).not.toHaveBeenCalled();
|
|
555
|
+
await blur(input);
|
|
284
556
|
expect(dispatch).toHaveBeenLastCalledWith({
|
|
285
557
|
type: "save",
|
|
286
558
|
$input: emitted,
|
|
@@ -370,19 +642,23 @@ describe("DatePicker temporal contract", () => {
|
|
|
370
642
|
};
|
|
371
643
|
const container = await mount(<Surface />);
|
|
372
644
|
const input = container.querySelector("input")!;
|
|
373
|
-
await
|
|
374
|
-
await
|
|
645
|
+
await type(input, "2026-07-16T08:30");
|
|
646
|
+
await type(input, "");
|
|
375
647
|
expect(input.step).toBe("");
|
|
376
|
-
await
|
|
648
|
+
await type(input, "2026-07-16T09:45");
|
|
649
|
+
await blur(input);
|
|
377
650
|
expect(dispatch).toHaveBeenLastCalledWith({
|
|
378
651
|
type: "save",
|
|
379
652
|
$input: "2026-07-16T13:45-00:00",
|
|
380
653
|
});
|
|
381
654
|
expect(input.step).toBe("");
|
|
382
655
|
await React.act(async () => replace!("2026-07-17T12:34:56+08:00"));
|
|
383
|
-
await
|
|
656
|
+
await blur(input);
|
|
657
|
+
expect(dispatch).toHaveBeenCalledTimes(1);
|
|
658
|
+
await type(input, "");
|
|
384
659
|
expect(input.step).toBe("1");
|
|
385
|
-
await
|
|
660
|
+
await type(input, "2026-07-18T09:45:30");
|
|
661
|
+
await blur(input);
|
|
386
662
|
expect(dispatch).toHaveBeenLastCalledWith({
|
|
387
663
|
type: "save",
|
|
388
664
|
$input: "2026-07-18T21:45:30+08:00",
|
|
@@ -516,6 +792,7 @@ describe("DatePicker temporal contract", () => {
|
|
|
516
792
|
$input: { when: initial },
|
|
517
793
|
});
|
|
518
794
|
await change(input, "2025-12-16T09:30");
|
|
795
|
+
await blur(input);
|
|
519
796
|
expect(dispatch).toHaveBeenLastCalledWith({
|
|
520
797
|
type: "picker",
|
|
521
798
|
when: edited,
|
|
@@ -721,6 +998,27 @@ describe("DatePicker temporal contract", () => {
|
|
|
721
998
|
expect(new Date("2026-07-15T12:00Z").getTimezoneOffset()).toBe(240);
|
|
722
999
|
});
|
|
723
1000
|
|
|
1001
|
+
it("keeps year edits stable across historical second-level offsets", async () => {
|
|
1002
|
+
const container = await mount(
|
|
1003
|
+
view({ inputType: "datetime", value: "2025-12-15T17:00Z" }),
|
|
1004
|
+
);
|
|
1005
|
+
const input = container.querySelector<HTMLInputElement>("input")!;
|
|
1006
|
+
|
|
1007
|
+
for (const [year, canonical] of [
|
|
1008
|
+
["0002", "0002-12-15T16:56:02Z"],
|
|
1009
|
+
["0020", "0020-12-15T16:56:02Z"],
|
|
1010
|
+
["0202", "0202-12-15T16:56:02Z"],
|
|
1011
|
+
["2025", "2025-12-15T17:00Z"],
|
|
1012
|
+
]) {
|
|
1013
|
+
const local = `${year}-12-15T12:00`;
|
|
1014
|
+
await change(input, local);
|
|
1015
|
+
expect(input.value).toBe(local);
|
|
1016
|
+
expect(input.step).toBe("");
|
|
1017
|
+
expect(input.getAttribute(FIELD_VALUE_ATTR)).toBe(canonical);
|
|
1018
|
+
expect(collectFormValues([input])).toEqual({ when: canonical });
|
|
1019
|
+
}
|
|
1020
|
+
});
|
|
1021
|
+
|
|
724
1022
|
it("declares the three temporal input types and defaults to date", async () => {
|
|
725
1023
|
const schema = interactiveVocabulary.DatePicker.properties;
|
|
726
1024
|
for (const inputType of ["date", "datetime", "time"]) {
|
|
@@ -785,6 +1083,7 @@ describe("DatePicker temporal contract", () => {
|
|
|
785
1083
|
expect(input.name).toBe("when");
|
|
786
1084
|
expect(input.readOnly).toBe(false);
|
|
787
1085
|
await change(input, edited);
|
|
1086
|
+
await blur(input);
|
|
788
1087
|
expect(dispatch).toHaveBeenLastCalledWith({
|
|
789
1088
|
type: "save",
|
|
790
1089
|
$input: emitted,
|
|
@@ -847,6 +1146,7 @@ describe("DatePicker temporal contract", () => {
|
|
|
847
1146
|
expect(input.type).toBe("datetime-local");
|
|
848
1147
|
expect(input.value).toBe("");
|
|
849
1148
|
await change(input, edited!);
|
|
1149
|
+
await blur(input);
|
|
850
1150
|
expect(dispatch).toHaveBeenLastCalledWith({
|
|
851
1151
|
type: "save",
|
|
852
1152
|
$input: emitted,
|
|
@@ -863,13 +1163,16 @@ describe("DatePicker temporal contract", () => {
|
|
|
863
1163
|
const input = container.querySelector("input")!;
|
|
864
1164
|
await change(input, "2026-01-15T12:34");
|
|
865
1165
|
await change(input, "2026-07-15T12:34");
|
|
1166
|
+
await blur(input);
|
|
866
1167
|
expect(dispatch).toHaveBeenLastCalledWith({
|
|
867
1168
|
type: "save",
|
|
868
1169
|
$input: "2026-07-15T12:34:00-04:00",
|
|
869
1170
|
});
|
|
870
1171
|
await change(input, "");
|
|
1172
|
+
await blur(input);
|
|
871
1173
|
expect(dispatch).toHaveBeenLastCalledWith({ type: "save", $input: "" });
|
|
872
1174
|
await change(input, "2026-07-16T12:34");
|
|
1175
|
+
await blur(input);
|
|
873
1176
|
expect(dispatch).toHaveBeenLastCalledWith({
|
|
874
1177
|
type: "save",
|
|
875
1178
|
$input: "2026-07-16T12:34:00-04:00",
|
|
@@ -949,6 +1252,7 @@ describe("DatePicker temporal contract", () => {
|
|
|
949
1252
|
expect(input.max).toBe(max);
|
|
950
1253
|
expect(input.step).toBe(step);
|
|
951
1254
|
await change(input, edited);
|
|
1255
|
+
await blur(input);
|
|
952
1256
|
expect(dispatch).toHaveBeenLastCalledWith({
|
|
953
1257
|
type: "save",
|
|
954
1258
|
$input: normalizedNativeValue(input.type, edited),
|
|
@@ -1025,6 +1329,7 @@ describe("DatePicker temporal contract", () => {
|
|
|
1025
1329
|
);
|
|
1026
1330
|
await change(input, edited);
|
|
1027
1331
|
expect(update).toHaveBeenLastCalledWith("when", emitted);
|
|
1332
|
+
await blur(input);
|
|
1028
1333
|
expect(dispatch).toHaveBeenLastCalledWith({
|
|
1029
1334
|
type: "save",
|
|
1030
1335
|
$input: emitted,
|
|
@@ -1038,6 +1343,7 @@ describe("DatePicker temporal contract", () => {
|
|
|
1038
1343
|
expect(input.value).toBe(nativeValue(input.type, edited));
|
|
1039
1344
|
await change(input, "");
|
|
1040
1345
|
expect(update).toHaveBeenLastCalledWith("when", "");
|
|
1346
|
+
await blur(input);
|
|
1041
1347
|
expect(dispatch).toHaveBeenLastCalledWith({ type: "save", $input: "" });
|
|
1042
1348
|
},
|
|
1043
1349
|
);
|
|
@@ -1061,6 +1367,7 @@ describe("DatePicker temporal contract", () => {
|
|
|
1061
1367
|
nativeValue("datetime-local", "2026-07-15T14:04:56"),
|
|
1062
1368
|
);
|
|
1063
1369
|
await change(input, "2026-07-16T09:45:30");
|
|
1370
|
+
await blur(input);
|
|
1064
1371
|
expect(dispatch).toHaveBeenLastCalledWith({
|
|
1065
1372
|
type: "save",
|
|
1066
1373
|
$input: "2026-07-16T08:15:30-05:30",
|
|
@@ -1084,6 +1391,7 @@ describe("DatePicker temporal contract", () => {
|
|
|
1084
1391
|
nativeValue("datetime-local", "2026-07-17T00:34:56"),
|
|
1085
1392
|
);
|
|
1086
1393
|
await change(input, "2026-07-18T09:45:30");
|
|
1394
|
+
await blur(input);
|
|
1087
1395
|
expect(dispatch).toHaveBeenLastCalledWith({
|
|
1088
1396
|
type: "save",
|
|
1089
1397
|
$input: "2026-07-18T21:45:30+08:00",
|
|
@@ -1119,6 +1427,7 @@ describe("DatePicker temporal contract", () => {
|
|
|
1119
1427
|
nativeValue("datetime-local", "2026-07-15T00:34:56.123"),
|
|
1120
1428
|
);
|
|
1121
1429
|
await change(input, "2026-07-16T09:45:30");
|
|
1430
|
+
await blur(input);
|
|
1122
1431
|
expect(dispatch).toHaveBeenLastCalledWith({
|
|
1123
1432
|
type: "save",
|
|
1124
1433
|
$input: "2026-07-16T21:45:30.000+08:00",
|
|
@@ -411,15 +411,17 @@ function DatePickerRender({
|
|
|
411
411
|
initialValue,
|
|
412
412
|
value: initialValue,
|
|
413
413
|
anchor: initialValue,
|
|
414
|
+
committedValue: initialValue,
|
|
415
|
+
edited: false,
|
|
414
416
|
});
|
|
415
417
|
if (selection.initialValue !== initialValue) {
|
|
418
|
+
const isEcho = isBound && initialValue === selection.value;
|
|
416
419
|
setSelection({
|
|
417
420
|
initialValue,
|
|
418
421
|
value: initialValue,
|
|
419
|
-
anchor:
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
: initialValue,
|
|
422
|
+
anchor: isEcho ? selection.anchor : initialValue,
|
|
423
|
+
committedValue: isEcho ? selection.committedValue : initialValue,
|
|
424
|
+
edited: isEcho && selection.edited,
|
|
423
425
|
});
|
|
424
426
|
}
|
|
425
427
|
const currentValue = isBound ? initialValue : selection.value;
|
|
@@ -457,6 +459,20 @@ function DatePickerRender({
|
|
|
457
459
|
: inputType === "datetime" && hydrated
|
|
458
460
|
? toPickerLocalDateTime(currentValue)
|
|
459
461
|
: normalizeTemporalInputValue(currentValue);
|
|
462
|
+
const keyboardActive = React.useRef(false);
|
|
463
|
+
const valueFromInput = (input: HTMLInputElement) => {
|
|
464
|
+
const inputValue = normalizeTemporalInputValue(input.value);
|
|
465
|
+
return inputType === "datetime"
|
|
466
|
+
? fromLocalDateTime(inputValue, selection.anchor)
|
|
467
|
+
: inputValue;
|
|
468
|
+
};
|
|
469
|
+
const commit = (input: HTMLInputElement) => {
|
|
470
|
+
if (!selection.edited) return;
|
|
471
|
+
const nextValue = valueFromInput(input);
|
|
472
|
+
setSelection({ ...selection, committedValue: nextValue, edited: false });
|
|
473
|
+
if (nextValue !== selection.committedValue)
|
|
474
|
+
fire($action, $dispatch, nextValue, input);
|
|
475
|
+
};
|
|
460
476
|
return (
|
|
461
477
|
<input
|
|
462
478
|
key={isBound ? undefined : initialValue}
|
|
@@ -504,28 +520,73 @@ function DatePickerRender({
|
|
|
504
520
|
}
|
|
505
521
|
step={
|
|
506
522
|
inputType !== "date"
|
|
507
|
-
? getTemporalInputStep(
|
|
523
|
+
? getTemporalInputStep(
|
|
524
|
+
selection.anchor,
|
|
525
|
+
hasInstantAnchor ? displayValue : currentValue,
|
|
526
|
+
min,
|
|
527
|
+
max,
|
|
528
|
+
)
|
|
508
529
|
: undefined
|
|
509
530
|
}
|
|
510
531
|
onChange={(e) => {
|
|
511
|
-
const
|
|
512
|
-
const nextValue =
|
|
513
|
-
inputType === "datetime"
|
|
514
|
-
? fromLocalDateTime(inputValue, selection.anchor)
|
|
515
|
-
: inputValue;
|
|
532
|
+
const nextValue = valueFromInput(e.currentTarget);
|
|
516
533
|
const canonical = canonicalFieldValue(nextValue);
|
|
517
534
|
if (canonical !== undefined) {
|
|
518
535
|
e.currentTarget.setAttribute(FIELD_VALUE_ATTR, canonical);
|
|
519
536
|
} else {
|
|
520
537
|
e.currentTarget.removeAttribute(FIELD_VALUE_ATTR);
|
|
521
538
|
}
|
|
539
|
+
// A change without an editing key down is a pick from the native picker, which commits at once; a typed value waits for blur or Enter.
|
|
540
|
+
const typed = keyboardActive.current;
|
|
541
|
+
keyboardActive.current = false;
|
|
522
542
|
setSelection({
|
|
523
543
|
initialValue,
|
|
524
544
|
value: nextValue,
|
|
525
545
|
anchor: selection.anchor,
|
|
546
|
+
committedValue: typed ? selection.committedValue : nextValue,
|
|
547
|
+
edited: typed,
|
|
526
548
|
});
|
|
527
549
|
updateBinding?.(nextValue);
|
|
528
|
-
|
|
550
|
+
if (!typed && nextValue !== selection.committedValue)
|
|
551
|
+
fire($action, $dispatch, nextValue, e.currentTarget);
|
|
552
|
+
}}
|
|
553
|
+
onBlur={(e) => {
|
|
554
|
+
keyboardActive.current = false;
|
|
555
|
+
commit(e.currentTarget);
|
|
556
|
+
}}
|
|
557
|
+
onKeyDown={(e) => {
|
|
558
|
+
if (
|
|
559
|
+
e.key === "Enter" &&
|
|
560
|
+
!e.nativeEvent.isComposing &&
|
|
561
|
+
!e.currentTarget.form
|
|
562
|
+
) {
|
|
563
|
+
commit(e.currentTarget);
|
|
564
|
+
return;
|
|
565
|
+
}
|
|
566
|
+
keyboardActive.current =
|
|
567
|
+
!e.altKey &&
|
|
568
|
+
!e.ctrlKey &&
|
|
569
|
+
!e.metaKey &&
|
|
570
|
+
(e.key.length === 1 ||
|
|
571
|
+
[
|
|
572
|
+
"ArrowDown",
|
|
573
|
+
"ArrowUp",
|
|
574
|
+
"Backspace",
|
|
575
|
+
"Delete",
|
|
576
|
+
"End",
|
|
577
|
+
"Home",
|
|
578
|
+
"PageDown",
|
|
579
|
+
"PageUp",
|
|
580
|
+
].includes(e.key));
|
|
581
|
+
}}
|
|
582
|
+
onKeyUp={() => {
|
|
583
|
+
keyboardActive.current = false;
|
|
584
|
+
}}
|
|
585
|
+
onPaste={() => {
|
|
586
|
+
keyboardActive.current = true;
|
|
587
|
+
}}
|
|
588
|
+
onCut={() => {
|
|
589
|
+
keyboardActive.current = true;
|
|
529
590
|
}}
|
|
530
591
|
/>
|
|
531
592
|
);
|
|
@@ -952,7 +1013,7 @@ export const interactiveVocabulary = {
|
|
|
952
1013
|
},
|
|
953
1014
|
DatePicker: {
|
|
954
1015
|
description:
|
|
955
|
-
"A date, datetime, or time input. Carries `$action
|
|
1016
|
+
"A date, datetime, or time input. Carries `$action`, which runs once per committed value: a pick from the picker, or a typed value on blur or Enter.",
|
|
956
1017
|
properties: z.object({
|
|
957
1018
|
inputType: z
|
|
958
1019
|
.enum(["date", "datetime", "time"])
|