Skip to content

Commit 4d3f2c1

Browse files
committed
fix form control output
1 parent 6e61e8c commit 4d3f2c1

66 files changed

Lines changed: 6043 additions & 592 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@node-projects/layout2vector",
3-
"version": "5.26.0",
3+
"version": "5.27.0",
44
"description": "DOM → Geometry → DXF/PDF/PNG library",
55
"repository": {
66
"type": "git",

src/extractors/form-controls.ts

Lines changed: 183 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -305,9 +305,19 @@ function extractSingleLineControlGeometry(
305305
const cs = getComputedStyle(el);
306306
const fontSize = getFontSize(node.extractedStyle, cs);
307307
const lineHeight = Math.min(getLineHeight(node.extractedStyle, cs, fontSize), localHeight);
308+
const temporalIconKind = getTemporalInputIconKind(el.type);
309+
const iconSlotWidth = temporalIconKind ? clamp(localHeight * 0.62, 14, 22) : 0;
310+
const iconGap = temporalIconKind ? clamp(localHeight * 0.12, 2, 4) : 0;
308311
const paddingX = Math.max(parsePx(cs.paddingLeft), align === "center" ? 8 : 6);
309312
const paddingY = Math.max(parsePx(cs.paddingTop), 4);
310-
const contentBox = insetRect(localWidth, localHeight, paddingX, paddingY, Math.max(parsePx(cs.paddingRight), paddingX), Math.max(parsePx(cs.paddingBottom), paddingY));
313+
const contentBox = insetRect(
314+
localWidth,
315+
localHeight,
316+
paddingX,
317+
paddingY,
318+
Math.max(parsePx(cs.paddingRight), paddingX) + iconSlotWidth + iconGap,
319+
Math.max(parsePx(cs.paddingBottom), paddingY)
320+
);
311321

312322
const nodes: IRNode[] = [];
313323
if (shouldRenderSyntheticControlBox(node.extractedStyle, cs)) {
@@ -320,6 +330,22 @@ function extractSingleLineControlGeometry(
320330
});
321331
}
322332

333+
const iconStyleColor = isVisibleColor(node.extractedStyle.color)
334+
? (node.extractedStyle.color as string)
335+
: DEFAULT_ICON_COLOR;
336+
if (temporalIconKind) {
337+
const iconNodes = createTemporalInputIconNodes(
338+
quad,
339+
localWidth,
340+
localHeight,
341+
temporalIconKind,
342+
iconStyleColor,
343+
node.extractedStyle.opacity,
344+
globalIndex + 2
345+
);
346+
nodes.push(...iconNodes);
347+
}
348+
323349
const text = displayText.text.trim();
324350
if (!text) return nodes;
325351

@@ -329,6 +355,149 @@ function extractSingleLineControlGeometry(
329355
return nodes;
330356
}
331357

358+
function getTemporalInputIconKind(type: string): "calendar" | "clock" | null {
359+
const normalizedType = type.toLowerCase();
360+
if (normalizedType === "time") return "clock";
361+
if (normalizedType === "date"
362+
|| normalizedType === "datetime-local"
363+
|| normalizedType === "month"
364+
|| normalizedType === "week") {
365+
return "calendar";
366+
}
367+
return null;
368+
}
369+
370+
function createTemporalInputIconNodes(
371+
quad: Quad,
372+
localWidth: number,
373+
localHeight: number,
374+
kind: "calendar" | "clock",
375+
color: string,
376+
opacity: number | undefined,
377+
zIndex: number
378+
): IRNode[] {
379+
const size = clamp(localHeight * 0.42, 9, 14);
380+
const rightInset = clamp(localHeight * 0.32, 10, 14);
381+
const left = Math.max(0, localWidth - size - rightInset);
382+
const top = Math.max(0, (localHeight - size) / 2);
383+
const right = left + size;
384+
const bottom = top + size;
385+
const strokeWidth = `${clamp(size * 0.08, 1, 1.4)}px`;
386+
387+
if (kind === "calendar") {
388+
const binderY = top + size * 0.3;
389+
const tabOffset = size * 0.22;
390+
const tabWidth = size * 0.14;
391+
return [
392+
{
393+
type: "polyline",
394+
points: [
395+
mapLocalPoint(quad, left, top, localWidth, localHeight),
396+
mapLocalPoint(quad, right, top, localWidth, localHeight),
397+
mapLocalPoint(quad, right, bottom, localWidth, localHeight),
398+
mapLocalPoint(quad, left, bottom, localWidth, localHeight),
399+
mapLocalPoint(quad, left, top, localWidth, localHeight),
400+
],
401+
closed: false,
402+
style: {
403+
stroke: color,
404+
strokeWidth,
405+
opacity,
406+
},
407+
zIndex,
408+
},
409+
{
410+
type: "polyline",
411+
points: [
412+
mapLocalPoint(quad, left, binderY, localWidth, localHeight),
413+
mapLocalPoint(quad, right, binderY, localWidth, localHeight),
414+
],
415+
closed: false,
416+
style: {
417+
stroke: color,
418+
strokeWidth,
419+
opacity,
420+
},
421+
zIndex,
422+
},
423+
{
424+
type: "polyline",
425+
points: [
426+
mapLocalPoint(quad, left + tabOffset, top - size * 0.05, localWidth, localHeight),
427+
mapLocalPoint(quad, left + tabOffset, top + tabWidth, localWidth, localHeight),
428+
],
429+
closed: false,
430+
style: {
431+
stroke: color,
432+
strokeWidth,
433+
opacity,
434+
},
435+
zIndex,
436+
},
437+
{
438+
type: "polyline",
439+
points: [
440+
mapLocalPoint(quad, right - tabOffset, top - size * 0.05, localWidth, localHeight),
441+
mapLocalPoint(quad, right - tabOffset, top + tabWidth, localWidth, localHeight),
442+
],
443+
closed: false,
444+
style: {
445+
stroke: color,
446+
strokeWidth,
447+
opacity,
448+
},
449+
zIndex,
450+
},
451+
];
452+
}
453+
454+
const centerX = left + size / 2;
455+
const centerY = top + size / 2;
456+
const radius = size * 0.46;
457+
return [
458+
{
459+
type: "polyline",
460+
points: approximateEllipsePoints(quad, centerX, centerY, radius, radius, localWidth, localHeight, 20),
461+
closed: true,
462+
style: {
463+
stroke: color,
464+
strokeWidth,
465+
fill: undefined,
466+
opacity,
467+
},
468+
zIndex,
469+
},
470+
{
471+
type: "polyline",
472+
points: [
473+
mapLocalPoint(quad, centerX, centerY, localWidth, localHeight),
474+
mapLocalPoint(quad, centerX, centerY - radius * 0.52, localWidth, localHeight),
475+
],
476+
closed: false,
477+
style: {
478+
stroke: color,
479+
strokeWidth,
480+
opacity,
481+
},
482+
zIndex,
483+
},
484+
{
485+
type: "polyline",
486+
points: [
487+
mapLocalPoint(quad, centerX, centerY, localWidth, localHeight),
488+
mapLocalPoint(quad, centerX + radius * 0.4, centerY, localWidth, localHeight),
489+
],
490+
closed: false,
491+
style: {
492+
stroke: color,
493+
strokeWidth,
494+
opacity,
495+
},
496+
zIndex,
497+
},
498+
];
499+
}
500+
332501
function extractTextAreaGeometry(
333502
textarea: HTMLTextAreaElement,
334503
node: StackingNode,
@@ -1107,6 +1276,7 @@ function formatTemporalInputDisplayValue(
11071276
type: string,
11081277
value: string
11091278
): string | null {
1279+
void input;
11101280
try {
11111281
switch (type) {
11121282
case "date": {
@@ -1147,6 +1317,9 @@ function formatTemporalInputDisplayValue(
11471317
case "week": {
11481318
const parsed = parseWeekValue(value);
11491319
if (!parsed) return null;
1320+
if (/^de(?:-|$)/i.test(parsed.locale)) {
1321+
return `Woche ${parsed.isoWeek}, ${parsed.isoYear}`;
1322+
}
11501323
const rangeFormatter = new Intl.DateTimeFormat(undefined, {
11511324
month: "short",
11521325
day: "numeric",
@@ -1240,7 +1413,13 @@ function parseMonthValue(value: string): Date | null {
12401413
return new Date(year, month - 1, 1, 12, 0, 0, 0);
12411414
}
12421415

1243-
function parseWeekValue(value: string): { start: Date; end: Date } | null {
1416+
function parseWeekValue(value: string): {
1417+
start: Date;
1418+
end: Date;
1419+
isoYear: number;
1420+
isoWeek: number;
1421+
locale: string;
1422+
} | null {
12441423
const match = value.match(/^(\d{4})-W(\d{2})$/);
12451424
if (!match) return null;
12461425

@@ -1257,8 +1436,9 @@ function parseWeekValue(value: string): { start: Date; end: Date } | null {
12571436
start.setDate(firstWeekMonday.getDate() + (isoWeek - 1) * 7);
12581437
const end = new Date(start);
12591438
end.setDate(start.getDate() + 6);
1439+
const locale = new Intl.DateTimeFormat(undefined).resolvedOptions().locale;
12601440

1261-
return { start, end };
1441+
return { start, end, isoYear, isoWeek, locale };
12621442
}
12631443

12641444
function defaultButtonLabel(type: string): string {

0 commit comments

Comments
 (0)