@hjmds/design-contracts 1.48.0 → 1.50.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/card.d.ts +5 -0
- package/dist/card.d.ts.map +1 -1
- package/dist/card.js +3 -0
- package/dist/card.js.map +1 -1
- package/dist/catalog.d.ts +34 -0
- package/dist/catalog.d.ts.map +1 -1
- package/dist/catalog.js +4 -0
- package/dist/catalog.js.map +1 -1
- package/dist/drawing-editor.d.ts +96 -0
- package/dist/drawing-editor.d.ts.map +1 -0
- package/dist/drawing-editor.js +43 -0
- package/dist/drawing-editor.js.map +1 -0
- package/dist/timeline.d.ts +7 -0
- package/dist/timeline.d.ts.map +1 -1
- package/dist/timeline.js +7 -0
- package/dist/timeline.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/version.js.map +1 -1
- package/docs/drawing-editor.md +13 -0
- package/docs/generated/component-maturity.md +5 -4
- package/docs/generated/renderer-evidence.json +109 -9
- package/docs/generated/renderer-evidence.md +5 -3
- package/docs/generated/showcase-manifest.json +52 -1
- package/docs/optional-adapters.md +1 -1
- package/docs/stable-core.md +12 -0
- package/docs/timeline.md +13 -0
- package/docs/usage/README.md +6 -5
- package/docs/usage/components/card.md +1 -0
- package/docs/usage/components/context-menu.md +2 -2
- package/docs/usage/components/drawing-editor.md +135 -0
- package/docs/usage/components/image-choice-group.md +1 -1
- package/docs/usage/components/keyboard-dock.md +2 -2
- package/docs/usage/components/keyboard-form-scroll-view.md +2 -2
- package/docs/usage/components/keyboard-motion-provider.md +2 -2
- package/docs/usage/components/share-sheet.md +5 -3
- package/docs/usage/components/sheet.md +13 -1
- package/docs/usage/components/timeline.md +27 -3
- package/docs/usage/compositions/disclosure.md +3 -3
- package/package.json +6 -1
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
export const DRAWING_INK_COLORS = ['#333d4b', '#ffffff', '#e03131', '#f08c00', '#ffd43b', '#087f5b', '#3f66a5', '#7048e8', '#9c4668', '#f783ac', '#8d6e63', '#868e96'];
|
|
2
|
+
/** Default background. The person can change it; the chosen background is part of the saved photo. */
|
|
3
|
+
export const DRAWING_PAPER = '#fffaf2';
|
|
4
|
+
/** Background presets: the default paper, light tints that keep ink readable, and one dark ground for light ink. */
|
|
5
|
+
export const DRAWING_BACKGROUND_COLORS = ['#fffaf2', '#ffffff', '#f1f3f5', '#ffe3e3', '#ffdeeb', '#fff3bf', '#d3f9d8', '#c5f6fa', '#d0ebff', '#e5dbff', '#333d4b'];
|
|
6
|
+
export function drawingStrokeColor(stroke, background) {
|
|
7
|
+
return stroke.erase ? background : stroke.color;
|
|
8
|
+
}
|
|
9
|
+
/** Something worth saving: an ink stroke, or a background other than the default paper. */
|
|
10
|
+
export function drawingHasContent(strokes, background) {
|
|
11
|
+
return background !== DRAWING_PAPER || strokes.some(stroke => !stroke.erase);
|
|
12
|
+
}
|
|
13
|
+
export function drawingInkPath(stroke) {
|
|
14
|
+
return stroke.points.map(([x, y], i) => `${i ? 'L' : 'M'}${x.toFixed(1)},${y.toFixed(1)}`).join(' ') + (stroke.points.length === 1 ? ' l0.1,0.1' : '');
|
|
15
|
+
}
|
|
16
|
+
export const emptyDrawing = { past: [], present: [], future: [] };
|
|
17
|
+
/** A stroke, including an eraser, is one undo step. Clearing can also be undone. */
|
|
18
|
+
export function drawingReducer(state, action) {
|
|
19
|
+
if (action.type === 'undo') {
|
|
20
|
+
const previous = state.past.at(-1);
|
|
21
|
+
return previous ? { past: state.past.slice(0, -1), present: previous, future: [state.present, ...state.future] } : state;
|
|
22
|
+
}
|
|
23
|
+
if (action.type === 'redo') {
|
|
24
|
+
const next = state.future[0];
|
|
25
|
+
return next ? { past: [...state.past, state.present], present: next, future: state.future.slice(1) } : state;
|
|
26
|
+
}
|
|
27
|
+
if (action.type === 'clear' && !state.present.length)
|
|
28
|
+
return state;
|
|
29
|
+
return { past: [...state.past, state.present], present: action.type === 'add' ? [...state.present, action.stroke] : [], future: [] };
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* One-row colour strip for the drawing toolbar: colours the person picked from the full picker this session come
|
|
33
|
+
* first (newest first, at most four), then the presets. Recent colours are not stored; the drawing itself is.
|
|
34
|
+
*/
|
|
35
|
+
export function drawingQuickColors(recent) {
|
|
36
|
+
return [...new Set([...recent.slice(0, 4), ...DRAWING_INK_COLORS])];
|
|
37
|
+
}
|
|
38
|
+
/** Shared editor geometry; the 400px cap leaves tools visible on phone-sized hosts. */
|
|
39
|
+
export const drawingEditorRecipe = {
|
|
40
|
+
maxCanvas: 400, minCanvas: 160, gap: 12, radius: 16, sizePreviewMs: 700,
|
|
41
|
+
brush: { min: 2, max: 32, step: 2, initial: 12 },
|
|
42
|
+
};
|
|
43
|
+
//# sourceMappingURL=drawing-editor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"drawing-editor.js","sourceRoot":"","sources":["../src/drawing-editor.ts"],"names":[],"mappings":"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","sourcesContent":["export const DRAWING_INK_COLORS = ['#333d4b', '#ffffff', '#e03131', '#f08c00', '#ffd43b', '#087f5b', '#3f66a5', '#7048e8', '#9c4668', '#f783ac', '#8d6e63', '#868e96'] as const;\n/** Default background. The person can change it; the chosen background is part of the saved photo. */\nexport const DRAWING_PAPER = '#fffaf2';\n/** Background presets: the default paper, light tints that keep ink readable, and one dark ground for light ink. */\nexport const DRAWING_BACKGROUND_COLORS = ['#fffaf2', '#ffffff', '#f1f3f5', '#ffe3e3', '#ffdeeb', '#fff3bf', '#d3f9d8', '#c5f6fa', '#d0ebff', '#e5dbff', '#333d4b'] as const;\n/**\n * An eraser stroke has no colour of its own: it paints whatever the background is when drawn, so changing the\n * background after erasing never leaves old-paper stripes (2026-10-25, background colour added).\n */\nexport type DrawingStroke = Readonly<{ width: number; points: readonly [number, number][] } & ({ color: string; erase?: never } | { erase: true; color?: never })>;\nexport function drawingStrokeColor(stroke: DrawingStroke, background: string): string {\n return stroke.erase ? background : stroke.color;\n}\n/** Something worth saving: an ink stroke, or a background other than the default paper. */\nexport function drawingHasContent(strokes: readonly DrawingStroke[], background: string): boolean {\n return background !== DRAWING_PAPER || strokes.some(stroke => !stroke.erase);\n}\nexport function drawingInkPath(stroke: DrawingStroke): string {\n return stroke.points.map(([x, y], i) => `${i ? 'L' : 'M'}${x.toFixed(1)},${y.toFixed(1)}`).join(' ') + (stroke.points.length === 1 ? ' l0.1,0.1' : '');\n}\n\nexport type DrawingHistory = Readonly<{\n past: readonly (readonly DrawingStroke[])[];\n present: readonly DrawingStroke[];\n future: readonly (readonly DrawingStroke[])[];\n}>;\nexport type DrawingAction = Readonly<{ type: 'add'; stroke: DrawingStroke }> | Readonly<{ type: 'undo' | 'redo' | 'clear' }>;\nexport const emptyDrawing: DrawingHistory = { past: [], present: [], future: [] };\n/** A stroke, including an eraser, is one undo step. Clearing can also be undone. */\nexport function drawingReducer(state: DrawingHistory, action: DrawingAction): DrawingHistory {\n if (action.type === 'undo') {\n const previous = state.past.at(-1);\n return previous ? { past: state.past.slice(0, -1), present: previous, future: [state.present, ...state.future] } : state;\n }\n if (action.type === 'redo') {\n const next = state.future[0];\n return next ? { past: [...state.past, state.present], present: next, future: state.future.slice(1) } : state;\n }\n if (action.type === 'clear' && !state.present.length) return state;\n return { past: [...state.past, state.present], present: action.type === 'add' ? [...state.present, action.stroke] : [], future: [] };\n}\n\n/**\n * One-row colour strip for the drawing toolbar: colours the person picked from the full picker this session come\n * first (newest first, at most four), then the presets. Recent colours are not stored; the drawing itself is.\n */\nexport function drawingQuickColors(recent: readonly string[]): readonly string[] {\n return [...new Set([...recent.slice(0, 4), ...DRAWING_INK_COLORS])];\n}\n\n/** Shared editor geometry; the 400px cap leaves tools visible on phone-sized hosts. */\nexport const drawingEditorRecipe = {\n maxCanvas: 400, minCanvas: 160, gap: 12, radius: 16, sizePreviewMs: 700,\n brush: { min: 2, max: 32, step: 2, initial: 12 },\n} as const;\nexport type DrawingImage = Readonly<{ base64: string; mimeType: 'image/png' | 'image/jpeg' }>;\nexport type DrawingEditorLabels = Readonly<{\n title: string; save: string; close: string; cancel: string; failed: string;\n pen: string; eraser: string; undo: string; redo: string; clear: string; width: string;\n moreColors: string; penColor: string; background: string; backgroundPresets: string;\n hex: string; hue: string; saturation: string; brightness: string; opacity: string; invalidColor: string;\n discardTitle: string; discardBody: string; discardConfirm: string;\n}>;\n/** Upload, account identity and navigation stay in the product. Reject on failure to retain the drawing. */\nexport type DrawingEditorProps = Readonly<{\n labels: DrawingEditorLabels;\n onSave(image: DrawingImage): Promise<void>;\n onCancel(): void;\n cropPreview?: 'circle' | 'none';\n safeAreaBottom?: number;\n testID?: string;\n}>;\n"]}
|
package/dist/timeline.d.ts
CHANGED
|
@@ -33,6 +33,7 @@ export type TimelineDescriptor<Id extends string = string> = Readonly<{
|
|
|
33
33
|
}>;
|
|
34
34
|
export declare const timelineDefaults: {
|
|
35
35
|
readonly itemTone: "neutral";
|
|
36
|
+
readonly variant: "journal";
|
|
36
37
|
};
|
|
37
38
|
export type TimelineAccessibleNameInfo = Readonly<{
|
|
38
39
|
position: number;
|
|
@@ -67,6 +68,7 @@ export declare function validateTimelineDescriptor<Id extends string>(descriptor
|
|
|
67
68
|
*/
|
|
68
69
|
export declare function validateTimelineSelection(selectedId: unknown, onSelectedIdChange: unknown, options?: Readonly<{
|
|
69
70
|
hasActions: boolean;
|
|
71
|
+
variant?: "record" | "journal";
|
|
70
72
|
}>): void;
|
|
71
73
|
/**
|
|
72
74
|
* Keyboard target for a selectable Timeline (vertical listbox): ArrowDown/ArrowUp step one record, Home/End jump to
|
|
@@ -84,6 +86,11 @@ export declare function resolveTimelineDescriptor<Id extends string>(descriptor:
|
|
|
84
86
|
export declare const timelineRecipe: {
|
|
85
87
|
readonly slots: readonly ["root", "item", "dot", "connector", "content", "timestamp", "label", "description"];
|
|
86
88
|
readonly gap: 20;
|
|
89
|
+
readonly journal: {
|
|
90
|
+
readonly gap: 0;
|
|
91
|
+
readonly paddingBlock: 16;
|
|
92
|
+
readonly dotDiameter: 8;
|
|
93
|
+
};
|
|
87
94
|
readonly item: {
|
|
88
95
|
readonly padding: 12;
|
|
89
96
|
readonly emphasisBackground: Readonly<{
|
package/dist/timeline.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"timeline.d.ts","sourceRoot":"","sources":["../src/timeline.ts"],"names":[],"mappings":"AAMA;;;;;;;;;GASG;AACH,MAAM,MAAM,gBAAgB,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,GAAG,WAAW,CAAC;AAE5E,MAAM,MAAM,sBAAsB,CAAC,EAAE,SAAS,MAAM,GAAG,MAAM,IAAI,QAAQ,CAAC;IACxE,EAAE,EAAE,EAAE,CAAC;IACP,KAAK,EAAE,MAAM,CAAC;IACd,0HAA0H;IAC1H,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,+FAA+F;IAC/F,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,gEAAgE;IAChE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,gBAAgB,CAAC;IACxB,iFAAiF;IACjF,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB,CAAC,CAAC;AAEH,MAAM,MAAM,kBAAkB,CAAC,EAAE,SAAS,MAAM,GAAG,MAAM,IAAI,QAAQ,CAAC;IACpE,KAAK,EAAE,SAAS,sBAAsB,CAAC,EAAE,CAAC,EAAE,CAAC;IAC7C;;;;OAIG;IACH,UAAU,CAAC,EAAE,EAAE,GAAG,IAAI,CAAC;CACxB,CAAC,CAAC;AAEH,eAAO,MAAM,gBAAgB
|
|
1
|
+
{"version":3,"file":"timeline.d.ts","sourceRoot":"","sources":["../src/timeline.ts"],"names":[],"mappings":"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"}
|
package/dist/timeline.js
CHANGED
|
@@ -3,6 +3,7 @@ import { semanticColors } from "./semantic-colors.js";
|
|
|
3
3
|
import { getCollectionNavigationIntent, getCollectionNavigationTarget } from "./collection.js";
|
|
4
4
|
export const timelineDefaults = {
|
|
5
5
|
itemTone: "neutral",
|
|
6
|
+
variant: "journal",
|
|
6
7
|
};
|
|
7
8
|
function assertNonEmpty(value, field) {
|
|
8
9
|
if (value.trim().length === 0) {
|
|
@@ -60,6 +61,9 @@ export function validateTimelineDescriptor(descriptor) {
|
|
|
60
61
|
* the passive Timeline.
|
|
61
62
|
*/
|
|
62
63
|
export function validateTimelineSelection(selectedId, onSelectedIdChange, options = { hasActions: false }) {
|
|
64
|
+
if (options.variant !== undefined && options.variant !== "record" && options.variant !== "journal") {
|
|
65
|
+
throw new TypeError("Unsupported Timeline variant");
|
|
66
|
+
}
|
|
63
67
|
if ((selectedId === undefined) !== (onSelectedIdChange === undefined)) {
|
|
64
68
|
throw new TypeError("Timeline selectedId and onSelectedIdChange must be supplied together");
|
|
65
69
|
}
|
|
@@ -125,6 +129,9 @@ export const timelineRecipe = {
|
|
|
125
129
|
"description",
|
|
126
130
|
],
|
|
127
131
|
gap: spacing.lg,
|
|
132
|
+
// 2026-10-27: the approved journey journal keeps the reading rail, without large selected cards.
|
|
133
|
+
// Passive histories keep every detail and command; selection adds disclosure without changing event semantics.
|
|
134
|
+
journal: { gap: 0, paddingBlock: spacing.md, dotDiameter: 8 },
|
|
128
135
|
// A record remains passive: emphasis highlights its history without implying progress.
|
|
129
136
|
item: {
|
|
130
137
|
padding: spacing.sm,
|
package/dist/timeline.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"timeline.js","sourceRoot":"","sources":["../src/timeline.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ColorReference } from \"./color-references.js\";\nimport type { FontWeightValue, TextVariant } from \"./foundations.js\";\nimport { fontWeight, spacing, stroke } from \"./foundations.js\";\nimport { semanticColors } from \"./semantic-colors.js\";\nimport { getCollectionNavigationIntent, getCollectionNavigationTarget } from \"./collection.js\";\n\n/**\n * Timeline is a record of what already happened, not a position in a flow —\n * that is Steps' job (see docs/steps.md and the boundary note in\n * docs/timeline.md). Because there is no cursor, Timeline never derives a\n * status from position: every item is simply \"happened\", and the connector\n * between items carries no reached/unreached meaning. Ant Design's\n * alternating left/right layout is intentionally not reproduced — it is a\n * Web-only decoration with no equivalent in a single-direction native list;\n * see docs/timeline.md for the rationale.\n */\nexport type TimelineItemTone = \"neutral\" | \"info\" | \"success\" | \"attention\";\n\nexport type TimelineItemDescriptor<Id extends string = string> = Readonly<{\n id: Id;\n label: string;\n /** Product-formatted \"when\" copy, e.g. \"3회 초\" or \"2024-01-15\". Timeline never formats a clock, inning, or date itself. */\n timestamp?: string;\n /** Machine-readable date for the formatted timestamp; products retain the original instant. */\n dateTime?: string;\n /** Product-formatted supplementary detail, e.g. \"안타로 1루 진출\". */\n description?: string;\n tone?: TimelineItemTone;\n /** Passive emphasis for a notable record, not a progress cursor or selection. */\n emphasized?: boolean;\n}>;\n\nexport type TimelineDescriptor<Id extends string = string> = Readonly<{\n items: readonly TimelineItemDescriptor<Id>[];\n /**\n * Single selection of one record (2026-10-23): `undefined` keeps the passive record, `null` makes the list selectable\n * with nothing chosen yet. Selection is not a progress cursor — no status is derived from it. Diairy's journey log\n * rebuilt this with a `Button` tone toggle + `aria-pressed` per step (SELECTION forbids button-as-selection).\n */\n selectedId?: Id | null;\n}>;\n\nexport const timelineDefaults = {\n itemTone: \"neutral\",\n} as const satisfies Readonly<{ itemTone: TimelineItemTone }>;\n\nexport type TimelineAccessibleNameInfo = Readonly<{\n position: number;\n total: number;\n label: string;\n}>;\n\n/**\n * Products own the exact phrase order and counter-word grammar, same reason\n * Steps does not assemble its own accessible name (see docs/steps.md).\n */\nexport type ComposeTimelineAccessibleName = (\n info: TimelineAccessibleNameInfo,\n) => string;\n\nexport type ResolvedTimelineItemDescriptor<Id extends string = string> = Omit<\n TimelineItemDescriptor<Id>,\n \"tone\"\n> &\n Readonly<{\n tone: TimelineItemTone;\n /** 1-indexed so position and label both preserve reading order for renderers. */\n position: number;\n total: number;\n /** Order + label, for platforms without built-in list-position semantics. */\n accessibleName: string;\n /** True only for the record matching a selectable Timeline's `selectedId`. */\n selected: boolean;\n }>;\n\nexport type ResolveTimelineOptions = Readonly<{\n composeAccessibleName: ComposeTimelineAccessibleName;\n}>;\n\nfunction assertNonEmpty(value: string, field: string): void {\n if (value.trim().length === 0) {\n throw new TypeError(`Timeline ${field} must not be empty`);\n }\n}\n\nexport function validateTimelineItemDescriptor<Id extends string>(\n item: TimelineItemDescriptor<Id>,\n): void {\n assertNonEmpty(item.id, \"item id\");\n if (item.emphasized !== undefined && typeof item.emphasized !== \"boolean\") {\n throw new TypeError(\"Timeline item emphasized must be a boolean\");\n }\n if (item.id !== item.id.trim()) {\n throw new TypeError(\"Timeline item id must not start or end with whitespace\");\n }\n assertNonEmpty(item.label, \"item label\");\n if (item.timestamp !== undefined) {\n assertNonEmpty(item.timestamp, \"item timestamp\");\n }\n if (item.dateTime !== undefined) {\n assertNonEmpty(item.dateTime, \"item dateTime\");\n if (item.timestamp === undefined) throw new TypeError(\"Timeline dateTime requires timestamp\");\n }\n if (item.description !== undefined) {\n assertNonEmpty(item.description, \"item description\");\n }\n if (\n item.tone !== undefined &&\n item.tone !== \"neutral\" &&\n item.tone !== \"info\" &&\n item.tone !== \"success\" &&\n item.tone !== \"attention\"\n ) {\n throw new TypeError(`Unsupported Timeline item tone: ${String(item.tone)}`);\n }\n}\n\nexport function validateTimelineDescriptor<Id extends string>(\n descriptor: TimelineDescriptor<Id>,\n): void {\n if (descriptor.items.length === 0) {\n throw new RangeError(\"Timeline must contain at least one item\");\n }\n const ids = new Set<Id>();\n for (const item of descriptor.items) {\n validateTimelineItemDescriptor(item);\n if (ids.has(item.id)) {\n throw new TypeError(`Duplicate Timeline item id: ${item.id}`);\n }\n ids.add(item.id);\n }\n if (descriptor.selectedId !== undefined && descriptor.selectedId !== null && !ids.has(descriptor.selectedId)) {\n throw new RangeError(`Timeline selectedId does not match any item id: ${String(descriptor.selectedId)}`);\n }\n}\n\n/**\n * Selection is a pair: a controlled `selectedId` (null for none) and the change callback, or neither. A selectable\n * record is one target (Web option, Native selected button), so it cannot also host `renderActions` commands —\n * nested controls inside an option are not operable by assistive tech. Products that need a per-record command keep\n * the passive Timeline.\n */\nexport function validateTimelineSelection(\n selectedId: unknown,\n onSelectedIdChange: unknown,\n options: Readonly<{ hasActions: boolean }> = { hasActions: false },\n): void {\n if ((selectedId === undefined) !== (onSelectedIdChange === undefined)) {\n throw new TypeError(\"Timeline selectedId and onSelectedIdChange must be supplied together\");\n }\n if (onSelectedIdChange !== undefined && typeof onSelectedIdChange !== \"function\") {\n throw new TypeError(\"Timeline onSelectedIdChange must be a function\");\n }\n if (onSelectedIdChange !== undefined && options.hasActions) {\n throw new TypeError(\"A selectable Timeline cannot render per-record actions; the record itself is the selection target\");\n }\n}\n\n/**\n * Keyboard target for a selectable Timeline (vertical listbox): ArrowDown/ArrowUp step one record, Home/End jump to\n * the ends, and the ends do not wrap — a history has a first and a last record. Selection follows focus, as in the\n * single-select listbox pattern, because choosing a record only changes what the product shows next to it.\n * Returns undefined for keys Timeline does not own. Reuses the shared collection navigation math.\n */\nexport function getTimelineSelectionTarget<Id extends string>(\n items: readonly TimelineItemDescriptor<Id>[],\n currentId: Id | null,\n key: string,\n): Id | undefined {\n const intent = getCollectionNavigationIntent(key as Parameters<typeof getCollectionNavigationIntent>[0]);\n if (!intent) return undefined;\n return getCollectionNavigationTarget(\n { items: items.map((item) => ({ id: item.id, label: item.label, textValue: item.label })) },\n currentId,\n intent,\n false,\n );\n}\n\n/**\n * Attaches order-preserving accessible names to an already-ordered item list.\n * Unlike Steps, no status is derived from position — every item already\n * happened, so this only adds order and the resolved tone default.\n */\nexport function resolveTimelineDescriptor<Id extends string>(\n descriptor: TimelineDescriptor<Id>,\n options: ResolveTimelineOptions,\n): readonly ResolvedTimelineItemDescriptor<Id>[] {\n validateTimelineDescriptor(descriptor);\n if (typeof options.composeAccessibleName !== \"function\") {\n throw new TypeError(\"Timeline composeAccessibleName must be a function\");\n }\n\n const total = descriptor.items.length;\n return descriptor.items.map((item, index) => {\n const position = index + 1;\n const accessibleName = options.composeAccessibleName({\n position,\n total,\n label: item.label,\n });\n if (typeof accessibleName !== \"string\" || accessibleName.trim().length === 0) {\n throw new TypeError(\n \"Timeline composeAccessibleName must return a non-empty string\",\n );\n }\n return {\n ...item,\n tone: item.tone ?? timelineDefaults.itemTone,\n position,\n total,\n accessibleName,\n selected: descriptor.selectedId !== undefined && descriptor.selectedId === item.id,\n };\n });\n}\n\nexport const timelineRecipe = {\n slots: [\n \"root\",\n \"item\",\n \"dot\",\n \"connector\",\n \"content\",\n \"timestamp\",\n \"label\",\n \"description\",\n ] as const,\n gap: spacing.lg,\n // A record remains passive: emphasis highlights its history without implying progress.\n item: {\n padding: spacing.sm,\n emphasisBackground: semanticColors.feedback.info.background,\n /**\n * Selected record (selectable Timeline): the neutral persistent-selection surface and ring shared with ListRow,\n * not brand fill (theme-palette.md 선택 상태 색상 기준). Selection outranks passive emphasis on the same record.\n */\n selected: {\n background: semanticColors.selection.background,\n border: semanticColors.selection.border,\n borderWidth: stroke.default,\n },\n },\n dot: {\n diameter: 10,\n borderWidth: stroke.default,\n tones: {\n neutral: { border: null, fill: semanticColors.content.secondary },\n info: {\n border: semanticColors.feedback.info.border,\n fill: semanticColors.feedback.info.foreground,\n },\n success: {\n border: semanticColors.feedback.success.border,\n fill: semanticColors.feedback.success.foreground,\n },\n attention: {\n border: semanticColors.feedback.attention.border,\n fill: semanticColors.feedback.attention.foreground,\n },\n },\n },\n /**\n * A single tone, unlike stepsRecipe.connector's reached/unreached pair —\n * Timeline has no cursor, so no segment is \"not yet reached\".\n */\n connector: {\n width: stroke.default,\n tone: semanticColors.border.default,\n },\n timestamp: {\n textVariant: \"caption\",\n color: semanticColors.content.secondary,\n },\n label: {\n textVariant: \"body\",\n fontWeight: fontWeight.medium,\n color: semanticColors.content.primary,\n },\n description: {\n textVariant: \"body\",\n color: semanticColors.content.body,\n },\n} as const satisfies {\n slots: readonly string[];\n gap: number;\n item: {\n padding: number;\n emphasisBackground: ColorReference;\n selected: { background: ColorReference; border: ColorReference; borderWidth: number };\n };\n dot: {\n diameter: number;\n borderWidth: number;\n tones: Record<\n TimelineItemTone,\n { border: ColorReference | null; fill: ColorReference }\n >;\n };\n connector: {\n width: number;\n tone: ColorReference;\n };\n timestamp: { textVariant: TextVariant; color: ColorReference };\n label: { textVariant: TextVariant; fontWeight: FontWeightValue; color: ColorReference };\n description: { textVariant: TextVariant; color: ColorReference };\n};\n"]}
|
|
1
|
+
{"version":3,"file":"timeline.js","sourceRoot":"","sources":["../src/timeline.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ColorReference } from \"./color-references.js\";\nimport type { FontWeightValue, TextVariant } from \"./foundations.js\";\nimport { fontWeight, spacing, stroke } from \"./foundations.js\";\nimport { semanticColors } from \"./semantic-colors.js\";\nimport { getCollectionNavigationIntent, getCollectionNavigationTarget } from \"./collection.js\";\n\n/**\n * Timeline is a record of what already happened, not a position in a flow —\n * that is Steps' job (see docs/steps.md and the boundary note in\n * docs/timeline.md). Because there is no cursor, Timeline never derives a\n * status from position: every item is simply \"happened\", and the connector\n * between items carries no reached/unreached meaning. Ant Design's\n * alternating left/right layout is intentionally not reproduced — it is a\n * Web-only decoration with no equivalent in a single-direction native list;\n * see docs/timeline.md for the rationale.\n */\nexport type TimelineItemTone = \"neutral\" | \"info\" | \"success\" | \"attention\";\n\nexport type TimelineItemDescriptor<Id extends string = string> = Readonly<{\n id: Id;\n label: string;\n /** Product-formatted \"when\" copy, e.g. \"3회 초\" or \"2024-01-15\". Timeline never formats a clock, inning, or date itself. */\n timestamp?: string;\n /** Machine-readable date for the formatted timestamp; products retain the original instant. */\n dateTime?: string;\n /** Product-formatted supplementary detail, e.g. \"안타로 1루 진출\". */\n description?: string;\n tone?: TimelineItemTone;\n /** Passive emphasis for a notable record, not a progress cursor or selection. */\n emphasized?: boolean;\n}>;\n\nexport type TimelineDescriptor<Id extends string = string> = Readonly<{\n items: readonly TimelineItemDescriptor<Id>[];\n /**\n * Single selection of one record (2026-10-23): `undefined` keeps the passive record, `null` makes the list selectable\n * with nothing chosen yet. Selection is not a progress cursor — no status is derived from it. Diairy's journey log\n * rebuilt this with a `Button` tone toggle + `aria-pressed` per step (SELECTION forbids button-as-selection).\n */\n selectedId?: Id | null;\n}>;\n\nexport const timelineDefaults = {\n itemTone: \"neutral\",\n variant: \"journal\",\n} as const satisfies Readonly<{ itemTone: TimelineItemTone; variant: \"record\" | \"journal\" }>;\n\nexport type TimelineAccessibleNameInfo = Readonly<{\n position: number;\n total: number;\n label: string;\n}>;\n\n/**\n * Products own the exact phrase order and counter-word grammar, same reason\n * Steps does not assemble its own accessible name (see docs/steps.md).\n */\nexport type ComposeTimelineAccessibleName = (\n info: TimelineAccessibleNameInfo,\n) => string;\n\nexport type ResolvedTimelineItemDescriptor<Id extends string = string> = Omit<\n TimelineItemDescriptor<Id>,\n \"tone\"\n> &\n Readonly<{\n tone: TimelineItemTone;\n /** 1-indexed so position and label both preserve reading order for renderers. */\n position: number;\n total: number;\n /** Order + label, for platforms without built-in list-position semantics. */\n accessibleName: string;\n /** True only for the record matching a selectable Timeline's `selectedId`. */\n selected: boolean;\n }>;\n\nexport type ResolveTimelineOptions = Readonly<{\n composeAccessibleName: ComposeTimelineAccessibleName;\n}>;\n\nfunction assertNonEmpty(value: string, field: string): void {\n if (value.trim().length === 0) {\n throw new TypeError(`Timeline ${field} must not be empty`);\n }\n}\n\nexport function validateTimelineItemDescriptor<Id extends string>(\n item: TimelineItemDescriptor<Id>,\n): void {\n assertNonEmpty(item.id, \"item id\");\n if (item.emphasized !== undefined && typeof item.emphasized !== \"boolean\") {\n throw new TypeError(\"Timeline item emphasized must be a boolean\");\n }\n if (item.id !== item.id.trim()) {\n throw new TypeError(\"Timeline item id must not start or end with whitespace\");\n }\n assertNonEmpty(item.label, \"item label\");\n if (item.timestamp !== undefined) {\n assertNonEmpty(item.timestamp, \"item timestamp\");\n }\n if (item.dateTime !== undefined) {\n assertNonEmpty(item.dateTime, \"item dateTime\");\n if (item.timestamp === undefined) throw new TypeError(\"Timeline dateTime requires timestamp\");\n }\n if (item.description !== undefined) {\n assertNonEmpty(item.description, \"item description\");\n }\n if (\n item.tone !== undefined &&\n item.tone !== \"neutral\" &&\n item.tone !== \"info\" &&\n item.tone !== \"success\" &&\n item.tone !== \"attention\"\n ) {\n throw new TypeError(`Unsupported Timeline item tone: ${String(item.tone)}`);\n }\n}\n\nexport function validateTimelineDescriptor<Id extends string>(\n descriptor: TimelineDescriptor<Id>,\n): void {\n if (descriptor.items.length === 0) {\n throw new RangeError(\"Timeline must contain at least one item\");\n }\n const ids = new Set<Id>();\n for (const item of descriptor.items) {\n validateTimelineItemDescriptor(item);\n if (ids.has(item.id)) {\n throw new TypeError(`Duplicate Timeline item id: ${item.id}`);\n }\n ids.add(item.id);\n }\n if (descriptor.selectedId !== undefined && descriptor.selectedId !== null && !ids.has(descriptor.selectedId)) {\n throw new RangeError(`Timeline selectedId does not match any item id: ${String(descriptor.selectedId)}`);\n }\n}\n\n/**\n * Selection is a pair: a controlled `selectedId` (null for none) and the change callback, or neither. A selectable\n * record is one target (Web option, Native selected button), so it cannot also host `renderActions` commands —\n * nested controls inside an option are not operable by assistive tech. Products that need a per-record command keep\n * the passive Timeline.\n */\nexport function validateTimelineSelection(\n selectedId: unknown,\n onSelectedIdChange: unknown,\n options: Readonly<{ hasActions: boolean; variant?: \"record\" | \"journal\" }> = { hasActions: false },\n): void {\n if (options.variant !== undefined && options.variant !== \"record\" && options.variant !== \"journal\") {\n throw new TypeError(\"Unsupported Timeline variant\");\n }\n if ((selectedId === undefined) !== (onSelectedIdChange === undefined)) {\n throw new TypeError(\"Timeline selectedId and onSelectedIdChange must be supplied together\");\n }\n if (onSelectedIdChange !== undefined && typeof onSelectedIdChange !== \"function\") {\n throw new TypeError(\"Timeline onSelectedIdChange must be a function\");\n }\n if (onSelectedIdChange !== undefined && options.hasActions) {\n throw new TypeError(\"A selectable Timeline cannot render per-record actions; the record itself is the selection target\");\n }\n}\n\n/**\n * Keyboard target for a selectable Timeline (vertical listbox): ArrowDown/ArrowUp step one record, Home/End jump to\n * the ends, and the ends do not wrap — a history has a first and a last record. Selection follows focus, as in the\n * single-select listbox pattern, because choosing a record only changes what the product shows next to it.\n * Returns undefined for keys Timeline does not own. Reuses the shared collection navigation math.\n */\nexport function getTimelineSelectionTarget<Id extends string>(\n items: readonly TimelineItemDescriptor<Id>[],\n currentId: Id | null,\n key: string,\n): Id | undefined {\n const intent = getCollectionNavigationIntent(key as Parameters<typeof getCollectionNavigationIntent>[0]);\n if (!intent) return undefined;\n return getCollectionNavigationTarget(\n { items: items.map((item) => ({ id: item.id, label: item.label, textValue: item.label })) },\n currentId,\n intent,\n false,\n );\n}\n\n/**\n * Attaches order-preserving accessible names to an already-ordered item list.\n * Unlike Steps, no status is derived from position — every item already\n * happened, so this only adds order and the resolved tone default.\n */\nexport function resolveTimelineDescriptor<Id extends string>(\n descriptor: TimelineDescriptor<Id>,\n options: ResolveTimelineOptions,\n): readonly ResolvedTimelineItemDescriptor<Id>[] {\n validateTimelineDescriptor(descriptor);\n if (typeof options.composeAccessibleName !== \"function\") {\n throw new TypeError(\"Timeline composeAccessibleName must be a function\");\n }\n\n const total = descriptor.items.length;\n return descriptor.items.map((item, index) => {\n const position = index + 1;\n const accessibleName = options.composeAccessibleName({\n position,\n total,\n label: item.label,\n });\n if (typeof accessibleName !== \"string\" || accessibleName.trim().length === 0) {\n throw new TypeError(\n \"Timeline composeAccessibleName must return a non-empty string\",\n );\n }\n return {\n ...item,\n tone: item.tone ?? timelineDefaults.itemTone,\n position,\n total,\n accessibleName,\n selected: descriptor.selectedId !== undefined && descriptor.selectedId === item.id,\n };\n });\n}\n\nexport const timelineRecipe = {\n slots: [\n \"root\",\n \"item\",\n \"dot\",\n \"connector\",\n \"content\",\n \"timestamp\",\n \"label\",\n \"description\",\n ] as const,\n gap: spacing.lg,\n // 2026-10-27: the approved journey journal keeps the reading rail, without large selected cards.\n // Passive histories keep every detail and command; selection adds disclosure without changing event semantics.\n journal: { gap: 0, paddingBlock: spacing.md, dotDiameter: 8 },\n // A record remains passive: emphasis highlights its history without implying progress.\n item: {\n padding: spacing.sm,\n emphasisBackground: semanticColors.feedback.info.background,\n /**\n * Selected record (selectable Timeline): the neutral persistent-selection surface and ring shared with ListRow,\n * not brand fill (theme-palette.md 선택 상태 색상 기준). Selection outranks passive emphasis on the same record.\n */\n selected: {\n background: semanticColors.selection.background,\n border: semanticColors.selection.border,\n borderWidth: stroke.default,\n },\n },\n dot: {\n diameter: 10,\n borderWidth: stroke.default,\n tones: {\n neutral: { border: null, fill: semanticColors.content.secondary },\n info: {\n border: semanticColors.feedback.info.border,\n fill: semanticColors.feedback.info.foreground,\n },\n success: {\n border: semanticColors.feedback.success.border,\n fill: semanticColors.feedback.success.foreground,\n },\n attention: {\n border: semanticColors.feedback.attention.border,\n fill: semanticColors.feedback.attention.foreground,\n },\n },\n },\n /**\n * A single tone, unlike stepsRecipe.connector's reached/unreached pair —\n * Timeline has no cursor, so no segment is \"not yet reached\".\n */\n connector: {\n width: stroke.default,\n tone: semanticColors.border.default,\n },\n timestamp: {\n textVariant: \"caption\",\n color: semanticColors.content.secondary,\n },\n label: {\n textVariant: \"body\",\n fontWeight: fontWeight.medium,\n color: semanticColors.content.primary,\n },\n description: {\n textVariant: \"body\",\n color: semanticColors.content.body,\n },\n} as const satisfies {\n slots: readonly string[];\n gap: number;\n journal: { gap: number; paddingBlock: number; dotDiameter: number };\n item: {\n padding: number;\n emphasisBackground: ColorReference;\n selected: { background: ColorReference; border: ColorReference; borderWidth: number };\n };\n dot: {\n diameter: number;\n borderWidth: number;\n tones: Record<\n TimelineItemTone,\n { border: ColorReference | null; fill: ColorReference }\n >;\n };\n connector: {\n width: number;\n tone: ColorReference;\n };\n timestamp: { textVariant: TextVariant; color: ColorReference };\n label: { textVariant: TextVariant; fontWeight: FontWeightValue; color: ColorReference };\n description: { textVariant: TextVariant; color: ColorReference };\n};\n"]}
|
package/dist/version.d.ts
CHANGED
package/dist/version.js
CHANGED
package/dist/version.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"version.js","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,MAAM,CAAC,MAAM,mBAAmB,GAAG,QAAiB,CAAC","sourcesContent":["/** Package release shown by documentation surfaces. Kept in sync by a test. */\nexport const designSystemVersion = \"1.
|
|
1
|
+
{"version":3,"file":"version.js","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,MAAM,CAAC,MAAM,mBAAmB,GAAG,QAAiB,CAAC","sourcesContent":["/** Package release shown by documentation surfaces. Kept in sync by a test. */\nexport const designSystemVersion = \"1.50.0\" as const;\n"]}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# 손그림 편집기 계약
|
|
2
|
+
|
|
3
|
+
2026-10-27 사용자 요청으로 다에리·스핀트의 동일한 그림 편집기를 공통 API로 옮겼다.
|
|
4
|
+
`DrawingEditor`는 `EditorScreen`·`ColorPicker`·`Slider`를 합성하는 안정 컴포넌트다.
|
|
5
|
+
|
|
6
|
+
순수 이력·선·지우개·배경·팔레트·배치는 `src/drawing-editor.ts`가 소유한다. Web pointer capture와 Native responder,
|
|
7
|
+
SVG/Canvas 이미지 추출은 각 renderer에 남는다. 제품 계정·업로드·저장 형식 제한·성공 후 탐색은 포함하지 않는다.
|
|
8
|
+
|
|
9
|
+
프로필 사진은 `cropPreview="circle"`을 전달한다. 기본 사각 캔버스와 원형 미리보기 모두 실제 출력에는 오버레이를 넣지 않는다.
|
|
10
|
+
저장 실패는 그림을 보존하며 같은 초안으로 다시 시도한다. 한 획을 하나의 이력 단계로 취급하고 새로운 획은 redo 분기를 없앤다.
|
|
11
|
+
펜과 지우개의 실제 굵기는 같은 슬라이더 값이며 지우개는 항상 현재 배경색을 따른다.
|
|
12
|
+
|
|
13
|
+
사용법과 소비 이관은 [DrawingEditor 지침](usage/components/drawing-editor.md)을 따른다. 구현·표면 테스트·제품 채택과 패키지 게시·실기기 QA는 각각 별도 상태다.
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
# Component maturity and evidence manifest
|
|
2
2
|
|
|
3
|
-
> Generated from `@hjmds/design-contracts` 1.
|
|
3
|
+
> Generated from `@hjmds/design-contracts` 1.50.0. Do not edit this file directly; run `pnpm contracts:sync`.
|
|
4
4
|
|
|
5
5
|
This document is the human-readable projection of the same catalog and Showcase manifest consumed by product CI.
|
|
6
6
|
|
|
7
|
-
- Contract: stable
|
|
8
|
-
- Web renderer: stable
|
|
9
|
-
- Native renderer: stable
|
|
7
|
+
- Contract: stable 132
|
|
8
|
+
- Web renderer: stable 132
|
|
9
|
+
- Native renderer: stable 115 · unsupported 17
|
|
10
10
|
|
|
11
11
|
| Component | Canonical story ID | Contract | Web | Native | Documentation | Required evidence |
|
|
12
12
|
| --- | --- | --- | --- | --- | --- | --- |
|
|
@@ -54,6 +54,7 @@ This document is the human-readable projection of the same catalog and Showcase
|
|
|
54
54
|
| TimePicker | `input/time-picker` | stable | stable | stable | [authored](../time-picker.md) | contract: contract; web: default, dark, long-copy, large-text, rtl, reduced-motion, accessibility, keyboard; native: default, dark, long-copy, large-text, rtl, reduced-motion, accessibility, native-actions |
|
|
55
55
|
| DatePicker | `input/date-picker` | stable | stable | stable | [authored](../date-picker.md) | contract: contract; web: default, dark, long-copy, large-text, rtl, reduced-motion, accessibility, keyboard; native: default, dark, long-copy, large-text, rtl, reduced-motion, accessibility, native-actions |
|
|
56
56
|
| DateRangePicker | `input/date-range-picker` | stable | stable | stable | generated only | contract: contract; web: default, dark, large-text, rtl, reduced-motion, accessibility, keyboard; native: default, dark, large-text, rtl, reduced-motion, accessibility, native-actions |
|
|
57
|
+
| DrawingEditor | `input/drawing-editor` | stable | stable | stable | [authored](../drawing-editor.md) | contract: contract; web: default, dark, long-copy, large-text, rtl, reduced-motion, accessibility; native: default, dark, long-copy, large-text, rtl, reduced-motion, accessibility |
|
|
57
58
|
| ColorPicker | `input/color-picker` | stable | stable | stable | [authored](../color-picker.md) | contract: contract; web: default, dark, long-copy, large-text, rtl, reduced-motion, accessibility, keyboard; native: default, dark, long-copy, large-text, rtl, reduced-motion, accessibility |
|
|
58
59
|
| FilePicker | `input/file-picker` | stable | stable | stable | [authored](../file-picker.md) | contract: contract; web: default, dark, long-copy, large-text, rtl, reduced-motion, accessibility, keyboard; native: default, dark, long-copy, large-text, rtl, reduced-motion, accessibility, native-actions |
|
|
59
60
|
| Form | `input/form` | stable | stable | stable | [authored](../form.md) | contract: contract; web: default, dark, large-text, rtl, reduced-motion, accessibility, keyboard; native: default, dark, large-text, rtl, reduced-motion, accessibility, native-actions |
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
|
-
"designSystemVersion": "1.
|
|
3
|
+
"designSystemVersion": "1.50.0",
|
|
4
4
|
"source": "packages/design-contracts/docs/generated/showcase-manifest.json",
|
|
5
5
|
"surfaces": {
|
|
6
6
|
"web": {
|
|
7
7
|
"packageName": "@hjmds/react",
|
|
8
|
-
"packageVersion": "1.
|
|
9
|
-
"activeComponents":
|
|
10
|
-
"claimedComponents":
|
|
11
|
-
"completeScenarioComponents":
|
|
8
|
+
"packageVersion": "1.50.0",
|
|
9
|
+
"activeComponents": 132,
|
|
10
|
+
"claimedComponents": 132,
|
|
11
|
+
"completeScenarioComponents": 131,
|
|
12
12
|
"components": [
|
|
13
13
|
{
|
|
14
14
|
"componentId": "text",
|
|
@@ -2417,6 +2417,56 @@
|
|
|
2417
2417
|
],
|
|
2418
2418
|
"missingScenarios": []
|
|
2419
2419
|
},
|
|
2420
|
+
{
|
|
2421
|
+
"componentId": "drawing-editor",
|
|
2422
|
+
"name": "DrawingEditor",
|
|
2423
|
+
"status": "stable",
|
|
2424
|
+
"claimed": true,
|
|
2425
|
+
"subpath": "./drawing-editor",
|
|
2426
|
+
"exportNames": [
|
|
2427
|
+
"DrawingEditor"
|
|
2428
|
+
],
|
|
2429
|
+
"proofs": [
|
|
2430
|
+
{
|
|
2431
|
+
"scenarios": [
|
|
2432
|
+
"default"
|
|
2433
|
+
],
|
|
2434
|
+
"file": "test/default-render.ssr.test.tsx",
|
|
2435
|
+
"caseId": "drawing-editor"
|
|
2436
|
+
},
|
|
2437
|
+
{
|
|
2438
|
+
"scenarios": [
|
|
2439
|
+
"dark",
|
|
2440
|
+
"long-copy",
|
|
2441
|
+
"large-text",
|
|
2442
|
+
"rtl",
|
|
2443
|
+
"reduced-motion",
|
|
2444
|
+
"accessibility"
|
|
2445
|
+
],
|
|
2446
|
+
"file": "test/scenario-matrix.browser.test.tsx",
|
|
2447
|
+
"caseId": "drawing-editor"
|
|
2448
|
+
}
|
|
2449
|
+
],
|
|
2450
|
+
"requiredScenarios": [
|
|
2451
|
+
"default",
|
|
2452
|
+
"dark",
|
|
2453
|
+
"long-copy",
|
|
2454
|
+
"large-text",
|
|
2455
|
+
"rtl",
|
|
2456
|
+
"reduced-motion",
|
|
2457
|
+
"accessibility"
|
|
2458
|
+
],
|
|
2459
|
+
"claimedScenarios": [
|
|
2460
|
+
"default",
|
|
2461
|
+
"dark",
|
|
2462
|
+
"long-copy",
|
|
2463
|
+
"large-text",
|
|
2464
|
+
"rtl",
|
|
2465
|
+
"reduced-motion",
|
|
2466
|
+
"accessibility"
|
|
2467
|
+
],
|
|
2468
|
+
"missingScenarios": []
|
|
2469
|
+
},
|
|
2420
2470
|
{
|
|
2421
2471
|
"componentId": "color-picker",
|
|
2422
2472
|
"name": "ColorPicker",
|
|
@@ -7064,10 +7114,10 @@
|
|
|
7064
7114
|
},
|
|
7065
7115
|
"native": {
|
|
7066
7116
|
"packageName": "@hjmds/react-native",
|
|
7067
|
-
"packageVersion": "1.
|
|
7068
|
-
"activeComponents":
|
|
7069
|
-
"claimedComponents":
|
|
7070
|
-
"completeScenarioComponents":
|
|
7117
|
+
"packageVersion": "1.50.0",
|
|
7118
|
+
"activeComponents": 115,
|
|
7119
|
+
"claimedComponents": 115,
|
|
7120
|
+
"completeScenarioComponents": 112,
|
|
7071
7121
|
"components": [
|
|
7072
7122
|
{
|
|
7073
7123
|
"componentId": "text",
|
|
@@ -9355,6 +9405,56 @@
|
|
|
9355
9405
|
],
|
|
9356
9406
|
"missingScenarios": []
|
|
9357
9407
|
},
|
|
9408
|
+
{
|
|
9409
|
+
"componentId": "drawing-editor",
|
|
9410
|
+
"name": "DrawingEditor",
|
|
9411
|
+
"status": "stable",
|
|
9412
|
+
"claimed": true,
|
|
9413
|
+
"subpath": "./drawing-editor",
|
|
9414
|
+
"exportNames": [
|
|
9415
|
+
"DrawingEditor"
|
|
9416
|
+
],
|
|
9417
|
+
"proofs": [
|
|
9418
|
+
{
|
|
9419
|
+
"scenarios": [
|
|
9420
|
+
"default"
|
|
9421
|
+
],
|
|
9422
|
+
"file": "test/default-render.test.tsx",
|
|
9423
|
+
"caseId": "drawing-editor"
|
|
9424
|
+
},
|
|
9425
|
+
{
|
|
9426
|
+
"scenarios": [
|
|
9427
|
+
"dark",
|
|
9428
|
+
"long-copy",
|
|
9429
|
+
"large-text",
|
|
9430
|
+
"rtl",
|
|
9431
|
+
"reduced-motion",
|
|
9432
|
+
"accessibility"
|
|
9433
|
+
],
|
|
9434
|
+
"file": "test/scenario-matrix.test.tsx",
|
|
9435
|
+
"caseId": "drawing-editor"
|
|
9436
|
+
}
|
|
9437
|
+
],
|
|
9438
|
+
"requiredScenarios": [
|
|
9439
|
+
"default",
|
|
9440
|
+
"dark",
|
|
9441
|
+
"long-copy",
|
|
9442
|
+
"large-text",
|
|
9443
|
+
"rtl",
|
|
9444
|
+
"reduced-motion",
|
|
9445
|
+
"accessibility"
|
|
9446
|
+
],
|
|
9447
|
+
"claimedScenarios": [
|
|
9448
|
+
"default",
|
|
9449
|
+
"dark",
|
|
9450
|
+
"long-copy",
|
|
9451
|
+
"large-text",
|
|
9452
|
+
"rtl",
|
|
9453
|
+
"reduced-motion",
|
|
9454
|
+
"accessibility"
|
|
9455
|
+
],
|
|
9456
|
+
"missingScenarios": []
|
|
9457
|
+
},
|
|
9358
9458
|
{
|
|
9359
9459
|
"componentId": "color-picker",
|
|
9360
9460
|
"name": "ColorPicker",
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
# Renderer evidence coverage
|
|
2
2
|
|
|
3
|
-
> Generated for HJM 1.
|
|
3
|
+
> Generated for HJM 1.50.0. Do not edit directly; run `pnpm evidence:sync`.
|
|
4
4
|
|
|
5
5
|
This projection joins the canonical surface maturity manifest with first-party renderer claims. Missing scenarios are explicit beta promotion debt; stable surfaces are blocked by CI until none remain.
|
|
6
6
|
|
|
7
|
-
- web:
|
|
8
|
-
- native:
|
|
7
|
+
- web: 132/132 active implementations; 131/132 required scenario sets
|
|
8
|
+
- native: 115/115 active implementations; 112/115 required scenario sets
|
|
9
9
|
|
|
10
10
|
| Component | Surface | Maturity | Renderer package | Export | Claimed scenarios | Missing required scenarios | Executable proofs |
|
|
11
11
|
| --- | --- | --- | --- | --- | --- | --- | --- |
|
|
@@ -53,6 +53,7 @@ This projection joins the canonical surface maturity manifest with first-party r
|
|
|
53
53
|
| TimePicker | web | stable | @hjmds/react | ./time-picker | default, dark, long-copy, large-text, rtl, reduced-motion, accessibility, keyboard | none | test/default-render.ssr.test.tsx#time-picker, test/scenario-matrix.browser.test.tsx#time-picker, test/time-picker.browser.test.tsx#time-picker |
|
|
54
54
|
| DatePicker | web | stable | @hjmds/react | ./date-picker | default, dark, long-copy, large-text, rtl, reduced-motion, accessibility, keyboard | none | test/default-render.ssr.test.tsx#date-picker, test/scenario-matrix.browser.test.tsx#date-picker, test/stable-core.browser.test.tsx#date-picker |
|
|
55
55
|
| DateRangePicker | web | stable | @hjmds/react | ./date-range | default, dark, large-text, rtl, reduced-motion, accessibility, keyboard | none | test/default-render.ssr.test.tsx#date-range-picker, test/scenario-matrix.browser.test.tsx#date-range-picker, test/date-range.keyboard.browser.test.tsx#date-range-picker |
|
|
56
|
+
| DrawingEditor | web | stable | @hjmds/react | ./drawing-editor | default, dark, long-copy, large-text, rtl, reduced-motion, accessibility | none | test/default-render.ssr.test.tsx#drawing-editor, test/scenario-matrix.browser.test.tsx#drawing-editor |
|
|
56
57
|
| ColorPicker | web | stable | @hjmds/react | ./color-picker | default, dark, long-copy, large-text, rtl, reduced-motion, accessibility, keyboard | none | test/default-render.ssr.test.tsx#color-picker, test/scenario-matrix.browser.test.tsx#color-picker, test/web-additions.browser.test.tsx#color-picker |
|
|
57
58
|
| FilePicker | web | stable | @hjmds/react | ./file-picker | default, dark, long-copy, large-text, rtl, reduced-motion, accessibility, keyboard | none | test/default-render.ssr.test.tsx#file-picker, test/scenario-matrix.browser.test.tsx#file-picker, test/file-picker.keyboard.browser.test.tsx#file-picker |
|
|
58
59
|
| Form | web | stable | @hjmds/react | ./forms | default, dark, large-text, rtl, reduced-motion, accessibility, keyboard | none | test/default-render.ssr.test.tsx#form, test/scenario-matrix.browser.test.tsx#form, test/form.keyboard.browser.test.tsx#form |
|
|
@@ -182,6 +183,7 @@ This projection joins the canonical surface maturity manifest with first-party r
|
|
|
182
183
|
| TimePicker | native | stable | @hjmds/react-native | ./time-picker | default, dark, long-copy, large-text, rtl, reduced-motion, accessibility, native-actions | none | test/default-render.test.tsx#time-picker, test/scenario-matrix.test.tsx#time-picker, test/time-picker.test.tsx#time-picker |
|
|
183
184
|
| DatePicker | native | stable | @hjmds/react-native | ./date-picker | default, dark, long-copy, large-text, rtl, reduced-motion, accessibility, native-actions | none | test/default-render.test.tsx#date-picker, test/scenario-matrix.test.tsx#date-picker, test/stable-core.test.tsx#date-picker |
|
|
184
185
|
| DateRangePicker | native | stable | @hjmds/react-native | ./date-range | default, dark, large-text, rtl, reduced-motion, accessibility, native-actions | none | test/default-render.test.tsx#date-range-picker, test/scenario-matrix.test.tsx#date-range-picker, test/date-range-actions.test.tsx#date-range-picker |
|
|
186
|
+
| DrawingEditor | native | stable | @hjmds/react-native | ./drawing-editor | default, dark, long-copy, large-text, rtl, reduced-motion, accessibility | none | test/default-render.test.tsx#drawing-editor, test/scenario-matrix.test.tsx#drawing-editor |
|
|
185
187
|
| ColorPicker | native | stable | @hjmds/react-native | ./color-picker | default, dark, long-copy, large-text, rtl, reduced-motion, accessibility | none | test/default-render.test.tsx#color-picker, test/scenario-matrix.test.tsx#color-picker |
|
|
186
188
|
| FilePicker | native | stable | @hjmds/react-native | ./file-picker | default, dark, long-copy, large-text, rtl, reduced-motion, accessibility, native-actions | none | test/default-render.test.tsx#file-picker, test/scenario-matrix.test.tsx#file-picker, test/file-picker-host-action.test.tsx#file-picker |
|
|
187
189
|
| Form | native | stable | @hjmds/react-native | ./forms | default, dark, large-text, rtl, reduced-motion, accessibility, native-actions | none | test/default-render.test.tsx#form, test/scenario-matrix.test.tsx#form, test/form.interaction.test.tsx#form |
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 2,
|
|
3
|
-
"designSystemVersion": "1.
|
|
3
|
+
"designSystemVersion": "1.50.0",
|
|
4
4
|
"packageName": "@hjmds/design-contracts",
|
|
5
5
|
"components": [
|
|
6
6
|
{
|
|
@@ -2289,6 +2289,57 @@
|
|
|
2289
2289
|
"authored": null
|
|
2290
2290
|
}
|
|
2291
2291
|
},
|
|
2292
|
+
{
|
|
2293
|
+
"id": "drawing-editor",
|
|
2294
|
+
"storyId": "input/drawing-editor",
|
|
2295
|
+
"name": "DrawingEditor",
|
|
2296
|
+
"label": "손그림 편집기",
|
|
2297
|
+
"companions": [],
|
|
2298
|
+
"extensions": [],
|
|
2299
|
+
"category": "input",
|
|
2300
|
+
"platform": "adaptive",
|
|
2301
|
+
"contractStatus": "stable",
|
|
2302
|
+
"surfaces": {
|
|
2303
|
+
"web": "stable",
|
|
2304
|
+
"native": "stable"
|
|
2305
|
+
},
|
|
2306
|
+
"requirements": [
|
|
2307
|
+
{
|
|
2308
|
+
"surface": "contract",
|
|
2309
|
+
"scenarios": [
|
|
2310
|
+
"contract"
|
|
2311
|
+
]
|
|
2312
|
+
},
|
|
2313
|
+
{
|
|
2314
|
+
"surface": "web",
|
|
2315
|
+
"scenarios": [
|
|
2316
|
+
"default",
|
|
2317
|
+
"dark",
|
|
2318
|
+
"long-copy",
|
|
2319
|
+
"large-text",
|
|
2320
|
+
"rtl",
|
|
2321
|
+
"reduced-motion",
|
|
2322
|
+
"accessibility"
|
|
2323
|
+
]
|
|
2324
|
+
},
|
|
2325
|
+
{
|
|
2326
|
+
"surface": "native",
|
|
2327
|
+
"scenarios": [
|
|
2328
|
+
"default",
|
|
2329
|
+
"dark",
|
|
2330
|
+
"long-copy",
|
|
2331
|
+
"large-text",
|
|
2332
|
+
"rtl",
|
|
2333
|
+
"reduced-motion",
|
|
2334
|
+
"accessibility"
|
|
2335
|
+
]
|
|
2336
|
+
}
|
|
2337
|
+
],
|
|
2338
|
+
"documentation": {
|
|
2339
|
+
"generated": "docs/generated/component-maturity.md",
|
|
2340
|
+
"authored": "docs/drawing-editor.md"
|
|
2341
|
+
}
|
|
2342
|
+
},
|
|
2292
2343
|
{
|
|
2293
2344
|
"id": "color-picker",
|
|
2294
2345
|
"storyId": "input/color-picker",
|
|
@@ -59,7 +59,7 @@ packages or migrate consumer applications.
|
|
|
59
59
|
adoption. These are opt-in extensions, not six new canonical components or a stable promotion.
|
|
60
60
|
The canonical Statistic/Menu/Sheet/KeyboardAvoiding APIs remain available without additional peers.
|
|
61
61
|
ContextMenu's canonical Native status remains unsupported until the OS adapter has device evidence.
|
|
62
|
-
ImageViewer and
|
|
62
|
+
At this historical 2026-09-30 checkpoint ImageViewer and native adapters were experimental; current support is stated above.
|
|
63
63
|
|
|
64
64
|
## Entry points and ownership
|
|
65
65
|
|
package/docs/stable-core.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Stable Core
|
|
2
2
|
|
|
3
|
+
## 2026-10-27 DrawingEditor와 ShareSheet 승격
|
|
4
|
+
|
|
5
|
+
남은 실험의 승격 요청에 따라 DrawingEditor 계약과 Web·Native renderer를 stable로 올리고,
|
|
6
|
+
독립 카탈로그가 아닌 ShareSheet 합성 API도 문서화한 입력·결과 계약의 stable 지원으로 전환한다.
|
|
7
|
+
두 컴포넌트의 Web·Native 예시를 배포 섹션으로 옮긴다. DrawingEditor 획 입력은 포인터·터치이며
|
|
8
|
+
키보드 그리기는 제공하지 않는다. ShareSheet의 실제 복사·기기 공유·전송은 제품 또는 OS가 소유한다.
|
|
9
|
+
|
|
10
|
+
변경 범위 검사는 DrawingEditor·ShareSheet의 contracts 6건, Web 8건, Native 6건과
|
|
11
|
+
DrawingEditor의 어두운 RTL 환경·이름 있는 도구 동작을 각 renderer에서 추가 확인했다.
|
|
12
|
+
이는 Web 브라우저와 Native host 테스트이며 실제 OS 기기, VoiceOver/TalkBack, 제품 업로드나
|
|
13
|
+
스토어 반영의 증거는 아니다. 기존 선택형 어댑터의 10월 26일 stable 판정과 어긋난 개별 지침도 맞춘다.
|
|
14
|
+
|
|
3
15
|
## 2026-10-26 잔여 실험과 beta 승격
|
|
4
16
|
|
|
5
17
|
사용자의 “다 올려” 지시에 따라 남아 있던 카탈로그 beta와 실험 화면을 승격했다.
|
package/docs/timeline.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# Timeline contract
|
|
2
2
|
|
|
3
|
+
## 기본 여행 일지 (2026-10-27, 개발 소스·미게시)
|
|
4
|
+
|
|
5
|
+
사용자 승인 시안 「여행 일지」를 기본형으로 지정했다. 기본 `variant="journal"`은 배경 박스 없이
|
|
6
|
+
8px dot·1px connector와 위아래 16px 여백을 사용한다. 수동 기록은 전체 설명과 독립 행동을 유지하고,
|
|
7
|
+
선택형은 선택한 기록의 설명만 펼친다. 같은 기록을 다시 누르면 지도 선택을 유지하면서 상세를 접는다.
|
|
8
|
+
새 선택(외부 지도·날짜 포함)은 해당 기록을 펼친다. 제목·시각은 남는다.
|
|
9
|
+
|
|
10
|
+
Web은 disclosure button 목록과 `aria-expanded`·`aria-controls`, Native는 selected·expanded 상태를 제공한다.
|
|
11
|
+
선택 색을 진행 상태로 해석하지 않으며 사건 tone과 행동/가상화 경계는 보존한다.
|
|
12
|
+
이전 listbox·면/테두리 표현은 `variant="record"`로 명시할 때 제공한다.
|
|
13
|
+
이하 2026-10-23까지의 선택 면/listbox 설명은 이 호환 표현의 이력이다.
|
|
14
|
+
상세 소비·영향 범위는 [사용 지침](usage/components/timeline.md)을 따른다.
|
|
15
|
+
|
|
3
16
|
## 문제
|
|
4
17
|
|
|
5
18
|
이미 일어난 일들을 시간 순서로 보여준다 — 야잘알의 경기 플레이 기록(PBP, "1회 초 안타 →
|