@pihanga2/shadcn 0.2.20 → 0.2.22
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/AGENT.using-cards.md +2 -2
- package/README.md +4 -1
- package/cards/attachment/attachment.component.d.ts +4 -0
- package/cards/attachment/attachment.component.js +46 -0
- package/cards/attachment/attachment.component.js.map +1 -0
- package/cards/attachment/attachment.types.d.ts +95 -0
- package/cards/attachment/attachment.types.js +7 -0
- package/cards/attachment/attachment.types.js.map +1 -0
- package/cards/attachment/attachmentGroup.component.d.ts +4 -0
- package/cards/attachment/attachmentGroup.component.js +21 -0
- package/cards/attachment/attachmentGroup.component.js.map +1 -0
- package/cards/attachment/index.d.ts +1 -0
- package/cards/attachment/index.js +16 -0
- package/cards/attachment/index.js.map +1 -0
- package/cards/bubble/bubble.component.d.ts +4 -0
- package/cards/bubble/bubble.component.js +67 -0
- package/cards/bubble/bubble.component.js.map +1 -0
- package/cards/bubble/bubble.types.d.ts +106 -0
- package/cards/bubble/bubble.types.js +7 -0
- package/cards/bubble/bubble.types.js.map +1 -0
- package/cards/bubble/bubbleGroup.component.d.ts +4 -0
- package/cards/bubble/bubbleGroup.component.js +21 -0
- package/cards/bubble/bubbleGroup.component.js.map +1 -0
- package/cards/bubble/index.d.ts +1 -0
- package/cards/bubble/index.js +16 -0
- package/cards/bubble/index.js.map +1 -0
- package/cards/button/button.component.js +56 -54
- package/cards/button/button.component.js.map +1 -1
- package/cards/button/button.types.d.ts +24 -0
- package/cards/button/button.types.js.map +1 -1
- package/cards/checkbox/checkbox.types.d.ts +2 -7
- package/cards/checkbox/checkbox.types.js.map +1 -1
- package/cards/dataTable/dataTable.component.js +191 -124
- package/cards/dataTable/dataTable.component.js.map +1 -1
- package/cards/dataTable/dataTable.types.d.ts +50 -0
- package/cards/dataTable/dataTable.types.js +6 -3
- package/cards/dataTable/dataTable.types.js.map +1 -1
- package/cards/dataTable/index.js +6 -6
- package/cards/dropDownMenu/dropdown-menu.ui.js +41 -41
- package/cards/field/field.component.js +1 -1
- package/cards/field/field.component.js.map +1 -1
- package/cards/form/form.component.js +57 -32
- package/cards/form/form.component.js.map +1 -1
- package/cards/form/form.context.d.ts +11 -0
- package/cards/form/form.context.js +1 -0
- package/cards/form/form.context.js.map +1 -1
- package/cards/form/form.types.d.ts +75 -0
- package/cards/form/form.types.js +6 -2
- package/cards/form/form.types.js.map +1 -1
- package/cards/form/index.js +5 -5
- package/cards/input/index.js +6 -6
- package/cards/input/input.component.js +38 -32
- package/cards/input/input.component.js.map +1 -1
- package/cards/input/input.types.d.ts +14 -7
- package/cards/input/input.types.js +2 -2
- package/cards/input/input.types.js.map +1 -1
- package/cards/message/index.d.ts +1 -0
- package/cards/message/index.js +15 -0
- package/cards/message/index.js.map +1 -0
- package/cards/message/message.component.d.ts +4 -0
- package/cards/message/message.component.js +47 -0
- package/cards/message/message.component.js.map +1 -0
- package/cards/message/message.types.d.ts +56 -0
- package/cards/message/message.types.js +7 -0
- package/cards/message/message.types.js.map +1 -0
- package/cards/message/messageGroup.component.d.ts +4 -0
- package/cards/message/messageGroup.component.js +21 -0
- package/cards/message/messageGroup.component.js.map +1 -0
- package/cards/pageWithNavbarMeta/index.js +35 -25
- package/cards/pageWithNavbarMeta/index.js.map +1 -1
- package/cards/pageWithNavbarMeta/pageWithNavbarMeta.types.d.ts +1 -4
- package/cards/select/select.types.d.ts +2 -7
- package/cards/select/select.types.js.map +1 -1
- package/cards/switch/switch.types.d.ts +2 -7
- package/cards/switch/switch.types.js.map +1 -1
- package/cards/textArea/textArea.component.js +1 -1
- package/cards/textArea/textArea.component.js.map +1 -1
- package/cards/textArea/textArea.types.d.ts +15 -11
- package/cards/textArea/textArea.types.js.map +1 -1
- package/cards/textField/textField.component.js +13 -9
- package/cards/textField/textField.component.js.map +1 -1
- package/cards/textField/textField.types.d.ts +12 -7
- package/cards/textField/textField.types.js.map +1 -1
- package/cards/toggleGroup/toggleGroup.types.d.ts +2 -7
- package/cards/toggleGroup/toggleGroup.types.js.map +1 -1
- package/components/ui/attachment.js +102 -0
- package/components/ui/attachment.js.map +1 -0
- package/components/ui/badge.js +7 -7
- package/components/ui/bubble.js +73 -0
- package/components/ui/bubble.js.map +1 -0
- package/components/ui/button.js +11 -11
- package/components/ui/empty.js +12 -12
- package/components/ui/field.js +16 -16
- package/components/ui/message.js +51 -0
- package/components/ui/message.js.map +1 -0
- package/components/ui/toggle-group.js +1 -1
- package/components/ui/toggle.js +1 -1
- package/package.json +17 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dataTable.types.js","names":[],"sources":["../../../src/cards/dataTable/dataTable.types.ts"],"sourcesContent":["import React from \"react\";\nimport {\n type PiCardRef,\n createCardDeclaration,\n createOnAction,\n registerActions,\n} from \"@pihanga2/core\";\n\nexport const DATA_TABLE_CARD = \"shad/data-table\";\n\nexport const DataTable = createCardDeclaration<DataTableProps, DataTableEvents>(\n DATA_TABLE_CARD,\n);\n\nexport const DATA_TABLE_ACTION = registerActions(DATA_TABLE_CARD, [\n \"row_clicked\",\n \"sort_changed\",\n \"show_detail\",\n \"hide_detail\",\n \"page_changed\",\n]);\n\nexport const onDataTableRowClicked = createOnAction<DataTableRowClickedEvent>(\n DATA_TABLE_ACTION.ROW_CLICKED,\n);\nexport const onDataTableSortChanged = createOnAction<DataTableSortChangedEvent>(\n DATA_TABLE_ACTION.SORT_CHANGED,\n);\nexport const onDataTableShowDetail = createOnAction<DataTableDetailEvent>(\n DATA_TABLE_ACTION.SHOW_DETAIL,\n);\nexport const onDataTableHideDetail = createOnAction<DataTableDetailEvent>(\n DATA_TABLE_ACTION.HIDE_DETAIL,\n);\nexport const onDataTablePageChanged = createOnAction<DataTablePageChangedEvent>(\n DATA_TABLE_ACTION.PAGE_CHANGED,\n);\n\n// ---------------------------------------------------------------------------\n// Column type definitions\n// ---------------------------------------------------------------------------\n\n/** Shared base for all column definitions */\nexport type DataTableColumnBase = {\n /** Key into the row's `data` object used to retrieve the cell value */\n key: string;\n /**\n * Column header label.\n * Defaults to the `key` with camelCase expanded to \"Title Case\".\n */\n title?: string;\n /** Column width as a CSS value (e.g. \"120px\", \"10%\", \"1fr\") */\n width?: string | number;\n /** Horizontal alignment of cell content. Defaults to \"left\". */\n align?: \"left\" | \"center\" | \"right\";\n /** Whether the column header is clickable for sorting */\n sortable?: boolean;\n /** Extra CSS class applied to the `<th>` header cell */\n headerClassName?: string;\n /**\n * Extra CSS class applied to every `<td>` data cell in this column.\n * May be a static string **or** a function called per-cell:\n * `(value, row) => \"bg-red-500 text-white\"`.\n */\n cellClassName?:\n | string\n | ((value: unknown, row: DataTableRow) => string | undefined);\n /**\n * Inline style applied to every `<td>` data cell in this column.\n * May be a static `React.CSSProperties` object **or** a function called\n * per-cell: `(value, row) => ({ backgroundColor: \"blue\", color: \"white\" })`.\n *\n * @example\n * ```ts\n * cellStyle: (value) => {\n * const map: Record<string, React.CSSProperties> = {\n * Superior: { backgroundColor: \"#4472C4\", color: \"#fff\" },\n * Satisfactory: { backgroundColor: \"#70AD47\", color: \"#fff\" },\n * \"Needs Improvement\": { backgroundColor: \"#FFC000\", color: \"#fff\" },\n * Unsatisfactory: { backgroundColor: \"#FF0000\", color: \"#fff\" },\n * };\n * return map[String(value)];\n * }\n * ```\n */\n cellStyle?:\n | React.CSSProperties\n | ((value: unknown, row: DataTableRow) => React.CSSProperties | undefined);\n};\n\n/** Plain text column — cell value rendered as a string */\nexport type TextColumn = DataTableColumnBase & {\n type?: \"text\";\n};\n\n/** Numeric column with optional custom formatter */\nexport type NumberColumn = DataTableColumnBase & {\n type: \"number\";\n /** Custom number formatter; defaults to `String(value)` */\n format?: (n: number) => string;\n};\n\n/** Badge/chip column — cell value is rendered inside a `<Badge>` */\nexport type BadgeColumn = DataTableColumnBase & {\n type: \"badge\";\n /**\n * Map from cell value (as a string) to a shadcn Badge variant.\n * Falls back to \"secondary\" for unrecognised values.\n */\n variants?: Record<\n string,\n \"default\" | \"secondary\" | \"destructive\" | \"outline\"\n >;\n};\n\n/** Date column with optional custom formatter */\nexport type DateColumn = DataTableColumnBase & {\n type: \"date\";\n /** Custom date formatter; defaults to `toLocaleDateString()` */\n format?: (d: Date | string) => string;\n};\n\n/** Boolean column — renders a check-mark or cross icon */\nexport type BooleanColumn = DataTableColumnBase & {\n type: \"boolean\";\n};\n\n/**\n * Card column — cell value must be a `PiCardRef` (string).\n * The referenced card is rendered inline via Pihanga's `<Card />`.\n * This enables full reuse of any registered Pihanga card as cell content.\n */\nexport type CardColumn = DataTableColumnBase & {\n type: \"card\";\n};\n\nexport type DataTableColumn =\n | TextColumn\n | NumberColumn\n | BadgeColumn\n | DateColumn\n | BooleanColumn\n | CardColumn;\n\n// ---------------------------------------------------------------------------\n// Row\n// ---------------------------------------------------------------------------\n\nexport type DataTableRow<T = Record<string, unknown>> = {\n /** Unique row identifier used as the React key and in events */\n id: string | number;\n /**\n * Row payload — values are keyed by the column's `key` field.\n * For \"card\" columns the value should be a `PiCardRef` string.\n */\n data: T;\n /**\n * When set, the row shows an expand/collapse toggle button.\n * The referenced card is rendered full-width below the row on expand.\n * Use `onShowDetail` / `onHideDetail` events to track expanded state in\n * your redux store and update `detailCard` per row as needed.\n */\n detailCard?: PiCardRef;\n};\n\n// ---------------------------------------------------------------------------\n// Props\n// ---------------------------------------------------------------------------\n\nexport type DataTableProps<T = Record<string, unknown>> = {\n /** Column definitions — order determines display order */\n columns: DataTableColumn[];\n /** Row data */\n rows: DataTableRow<T>[];\n /** Optional visible `<caption>` rendered below the table */\n caption?: string;\n\n // --- Sorting ---\n /**\n * Key of the currently sorted column.\n * When provided the sort state is controlled; otherwise managed internally.\n */\n sortKey?: string;\n /**\n * Sort direction.\n * When provided together with `sortKey` the sort state is controlled.\n */\n sortAscending?: boolean;\n\n // --- Pagination ---\n /** Maximum number of rows per page. Omit to disable pagination. */\n pageSize?: number;\n /** Current page index (0-based). Treated as controlled when provided. */\n currentPage?: number;\n /**\n * Total row count, used for server-side pagination display.\n * Leave unset for client-side pagination derived from `rows.length`.\n */\n totalRows?: number;\n\n // --- Display ---\n /** Keep the header visible while the body scrolls */\n stickyHeader?: boolean;\n /** Alternate row background colour (zebra striping) */\n striped?: boolean;\n /** Highlight rows on hover (default: true) */\n hoverable?: boolean;\n /** Use compact row padding */\n compact?: boolean;\n\n // --- Empty state ---\n /** Card rendered when `rows` is empty */\n emptyCard?: PiCardRef;\n /** Fallback text when `rows` is empty and `emptyCard` is not set */\n emptyText?: string;\n\n className?: string;\n};\n\n// ---------------------------------------------------------------------------\n// Events\n// ---------------------------------------------------------------------------\n\nexport type DataTableRowClickedEvent = {\n rowId: string | number;\n row: DataTableRow;\n};\n\nexport type DataTableSortChangedEvent = {\n /** The column key that was clicked */\n key: string;\n /** `true` = ascending, `false` = descending */\n ascending: boolean;\n};\n\nexport type DataTableDetailEvent = {\n rowId: string | number;\n row: DataTableRow;\n};\n\nexport type DataTablePageChangedEvent = {\n /** New page index (0-based) */\n page: number;\n pageSize: number;\n};\n\nexport type DataTableEvents = {\n onRowClicked: DataTableRowClickedEvent;\n onSortChanged: DataTableSortChangedEvent;\n onShowDetail: DataTableDetailEvent;\n onHideDetail: DataTableDetailEvent;\n onPageChanged: DataTablePageChangedEvent;\n};\n\n// ---------------------------------------------------------------------------\n// DataTableRowDetail — generic single-template detail panel\n// ---------------------------------------------------------------------------\n\n/** Configuration for a single field shown in the detail panel. */\nexport type DataTableRowDetailField = {\n /** Key to look up in `row.data` */\n key: string;\n /** Optional human-readable label prefix (e.g. \"Director\") */\n label?: string;\n /** Render style: \"title\" → h4 bold, \"muted\" → muted foreground, \"text\" (default) → plain */\n type?: \"title\" | \"text\" | \"muted\";\n /** Additional Tailwind class names forwarded to the element */\n className?: string;\n};\n\nexport type DataTableRowDetailProps = {\n /**\n * The row to display. Set this via a state mapper so a single template\n * card works for all rows:\n * ```ts\n * DataTableRowDetail({\n * row: (_, ctx) => ctx.ctxtProps?.row,\n * fields: [{ key: \"title\", type: \"title\" }, ...],\n * })\n * ```\n */\n row?: DataTableRow<Record<string, unknown>>;\n /**\n * Fields to render in order.\n * When omitted, every key in `row.data` is rendered as a plain key:value pair.\n */\n fields?: DataTableRowDetailField[];\n};\n\nexport const DATA_TABLE_ROW_DETAIL_CARD = \"shad/data-table-row-detail\";\nexport const DataTableRowDetail =\n createCardDeclaration<DataTableRowDetailProps>(DATA_TABLE_ROW_DETAIL_CARD);\n"],"mappings":";;;AAQA,IAAa,IAAkB,mBAElB,IAAY,EACvB,CACF,GAEa,IAAoB,EAAgB,GAAiB;CAChE;CACA;CACA;CACA;CACA;AACF,CAAC,GAEY,IAAwB,EACnC,EAAkB,WACpB,GACa,IAAyB,EACpC,EAAkB,YACpB,GACa,IAAwB,EACnC,EAAkB,WACpB,GACa,IAAwB,EACnC,EAAkB,WACpB,GACa,IAAyB,EACpC,EAAkB,YACpB,GA6Pa,IAA6B,8BAC7B,IACX,EAA+C,CAA0B"}
|
|
1
|
+
{"version":3,"file":"dataTable.types.js","names":[],"sources":["../../../src/cards/dataTable/dataTable.types.ts"],"sourcesContent":["import React from \"react\";\nimport {\n type PiCardRef,\n createCardDeclaration,\n createOnAction,\n registerActions,\n} from \"@pihanga2/core\";\n\nexport const DATA_TABLE_CARD = \"shad/data-table\";\n\nexport const DataTable = createCardDeclaration<DataTableProps, DataTableEvents>(\n DATA_TABLE_CARD,\n);\n\nexport const DATA_TABLE_ACTION = registerActions(DATA_TABLE_CARD, [\n \"row_clicked\",\n \"sort_changed\",\n \"show_detail\",\n \"hide_detail\",\n \"page_changed\",\n \"rows_selected\",\n \"all_rows_selected\",\n \"search_changed\",\n]);\n\nexport const onDataTableRowClicked = createOnAction<DataTableRowClickedEvent>(\n DATA_TABLE_ACTION.ROW_CLICKED,\n);\nexport const onDataTableSortChanged = createOnAction<DataTableSortChangedEvent>(\n DATA_TABLE_ACTION.SORT_CHANGED,\n);\nexport const onDataTableShowDetail = createOnAction<DataTableDetailEvent>(\n DATA_TABLE_ACTION.SHOW_DETAIL,\n);\nexport const onDataTableHideDetail = createOnAction<DataTableDetailEvent>(\n DATA_TABLE_ACTION.HIDE_DETAIL,\n);\nexport const onDataTablePageChanged = createOnAction<DataTablePageChangedEvent>(\n DATA_TABLE_ACTION.PAGE_CHANGED,\n);\nexport const onDataTableRowsSelected =\n createOnAction<DataTableRowsSelectedEvent>(\n DATA_TABLE_ACTION.ROWS_SELECTED,\n );\nexport const onDataTableAllRowsSelected =\n createOnAction<DataTableAllRowsSelectedEvent>(\n DATA_TABLE_ACTION.ALL_ROWS_SELECTED,\n );\nexport const onDataTableSearchChanged =\n createOnAction<DataTableSearchChangedEvent>(\n DATA_TABLE_ACTION.SEARCH_CHANGED,\n );\n\n// ---------------------------------------------------------------------------\n// Column type definitions\n// ---------------------------------------------------------------------------\n\n/** Shared base for all column definitions */\nexport type DataTableColumnBase = {\n /** Key into the row's `data` object used to retrieve the cell value */\n key: string;\n /**\n * Column header label.\n * Defaults to the `key` with camelCase expanded to \"Title Case\".\n */\n title?: string;\n /** Column width as a CSS value (e.g. \"120px\", \"10%\", \"1fr\") */\n width?: string | number;\n /** Horizontal alignment of cell content. Defaults to \"left\". */\n align?: \"left\" | \"center\" | \"right\";\n /** Whether the column header is clickable for sorting */\n sortable?: boolean;\n /** Extra CSS class applied to the `<th>` header cell */\n headerClassName?: string;\n /**\n * Extra CSS class applied to every `<td>` data cell in this column.\n * May be a static string **or** a function called per-cell:\n * `(value, row) => \"bg-red-500 text-white\"`.\n */\n cellClassName?:\n | string\n | ((value: unknown, row: DataTableRow) => string | undefined);\n /**\n * Inline style applied to every `<td>` data cell in this column.\n * May be a static `React.CSSProperties` object **or** a function called\n * per-cell: `(value, row) => ({ backgroundColor: \"blue\", color: \"white\" })`.\n *\n * @example\n * ```ts\n * cellStyle: (value) => {\n * const map: Record<string, React.CSSProperties> = {\n * Superior: { backgroundColor: \"#4472C4\", color: \"#fff\" },\n * Satisfactory: { backgroundColor: \"#70AD47\", color: \"#fff\" },\n * \"Needs Improvement\": { backgroundColor: \"#FFC000\", color: \"#fff\" },\n * Unsatisfactory: { backgroundColor: \"#FF0000\", color: \"#fff\" },\n * };\n * return map[String(value)];\n * }\n * ```\n */\n cellStyle?:\n | React.CSSProperties\n | ((value: unknown, row: DataTableRow) => React.CSSProperties | undefined);\n};\n\n/** Plain text column — cell value rendered as a string */\nexport type TextColumn = DataTableColumnBase & {\n type?: \"text\";\n};\n\n/** Numeric column with optional custom formatter */\nexport type NumberColumn = DataTableColumnBase & {\n type: \"number\";\n /** Custom number formatter; defaults to `String(value)` */\n format?: (n: number) => string;\n};\n\n/** Badge/chip column — cell value is rendered inside a `<Badge>` */\nexport type BadgeColumn = DataTableColumnBase & {\n type: \"badge\";\n /**\n * Map from cell value (as a string) to a shadcn Badge variant.\n * Falls back to \"secondary\" for unrecognised values.\n */\n variants?: Record<\n string,\n \"default\" | \"secondary\" | \"destructive\" | \"outline\"\n >;\n};\n\n/** Date column with optional custom formatter */\nexport type DateColumn = DataTableColumnBase & {\n type: \"date\";\n /** Custom date formatter; defaults to `toLocaleDateString()` */\n format?: (d: Date | string) => string;\n};\n\n/** Boolean column — renders a check-mark or cross icon */\nexport type BooleanColumn = DataTableColumnBase & {\n type: \"boolean\";\n};\n\n/**\n * Card column — cell value must be a `PiCardRef` (string).\n * The referenced card is rendered inline via Pihanga's `<Card />`.\n * This enables full reuse of any registered Pihanga card as cell content.\n */\nexport type CardColumn = DataTableColumnBase & {\n type: \"card\";\n};\n\nexport type DataTableColumn =\n | TextColumn\n | NumberColumn\n | BadgeColumn\n | DateColumn\n | BooleanColumn\n | CardColumn;\n\n// ---------------------------------------------------------------------------\n// Row\n// ---------------------------------------------------------------------------\n\nexport type DataTableRow<T = Record<string, unknown>> = {\n /** Unique row identifier used as the React key and in events */\n id: string | number;\n /**\n * Row payload — values are keyed by the column's `key` field.\n * For \"card\" columns the value should be a `PiCardRef` string.\n */\n data: T;\n /**\n * When set, the row shows an expand/collapse toggle button.\n * The referenced card is rendered full-width below the row on expand.\n * Use `onShowDetail` / `onHideDetail` events to track expanded state in\n * your redux store and update `detailCard` per row as needed.\n */\n detailCard?: PiCardRef;\n};\n\n// ---------------------------------------------------------------------------\n// Props\n// ---------------------------------------------------------------------------\n\nexport type DataTableProps<T = Record<string, unknown>> = {\n /** Column definitions — order determines display order */\n columns: DataTableColumn[];\n /** Row data */\n rows: DataTableRow<T>[];\n /** Optional visible `<caption>` rendered below the table */\n caption?: string;\n\n // --- Sorting ---\n /**\n * Key of the currently sorted column.\n * When provided the sort state is controlled; otherwise managed internally.\n */\n sortKey?: string;\n /**\n * Sort direction.\n * When provided together with `sortKey` the sort state is controlled.\n */\n sortAscending?: boolean;\n\n // --- Pagination ---\n /** Maximum number of rows per page. Omit to disable pagination. */\n pageSize?: number;\n /** Current page index (0-based). Treated as controlled when provided. */\n currentPage?: number;\n /**\n * Total row count, used for server-side pagination display.\n * Leave unset for client-side pagination derived from `rows.length`.\n */\n totalRows?: number;\n\n // --- Display ---\n /** Keep the header visible while the body scrolls */\n stickyHeader?: boolean;\n /** Alternate row background colour (zebra striping) */\n striped?: boolean;\n /** Highlight rows on hover (default: true) */\n hoverable?: boolean;\n /** Use compact row padding */\n compact?: boolean;\n\n // --- Empty state ---\n /** Card rendered when `rows` is empty */\n emptyCard?: PiCardRef;\n /** Fallback text when `rows` is empty and `emptyCard` is not set */\n emptyText?: string;\n\n // --- Row selection ---\n /**\n * When true, a checkbox column is rendered so rows can be selected.\n * Selection state is uncontrolled unless `selectedRowIds` is provided.\n */\n selectable?: boolean;\n /**\n * Ids of the currently selected rows.\n * When provided, selection becomes controlled — pair with `onRowsSelected`\n * to update it from your redux store.\n */\n selectedRowIds?: (string | number)[];\n\n // --- Search ---\n /** Show a search input above the table that filters rows client-side. */\n showSearch?: boolean;\n /** Placeholder text for the search input. */\n searchPlaceholder?: string;\n /**\n * Current search text.\n * When provided the search box becomes controlled — pair with\n * `onSearchChanged` to filter `rows` yourself (e.g. server-side).\n */\n searchText?: string;\n\n className?: string;\n};\n\n// ---------------------------------------------------------------------------\n// Events\n// ---------------------------------------------------------------------------\n\nexport type DataTableRowClickedEvent = {\n rowId: string | number;\n row: DataTableRow;\n};\n\nexport type DataTableSortChangedEvent = {\n /** The column key that was clicked */\n key: string;\n /** `true` = ascending, `false` = descending */\n ascending: boolean;\n};\n\nexport type DataTableDetailEvent = {\n rowId: string | number;\n row: DataTableRow;\n};\n\nexport type DataTablePageChangedEvent = {\n /** New page index (0-based) */\n page: number;\n pageSize: number;\n};\n\nexport type DataTableRowsSelectedEvent<T = Record<string, unknown>> = {\n /** All currently-selected rows (on the current page unless server-driven) */\n selected: DataTableRow<T>[];\n};\n\nexport type DataTableAllRowsSelectedEvent<T = Record<string, unknown>> = {\n /** Whether the header checkbox selected (true) or cleared (false) all rows */\n selected: boolean;\n /** Rows affected by the select-all toggle (visible/paginated rows) */\n rows: DataTableRow<T>[];\n};\n\nexport type DataTableSearchChangedEvent = {\n /** New search text */\n text: string;\n};\n\nexport type DataTableEvents = {\n onRowClicked: DataTableRowClickedEvent;\n onSortChanged: DataTableSortChangedEvent;\n onShowDetail: DataTableDetailEvent;\n onHideDetail: DataTableDetailEvent;\n onPageChanged: DataTablePageChangedEvent;\n onRowsSelected: DataTableRowsSelectedEvent;\n onAllRowsSelected: DataTableAllRowsSelectedEvent;\n onSearchChanged: DataTableSearchChangedEvent;\n};\n\n// ---------------------------------------------------------------------------\n// DataTableRowDetail — generic single-template detail panel\n// ---------------------------------------------------------------------------\n\n/** Configuration for a single field shown in the detail panel. */\nexport type DataTableRowDetailField = {\n /** Key to look up in `row.data` */\n key: string;\n /** Optional human-readable label prefix (e.g. \"Director\") */\n label?: string;\n /** Render style: \"title\" → h4 bold, \"muted\" → muted foreground, \"text\" (default) → plain */\n type?: \"title\" | \"text\" | \"muted\";\n /** Additional Tailwind class names forwarded to the element */\n className?: string;\n};\n\nexport type DataTableRowDetailProps = {\n /**\n * The row to display. Set this via a state mapper so a single template\n * card works for all rows:\n * ```ts\n * DataTableRowDetail({\n * row: (_, ctx) => ctx.ctxtProps?.row,\n * fields: [{ key: \"title\", type: \"title\" }, ...],\n * })\n * ```\n */\n row?: DataTableRow<Record<string, unknown>>;\n /**\n * Fields to render in order.\n * When omitted, every key in `row.data` is rendered as a plain key:value pair.\n */\n fields?: DataTableRowDetailField[];\n};\n\nexport const DATA_TABLE_ROW_DETAIL_CARD = \"shad/data-table-row-detail\";\nexport const DataTableRowDetail =\n createCardDeclaration<DataTableRowDetailProps>(DATA_TABLE_ROW_DETAIL_CARD);\n"],"mappings":";;;AAQA,IAAa,IAAkB,mBAElB,IAAY,EACvB,CACF,GAEa,IAAoB,EAAgB,GAAiB;CAChE;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF,CAAC,GAEY,IAAwB,EACnC,EAAkB,WACpB,GACa,IAAyB,EACpC,EAAkB,YACpB,GACa,IAAwB,EACnC,EAAkB,WACpB,GACa,IAAwB,EACnC,EAAkB,WACpB,GACa,IAAyB,EACpC,EAAkB,YACpB,GACa,IACX,EACE,EAAkB,aACpB,GACW,IACX,EACE,EAAkB,iBACpB,GACW,IACX,EACE,EAAkB,cACpB,GA0SW,IAA6B,8BAC7B,IACX,EAA+C,CAA0B"}
|
package/cards/dataTable/index.js
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import { DataTableComponent as e } from "./dataTable.component.js";
|
|
2
2
|
import { DataTableRowDetailComponent as t } from "./dataTableRowDetail.component.js";
|
|
3
|
-
import { DATA_TABLE_ACTION as n, DATA_TABLE_CARD as r, DATA_TABLE_ROW_DETAIL_CARD as i, DataTable as a, DataTableRowDetail as o,
|
|
4
|
-
import { actionTypesToEvents as
|
|
5
|
-
|
|
3
|
+
import { DATA_TABLE_ACTION as n, DATA_TABLE_CARD as r, DATA_TABLE_ROW_DETAIL_CARD as i, DataTable as a, DataTableRowDetail as o, onDataTableAllRowsSelected as s, onDataTableHideDetail as c, onDataTablePageChanged as l, onDataTableRowClicked as u, onDataTableRowsSelected as d, onDataTableSearchChanged as f, onDataTableShowDetail as p, onDataTableSortChanged as m } from "./dataTable.types.js";
|
|
4
|
+
import { actionTypesToEvents as h, registerCardComponent as g } from "@pihanga2/core";
|
|
5
|
+
g({
|
|
6
6
|
name: r,
|
|
7
7
|
component: e,
|
|
8
|
-
events:
|
|
9
|
-
}),
|
|
8
|
+
events: h(n)
|
|
9
|
+
}), g({
|
|
10
10
|
name: i,
|
|
11
11
|
component: t
|
|
12
12
|
});
|
|
13
13
|
//#endregion
|
|
14
|
-
export { n as DATA_TABLE_ACTION, r as DATA_TABLE_CARD, i as DATA_TABLE_ROW_DETAIL_CARD, a as DataTable, o as DataTableRowDetail, s as
|
|
14
|
+
export { n as DATA_TABLE_ACTION, r as DATA_TABLE_CARD, i as DATA_TABLE_ROW_DETAIL_CARD, a as DataTable, o as DataTableRowDetail, s as onDataTableAllRowsSelected, c as onDataTableHideDetail, l as onDataTablePageChanged, u as onDataTableRowClicked, d as onDataTableRowsSelected, f as onDataTableSearchChanged, p as onDataTableShowDetail, m as onDataTableSortChanged };
|
|
15
15
|
|
|
16
16
|
//# sourceMappingURL=index.js.map
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { cn as e } from "../../lib/utils.js";
|
|
3
3
|
import "react";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
4
|
+
import { cva as t } from "class-variance-authority";
|
|
5
|
+
import { jsx as n, jsxs as r } from "react/jsx-runtime";
|
|
6
6
|
import { CheckIcon as i, ChevronRightIcon as a } from "lucide-react";
|
|
7
7
|
import * as o from "@radix-ui/react-dropdown-menu";
|
|
8
8
|
//#region src/cards/dropDownMenu/dropdown-menu.ui.tsx
|
|
9
|
-
var s =
|
|
9
|
+
var s = t(e("relative flex cursor-pointer items-center gap-2 rounded-md align-middle text-sm no-focus-ring transition-bg-ease select-none data-disabled:pointer-events-none data-disabled:opacity-50", "[&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 [&_svg]:text-subtle-foreground", "text-accent-foreground hover:bg-accent focus:bg-accent focus:text-accent-foreground"), {
|
|
10
10
|
defaultVariants: {
|
|
11
11
|
size: "default",
|
|
12
12
|
variant: "default"
|
|
@@ -21,87 +21,87 @@ var s = r(e("relative flex cursor-pointer items-center gap-2 rounded-md align-mi
|
|
|
21
21
|
none: ""
|
|
22
22
|
}
|
|
23
23
|
}
|
|
24
|
-
}), c =
|
|
24
|
+
}), c = t(e("mt-1.5 mb-2 cursor-default px-[14px] text-xs font-medium text-muted-foreground select-none"), { variants: { inset: { true: "pl-8" } } });
|
|
25
25
|
function l(e) {
|
|
26
|
-
return /* @__PURE__ */
|
|
26
|
+
return /* @__PURE__ */ n(o.Root, { ...e });
|
|
27
27
|
}
|
|
28
28
|
function u(e) {
|
|
29
|
-
return /* @__PURE__ */
|
|
29
|
+
return /* @__PURE__ */ n(o.Trigger, { ...e });
|
|
30
30
|
}
|
|
31
31
|
function d(e) {
|
|
32
|
-
return /* @__PURE__ */
|
|
32
|
+
return /* @__PURE__ */ n(o.Group, {
|
|
33
33
|
className: "py-1.5",
|
|
34
34
|
...e
|
|
35
35
|
});
|
|
36
36
|
}
|
|
37
37
|
function f(e) {
|
|
38
|
-
return /* @__PURE__ */
|
|
38
|
+
return /* @__PURE__ */ n(o.Sub, { ...e });
|
|
39
39
|
}
|
|
40
40
|
function p(e) {
|
|
41
|
-
return /* @__PURE__ */
|
|
41
|
+
return /* @__PURE__ */ n(o.RadioGroup, { ...e });
|
|
42
42
|
}
|
|
43
|
-
function m({ children:
|
|
44
|
-
return /* @__PURE__ */
|
|
43
|
+
function m({ children: t, className: i, inset: s, ...c }) {
|
|
44
|
+
return /* @__PURE__ */ r(o.SubTrigger, {
|
|
45
45
|
className: e("mx-1 flex cursor-default items-center gap-2 rounded-md px-2 py-1.5 text-sm outline-hidden select-none focus:bg-accent data-[state=open]:bg-accent", "no-focus-ring", "data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0", s && "pl-8", i),
|
|
46
46
|
...c,
|
|
47
|
-
children: [
|
|
47
|
+
children: [t, /* @__PURE__ */ n(a, { className: "ml-auto" })]
|
|
48
48
|
});
|
|
49
49
|
}
|
|
50
|
-
function h({ className:
|
|
51
|
-
return /* @__PURE__ */
|
|
52
|
-
className: e("z-50 max-w-[100vw] min-w-32 w-max overflow-hidden rounded-lg bg-popover p-1 text-popover-foreground shadow-floating data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95",
|
|
50
|
+
function h({ className: t, ...r }) {
|
|
51
|
+
return /* @__PURE__ */ n(o.SubContent, {
|
|
52
|
+
className: e("z-50 max-w-[100vw] min-w-32 w-max overflow-hidden rounded-lg bg-popover p-1 text-popover-foreground shadow-floating data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95", t),
|
|
53
53
|
...r
|
|
54
54
|
});
|
|
55
55
|
}
|
|
56
|
-
function g({ className:
|
|
57
|
-
let a = /* @__PURE__ */
|
|
58
|
-
className: e("z-50 max-w-[100vw] min-w-32 w-max overflow-hidden rounded-lg bg-popover p-0 text-sm text-popover-foreground shadow-floating no-focus-ring", "data-[side=bottom]:origin-top data-[side=left]:origin-right data-[side=right]:origin-left data-[side=top]:origin-bottom data-[state=closed]:hidden data-[state=open]:animate-zoom",
|
|
56
|
+
function g({ className: t, portal: r, ...i }) {
|
|
57
|
+
let a = /* @__PURE__ */ n(o.Content, {
|
|
58
|
+
className: e("z-50 max-w-[100vw] min-w-32 w-max overflow-hidden rounded-lg bg-popover p-0 text-sm text-popover-foreground shadow-floating no-focus-ring", "data-[side=bottom]:origin-top data-[side=left]:origin-right data-[side=right]:origin-left data-[side=top]:origin-bottom data-[state=closed]:hidden data-[state=open]:animate-zoom", t),
|
|
59
59
|
sideOffset: 4,
|
|
60
60
|
...i
|
|
61
61
|
});
|
|
62
|
-
return r ? /* @__PURE__ */
|
|
62
|
+
return r ? /* @__PURE__ */ n(o.Portal, { children: a }) : a;
|
|
63
63
|
}
|
|
64
|
-
function _({ className:
|
|
65
|
-
return /* @__PURE__ */
|
|
66
|
-
className: e(s(),
|
|
64
|
+
function _({ className: t, ...r }) {
|
|
65
|
+
return /* @__PURE__ */ n(o.Item, {
|
|
66
|
+
className: e(s(), t),
|
|
67
67
|
...r
|
|
68
68
|
});
|
|
69
69
|
}
|
|
70
|
-
function v({ children:
|
|
71
|
-
return /* @__PURE__ */
|
|
70
|
+
function v({ children: t, className: a, ...s }) {
|
|
71
|
+
return /* @__PURE__ */ r(o.CheckboxItem, {
|
|
72
72
|
className: e("relative flex items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 no-focus-ring transition-bg-ease select-none focus:bg-accent focus:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:size-4", "mx-1 h-[28px] cursor-pointer data-[state=highlighted]:bg-accent data-[state=highlighted]:text-accent-foreground", a),
|
|
73
73
|
...s,
|
|
74
|
-
children: [/* @__PURE__ */
|
|
74
|
+
children: [/* @__PURE__ */ n("span", {
|
|
75
75
|
className: "absolute right-2 flex size-3.5 items-center justify-center",
|
|
76
|
-
children: /* @__PURE__ */
|
|
77
|
-
}),
|
|
76
|
+
children: /* @__PURE__ */ n(o.ItemIndicator, { children: /* @__PURE__ */ n(i, {}) })
|
|
77
|
+
}), t]
|
|
78
78
|
});
|
|
79
79
|
}
|
|
80
|
-
function y({ children:
|
|
81
|
-
return /* @__PURE__ */
|
|
80
|
+
function y({ children: t, className: a, hideIcon: s, ...c }) {
|
|
81
|
+
return /* @__PURE__ */ r(o.RadioItem, {
|
|
82
82
|
className: e("relative flex items-center rounded-sm pr-2 pl-8 no-focus-ring transition-bg-ease select-none focus:bg-accent focus:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50", "mx-1 h-[28px] cursor-pointer gap-2 data-[state=highlighted]:bg-accent data-[state=highlighted]:text-accent-foreground [&_svg]:size-4", a),
|
|
83
83
|
...c,
|
|
84
|
-
children: [!s && /* @__PURE__ */
|
|
84
|
+
children: [!s && /* @__PURE__ */ n("span", {
|
|
85
85
|
className: "absolute left-2 flex size-3.5 items-center justify-center",
|
|
86
|
-
children: /* @__PURE__ */
|
|
87
|
-
}),
|
|
86
|
+
children: /* @__PURE__ */ n(o.ItemIndicator, { children: /* @__PURE__ */ n(i, {}) })
|
|
87
|
+
}), t]
|
|
88
88
|
});
|
|
89
89
|
}
|
|
90
|
-
function b({ className:
|
|
91
|
-
return /* @__PURE__ */
|
|
92
|
-
className: e(c({ inset: r }),
|
|
90
|
+
function b({ className: t, inset: r, ...i }) {
|
|
91
|
+
return /* @__PURE__ */ n(o.Label, {
|
|
92
|
+
className: e(c({ inset: r }), t),
|
|
93
93
|
...i
|
|
94
94
|
});
|
|
95
95
|
}
|
|
96
|
-
function x({ className:
|
|
97
|
-
return /* @__PURE__ */
|
|
98
|
-
className: e("-mx-1 my-1 h-px bg-muted",
|
|
96
|
+
function x({ className: t, ...r }) {
|
|
97
|
+
return /* @__PURE__ */ n(o.Separator, {
|
|
98
|
+
className: e("-mx-1 my-1 h-px bg-muted", t),
|
|
99
99
|
...r
|
|
100
100
|
});
|
|
101
101
|
}
|
|
102
|
-
function S({ className:
|
|
103
|
-
return /* @__PURE__ */
|
|
104
|
-
className: e("ml-auto text-xs tracking-widest opacity-60",
|
|
102
|
+
function S({ className: t, ...r }) {
|
|
103
|
+
return /* @__PURE__ */ n("span", {
|
|
104
|
+
className: e("ml-auto text-xs tracking-widest opacity-60", t),
|
|
105
105
|
...r
|
|
106
106
|
});
|
|
107
107
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"field.component.js","names":[],"sources":["../../../src/cards/field/field.component.tsx"],"sourcesContent":["import React, {useMemo} from \"react\";\nimport {Card, type PiCardProps} from \"@pihanga2/core\";\nimport {\n Field,\n FieldDescription,\n FieldError,\n FieldLabel,\n} from \"@/components/ui/field\";\nimport {cn} from \"@/lib/utils\";\nimport {useFormContext} from \"@/cards/form/form.context\";\nimport type {PiFieldProps} from \"./field.types\";\n\nexport const FieldCardComponent = (\n props: PiCardProps<PiFieldProps>,\n): React.ReactNode => {\n const {\n label,\n fieldCard,\n name,\n description,\n error: propError,\n className,\n cardName,\n } = props;\n\n // When name is provided and we're inside a pi/form, use the form error.\n const form = useFormContext();\n const error = form.isInForm && name ? form.errors[name] : propError;\n\n // Stable id that links <FieldLabel htmlFor> to the inner control's id.\n const fieldId = React.useId();\n\n // Memoize the extra props forwarded to the inner card so that Pihanga does\n // not see a brand-new object on every render (which would cause it to treat\n // the inner card as a new/changed card and unmount/remount it).\n const isInvalid = Boolean(error);\n /*\n Pihanga's Card forwards every prop other than cardName / parentCard to\n the bound component. We inject `id` (so the control's element gets\n the id that FieldLabel points at) and `invalid` (so the control can\n set aria-invalid without needing its own error/label logic).\n */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const fieldCardProps = useMemo<any>(\n () => ({\n cardName: fieldCard,\n parentCard: cardName,\n id: fieldId,\n invalid: isInvalid,\n }),\n [cardName, fieldCard, fieldId, isInvalid],\n );\n\n return (\n <Field\n data-invalid={Boolean(error) || undefined}\n className={cn(className)}\n data-pihanga={cardName}\n >\n <FieldLabel htmlFor={fieldId}>{label}</FieldLabel
|
|
1
|
+
{"version":3,"file":"field.component.js","names":[],"sources":["../../../src/cards/field/field.component.tsx"],"sourcesContent":["import React, {useMemo} from \"react\";\nimport {Card, type PiCardProps} from \"@pihanga2/core\";\nimport {\n Field,\n FieldDescription,\n FieldError,\n FieldLabel,\n} from \"@/components/ui/field\";\nimport {cn} from \"@/lib/utils\";\nimport {useFormContext} from \"@/cards/form/form.context\";\nimport type {PiFieldProps} from \"./field.types\";\n\nexport const FieldCardComponent = (\n props: PiCardProps<PiFieldProps>,\n): React.ReactNode => {\n const {\n label,\n fieldCard,\n name,\n description,\n error: propError,\n className,\n cardName,\n } = props;\n\n // When name is provided and we're inside a pi/form, use the form error.\n const form = useFormContext();\n const error = form.isInForm && name ? form.errors[name] : propError;\n\n // Stable id that links <FieldLabel htmlFor> to the inner control's id.\n const fieldId = React.useId();\n\n // Memoize the extra props forwarded to the inner card so that Pihanga does\n // not see a brand-new object on every render (which would cause it to treat\n // the inner card as a new/changed card and unmount/remount it).\n const isInvalid = Boolean(error);\n /*\n Pihanga's Card forwards every prop other than cardName / parentCard to\n the bound component. We inject `id` (so the control's element gets\n the id that FieldLabel points at) and `invalid` (so the control can\n set aria-invalid without needing its own error/label logic).\n */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const fieldCardProps = useMemo<any>(\n () => ({\n cardName: fieldCard,\n parentCard: cardName,\n id: fieldId,\n invalid: isInvalid,\n }),\n [cardName, fieldCard, fieldId, isInvalid],\n );\n\n return (\n <Field\n data-invalid={Boolean(error) || undefined}\n className={cn(className)}\n data-pihanga={cardName}\n >\n {label && <FieldLabel htmlFor={fieldId}>{label}</FieldLabel>}\n\n <Card {...fieldCardProps} />\n\n {description && <FieldDescription>{description}</FieldDescription>}\n <FieldError>{error}</FieldError>\n </Field>\n );\n};\n"],"mappings":";;;;;;;AAYA,IAAa,KACX,MACoB;CACpB,IAAM,EACJ,UACA,cACA,SACA,gBACA,OAAO,GACP,cACA,gBACE,GAGE,IAAO,EAAe,GACtB,IAAQ,EAAK,YAAY,IAAO,EAAK,OAAO,KAAQ,GAGpD,IAAU,EAAM,MAAM,GAKtB,IAAY,EAAQ,GAQpB,IAAiB,SACd;EACL,UAAU;EACV,YAAY;EACZ,IAAI;EACJ,SAAS;CACX,IACA;EAAC;EAAU;EAAW;EAAS;CAAS,CAC1C;CAEA,OACE,kBAAC,GAAD;EACE,gBAAc,EAAQ,KAAU,KAAA;EAChC,WAAW,EAAG,CAAS;EACvB,gBAAc;YAHhB;GAKG,KAAS,kBAAC,GAAD;IAAY,SAAS;cAAU;GAAkB,CAAA;GAE3D,kBAAC,GAAD,EAAM,GAAI,EAAiB,CAAA;GAE1B,KAAe,kBAAC,GAAD,EAAA,UAAmB,EAA8B,CAAA;GACjE,kBAAC,GAAD,EAAA,UAAa,EAAkB,CAAA;EAC1B;;AAEX"}
|
|
@@ -1,21 +1,32 @@
|
|
|
1
1
|
import { cn as e } from "../../lib/utils.js";
|
|
2
2
|
import { FormContext as t } from "./form.context.js";
|
|
3
|
-
import { useCallback as n,
|
|
4
|
-
import { Card as
|
|
5
|
-
import { jsx as
|
|
3
|
+
import { useCallback as n, useEffect as r, useRef as i, useState as a } from "react";
|
|
4
|
+
import { Card as o } from "@pihanga2/core";
|
|
5
|
+
import { jsx as s, jsxs as c } from "react/jsx-runtime";
|
|
6
6
|
//#region src/cards/form/form.component.tsx
|
|
7
|
-
var
|
|
8
|
-
let { id:
|
|
9
|
-
|
|
7
|
+
var l = (l) => {
|
|
8
|
+
let { id: u, content: d = [], initialValues: f = {}, submitLabel: p = "Submit", hideSubmit: m = !1, className: h, cardName: g, onSubmitted: _, onCancelled: v, onAbandoned: y } = l, [b, x] = a(() => ({ ...f })), [S, C] = a({}), w = i(!1), T = i(u);
|
|
9
|
+
T.current = u;
|
|
10
|
+
let E = i(b);
|
|
11
|
+
E.current = b;
|
|
12
|
+
let D = i(y);
|
|
13
|
+
D.current = y, r(() => () => {
|
|
14
|
+
w.current || D.current({
|
|
15
|
+
id: T.current,
|
|
16
|
+
formData: E.current
|
|
17
|
+
});
|
|
18
|
+
}, []);
|
|
19
|
+
let O = n((e, t) => {
|
|
20
|
+
x((n) => ({
|
|
10
21
|
...n,
|
|
11
22
|
[e]: t
|
|
12
|
-
})),
|
|
23
|
+
})), C((t) => {
|
|
13
24
|
if (!t[e]) return t;
|
|
14
25
|
let n = { ...t };
|
|
15
26
|
return delete n[e], n;
|
|
16
27
|
});
|
|
17
|
-
}, []),
|
|
18
|
-
|
|
28
|
+
}, []), k = n((e, t) => {
|
|
29
|
+
C((n) => {
|
|
19
30
|
if (t === null) {
|
|
20
31
|
let t = { ...n };
|
|
21
32
|
return delete t[e], t;
|
|
@@ -25,40 +36,54 @@ var s = (s) => {
|
|
|
25
36
|
[e]: t
|
|
26
37
|
};
|
|
27
38
|
});
|
|
28
|
-
}, []),
|
|
29
|
-
|
|
30
|
-
id:
|
|
31
|
-
formData:
|
|
39
|
+
}, []), A = n(() => {
|
|
40
|
+
w.current = !0, _({
|
|
41
|
+
id: u,
|
|
42
|
+
formData: b
|
|
43
|
+
});
|
|
44
|
+
}, [
|
|
45
|
+
u,
|
|
46
|
+
b,
|
|
47
|
+
_
|
|
48
|
+
]), j = n(() => {
|
|
49
|
+
w.current = !0, v({
|
|
50
|
+
id: u,
|
|
51
|
+
formData: b
|
|
32
52
|
});
|
|
33
53
|
}, [
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
]),
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
54
|
+
u,
|
|
55
|
+
b,
|
|
56
|
+
v
|
|
57
|
+
]), M = n((e) => {
|
|
58
|
+
e.preventDefault(), A();
|
|
59
|
+
}, [A]), N = {
|
|
60
|
+
formData: b,
|
|
61
|
+
errors: S,
|
|
62
|
+
handleChange: O,
|
|
63
|
+
setError: k,
|
|
64
|
+
requestAction: n((e) => {
|
|
65
|
+
e === "submit" ? A() : j();
|
|
66
|
+
}, [A, j]),
|
|
42
67
|
isInForm: !0
|
|
43
68
|
};
|
|
44
|
-
return /* @__PURE__ */
|
|
45
|
-
value:
|
|
46
|
-
children: /* @__PURE__ */
|
|
47
|
-
onSubmit:
|
|
48
|
-
className: e("flex flex-col gap-4",
|
|
49
|
-
"data-pihanga":
|
|
50
|
-
children: [
|
|
69
|
+
return /* @__PURE__ */ s(t.Provider, {
|
|
70
|
+
value: N,
|
|
71
|
+
children: /* @__PURE__ */ c("form", {
|
|
72
|
+
onSubmit: M,
|
|
73
|
+
className: e("flex flex-col gap-4", h),
|
|
74
|
+
"data-pihanga": g,
|
|
75
|
+
children: [d.map((e, t) => /* @__PURE__ */ s(o, {
|
|
51
76
|
cardName: e,
|
|
52
|
-
parentCard:
|
|
53
|
-
}, t)), /* @__PURE__ */
|
|
77
|
+
parentCard: g
|
|
78
|
+
}, t)), !m && /* @__PURE__ */ s("button", {
|
|
54
79
|
type: "submit",
|
|
55
80
|
className: "inline-flex h-9 items-center justify-center rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground shadow transition-colors hover:bg-primary/90 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50",
|
|
56
|
-
children:
|
|
81
|
+
children: p
|
|
57
82
|
})]
|
|
58
83
|
})
|
|
59
84
|
});
|
|
60
85
|
};
|
|
61
86
|
//#endregion
|
|
62
|
-
export {
|
|
87
|
+
export { l as FormComponent };
|
|
63
88
|
|
|
64
89
|
//# sourceMappingURL=form.component.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"form.component.js","names":[],"sources":["../../../src/cards/form/form.component.tsx"],"sourcesContent":["import React, {useState, useCallback} from \"react\";\nimport {Card, type PiCardProps} from \"@pihanga2/core\";\nimport {cn} from \"@/lib/utils\";\nimport {FormContext, type FormContextValue} from \"./form.context\";\nimport type {PiFormEvents, PiFormProps} from \"./form.types\";\n\nexport const FormComponent = (\n props: PiCardProps<PiFormProps, PiFormEvents>,\n): React.ReactNode => {\n const {\n id,\n content = [],\n initialValues = {},\n submitLabel = \"Submit\",\n className,\n cardName,\n onSubmitted,\n } = props;\n\n const [formData, setFormData] = useState<Record<string, unknown>>(() => ({\n ...initialValues,\n }));\n const [errors, setErrors] = useState<Record<string, string>>({});\n\n const handleChange = useCallback((fieldName: string, value: unknown) => {\n setFormData((prev) => ({...prev, [fieldName]: value}));\n // Clear validation error when the user modifies the field\n setErrors((prev) => {\n if (!prev[fieldName]) return prev;\n const next = {...prev};\n delete next[fieldName];\n return next;\n });\n }, []);\n\n const setError = useCallback((fieldName: string, error: string | null) => {\n setErrors((prev) => {\n if (error === null) {\n const next = {...prev};\n delete next[fieldName];\n return next;\n }\n return {...prev, [fieldName]: error};\n });\n }, []);\n\n const handleSubmit = useCallback(\n (e: React.FormEvent) => {\n e.preventDefault();\n
|
|
1
|
+
{"version":3,"file":"form.component.js","names":[],"sources":["../../../src/cards/form/form.component.tsx"],"sourcesContent":["import React, {useState, useCallback, useRef, useEffect} from \"react\";\nimport {Card, type PiCardProps} from \"@pihanga2/core\";\nimport {cn} from \"@/lib/utils\";\nimport {FormContext, type FormContextValue} from \"./form.context\";\nimport type {FormAction, PiFormEvents, PiFormProps} from \"./form.types\";\n\nexport const FormComponent = (\n props: PiCardProps<PiFormProps, PiFormEvents>,\n): React.ReactNode => {\n const {\n id,\n content = [],\n initialValues = {},\n submitLabel = \"Submit\",\n hideSubmit = false,\n className,\n cardName,\n onSubmitted,\n onCancelled,\n onAbandoned,\n } = props;\n\n const [formData, setFormData] = useState<Record<string, unknown>>(() => ({\n ...initialValues,\n }));\n const [errors, setErrors] = useState<Record<string, string>>({});\n\n // Tracks whether the form reached a \"final\" state (submitted or\n // explicitly cancelled) so the unmount effect below can tell the\n // difference between that and being torn down some other way (e.g. its\n // parent pi/dialog was dismissed) — the latter fires `onAbandoned`.\n const finalizedRef = useRef(false);\n // Mirrors state in refs so the unmount effect (which only runs once) can\n // read the latest values without re-subscribing on every keystroke.\n const idRef = useRef(id);\n idRef.current = id;\n const formDataRef = useRef(formData);\n formDataRef.current = formData;\n const onAbandonedRef = useRef(onAbandoned);\n onAbandonedRef.current = onAbandoned;\n\n useEffect(() => {\n return () => {\n if (!finalizedRef.current) {\n onAbandonedRef.current({id: idRef.current, formData: formDataRef.current});\n }\n };\n // Intentionally run only on mount/unmount.\n \n }, []);\n\n const handleChange = useCallback((fieldName: string, value: unknown) => {\n setFormData((prev) => ({...prev, [fieldName]: value}));\n // Clear validation error when the user modifies the field\n setErrors((prev) => {\n if (!prev[fieldName]) return prev;\n const next = {...prev};\n delete next[fieldName];\n return next;\n });\n }, []);\n\n const setError = useCallback((fieldName: string, error: string | null) => {\n setErrors((prev) => {\n if (error === null) {\n const next = {...prev};\n delete next[fieldName];\n return next;\n }\n return {...prev, [fieldName]: error};\n });\n }, []);\n\n const doSubmit = useCallback(() => {\n finalizedRef.current = true;\n onSubmitted({id, formData});\n }, [id, formData, onSubmitted]);\n\n const doCancel = useCallback(() => {\n finalizedRef.current = true;\n onCancelled({id, formData});\n }, [id, formData, onCancelled]);\n\n const handleSubmit = useCallback(\n (e: React.FormEvent) => {\n e.preventDefault();\n doSubmit();\n },\n [doSubmit],\n );\n\n // Single entry point for any control nested in the form's content to\n // trigger a top-level form action (Button's `formAction`, a field's\n // `submitOnEnter`, etc.) — keeps all form-bindable controls consistent.\n const requestAction = useCallback(\n (action: FormAction) => {\n if (action === \"submit\") {\n doSubmit();\n } else {\n doCancel();\n }\n },\n [doSubmit, doCancel],\n );\n\n const contextValue: FormContextValue = {\n formData,\n errors,\n handleChange,\n setError,\n requestAction,\n isInForm: true,\n };\n\n return (\n <FormContext.Provider value={contextValue}>\n <form\n onSubmit={handleSubmit}\n className={cn(\"flex flex-col gap-4\", className)}\n data-pihanga={cardName}\n >\n {content.map((cref, idx) => (\n <Card key={idx} cardName={cref} parentCard={cardName} />\n ))}\n {!hideSubmit && (\n <button\n type=\"submit\"\n className=\"inline-flex h-9 items-center justify-center rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground shadow transition-colors hover:bg-primary/90 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50\"\n >\n {submitLabel}\n </button>\n )}\n </form>\n </FormContext.Provider>\n );\n};\n"],"mappings":";;;;;;AAMA,IAAa,KACX,MACoB;CACpB,IAAM,EACJ,OACA,aAAU,CAAC,GACX,mBAAgB,CAAC,GACjB,iBAAc,UACd,gBAAa,IACb,cACA,aACA,gBACA,gBACA,mBACE,GAEE,CAAC,GAAU,KAAe,SAAyC,EACvE,GAAG,EACL,EAAE,GACI,CAAC,GAAQ,KAAa,EAAiC,CAAC,CAAC,GAMzD,IAAe,EAAO,EAAK,GAG3B,IAAQ,EAAO,CAAE;CACvB,EAAM,UAAU;CAChB,IAAM,IAAc,EAAO,CAAQ;CACnC,EAAY,UAAU;CACtB,IAAM,IAAiB,EAAO,CAAW;CAGzC,AAFA,EAAe,UAAU,GAEzB,cACe;EACX,AAAK,EAAa,WAChB,EAAe,QAAQ;GAAC,IAAI,EAAM;GAAS,UAAU,EAAY;EAAO,CAAC;CAE7E,GAGC,CAAC,CAAC;CAEL,IAAM,IAAe,GAAa,GAAmB,MAAmB;EAGtE,AAFA,GAAa,OAAU;GAAC,GAAG;IAAO,IAAY;EAAK,EAAE,GAErD,GAAW,MAAS;GAClB,IAAI,CAAC,EAAK,IAAY,OAAO;GAC7B,IAAM,IAAO,EAAC,GAAG,EAAI;GAErB,OADA,OAAO,EAAK,IACL;EACT,CAAC;CACH,GAAG,CAAC,CAAC,GAEC,IAAW,GAAa,GAAmB,MAAyB;EACxE,GAAW,MAAS;GAClB,IAAI,MAAU,MAAM;IAClB,IAAM,IAAO,EAAC,GAAG,EAAI;IAErB,OADA,OAAO,EAAK,IACL;GACT;GACA,OAAO;IAAC,GAAG;KAAO,IAAY;GAAK;EACrC,CAAC;CACH,GAAG,CAAC,CAAC,GAEC,IAAW,QAAkB;EAEjC,AADA,EAAa,UAAU,IACvB,EAAY;GAAC;GAAI;EAAQ,CAAC;CAC5B,GAAG;EAAC;EAAI;EAAU;CAAW,CAAC,GAExB,IAAW,QAAkB;EAEjC,AADA,EAAa,UAAU,IACvB,EAAY;GAAC;GAAI;EAAQ,CAAC;CAC5B,GAAG;EAAC;EAAI;EAAU;CAAW,CAAC,GAExB,IAAe,GAClB,MAAuB;EAEtB,AADA,EAAE,eAAe,GACjB,EAAS;CACX,GACA,CAAC,CAAQ,CACX,GAgBM,IAAiC;EACrC;EACA;EACA;EACA;EACA,eAhBoB,GACnB,MAAuB;GACtB,AAAI,MAAW,WACb,EAAS,IAET,EAAS;EAEb,GACA,CAAC,GAAU,CAAQ,CAQnB;EACA,UAAU;CACZ;CAEA,OACE,kBAAC,EAAY,UAAb;EAAsB,OAAO;YAC3B,kBAAC,QAAD;GACE,UAAU;GACV,WAAW,EAAG,uBAAuB,CAAS;GAC9C,gBAAc;aAHhB,CAKG,EAAQ,KAAK,GAAM,MAClB,kBAAC,GAAD;IAAgB,UAAU;IAAM,YAAY;GAAW,GAA5C,CAA4C,CACxD,GACA,CAAC,KACA,kBAAC,UAAD;IACE,MAAK;IACL,WAAU;cAET;GACK,CAAA,CAEN;;CACc,CAAA;AAE1B"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { default as React } from 'react';
|
|
2
|
+
import { FormAction } from './form.types';
|
|
2
3
|
/**
|
|
3
4
|
* Shape of the data provided by FormContext.
|
|
4
5
|
* When inside a <Form> card, isInForm is true and all fields are populated.
|
|
@@ -9,6 +10,16 @@ export interface FormContextValue {
|
|
|
9
10
|
errors: Record<string, string>;
|
|
10
11
|
handleChange: (fieldName: string, value: unknown) => void;
|
|
11
12
|
setError: (fieldName: string, error: string | null) => void;
|
|
13
|
+
/**
|
|
14
|
+
* Call from within form content to request that the enclosing Form
|
|
15
|
+
* perform a top-level action:
|
|
16
|
+
* - `"submit"` — same as pressing the form's native submit button; fires
|
|
17
|
+
* `onSubmitted`. Used by `Button` (`formAction: "submit"`) and by
|
|
18
|
+
* form-bindable fields' `submitOnEnter` prop.
|
|
19
|
+
* - `"cancel"` — fires `onCancelled` without submitting or resetting the
|
|
20
|
+
* form's field values. Used by `Button` (`formAction: "cancel"`).
|
|
21
|
+
*/
|
|
22
|
+
requestAction: (action: FormAction) => void;
|
|
12
23
|
isInForm: boolean;
|
|
13
24
|
}
|
|
14
25
|
export declare const FormContext: React.Context<FormContextValue | null>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"form.context.js","names":[],"sources":["../../../src/cards/form/form.context.tsx"],"sourcesContent":["import React, {useContext} from \"react\";\n\n/**\n * Shape of the data provided by FormContext.\n * When inside a <Form> card, isInForm is true and all fields are populated.\n * When outside (fallback), isInForm is false.\n */\nexport interface FormContextValue {\n formData: Record<string, unknown>;\n errors: Record<string, string>;\n handleChange: (fieldName: string, value: unknown) => void;\n setError: (fieldName: string, error: string | null) => void;\n isInForm: boolean;\n}\n\nconst FALLBACK_CONTEXT: FormContextValue = {\n formData: {},\n errors: {},\n handleChange: () => {},\n setError: () => {},\n isInForm: false,\n};\n\nexport const FormContext = React.createContext<FormContextValue | null>(null);\n\n/**\n * Hook that reads the nearest FormContext.\n * Returns a fallback (isInForm=false) when called outside a <Form> card.\n *\n * Usage in field components:\n * const form = useFormContext();\n * const value = form.isInForm && name ? form.formData[name] ?? '' : propValue;\n */\nexport const useFormContext = (): FormContextValue => {\n const ctx = useContext(FormContext);\n return ctx ?? FALLBACK_CONTEXT;\n};\n"],"mappings":";;
|
|
1
|
+
{"version":3,"file":"form.context.js","names":[],"sources":["../../../src/cards/form/form.context.tsx"],"sourcesContent":["import React, {useContext} from \"react\";\nimport type {FormAction} from \"./form.types\";\n\n/**\n * Shape of the data provided by FormContext.\n * When inside a <Form> card, isInForm is true and all fields are populated.\n * When outside (fallback), isInForm is false.\n */\nexport interface FormContextValue {\n formData: Record<string, unknown>;\n errors: Record<string, string>;\n handleChange: (fieldName: string, value: unknown) => void;\n setError: (fieldName: string, error: string | null) => void;\n /**\n * Call from within form content to request that the enclosing Form\n * perform a top-level action:\n * - `\"submit\"` — same as pressing the form's native submit button; fires\n * `onSubmitted`. Used by `Button` (`formAction: \"submit\"`) and by\n * form-bindable fields' `submitOnEnter` prop.\n * - `\"cancel\"` — fires `onCancelled` without submitting or resetting the\n * form's field values. Used by `Button` (`formAction: \"cancel\"`).\n */\n requestAction: (action: FormAction) => void;\n isInForm: boolean;\n}\n\nconst FALLBACK_CONTEXT: FormContextValue = {\n formData: {},\n errors: {},\n handleChange: () => {},\n setError: () => {},\n requestAction: () => {},\n isInForm: false,\n};\n\nexport const FormContext = React.createContext<FormContextValue | null>(null);\n\n/**\n * Hook that reads the nearest FormContext.\n * Returns a fallback (isInForm=false) when called outside a <Form> card.\n *\n * Usage in field components:\n * const form = useFormContext();\n * const value = form.isInForm && name ? form.formData[name] ?? '' : propValue;\n */\nexport const useFormContext = (): FormContextValue => {\n const ctx = useContext(FormContext);\n return ctx ?? FALLBACK_CONTEXT;\n};\n"],"mappings":";;AA0BA,IAAM,IAAqC;CACzC,UAAU,CAAC;CACX,QAAQ,CAAC;CACT,oBAAoB,CAAC;CACrB,gBAAgB,CAAC;CACjB,qBAAqB,CAAC;CACtB,UAAU;AACZ,GAEa,IAAc,EAAM,cAAuC,IAAI,GAU/D,UACC,EAAW,CAChB,KAAO"}
|
|
@@ -3,6 +3,8 @@ export declare const PI_FORM_CARD = "pi/form";
|
|
|
3
3
|
export declare const Form: <S extends import('@pihanga2/core').ReduxState>(p: import('@pihanga2/core').PiMapProps<PiFormProps, S, PiFormEvents>) => import('@pihanga2/core').PiCardDef;
|
|
4
4
|
export declare const PI_FORM_ACTION: {
|
|
5
5
|
SUBMITTED: string;
|
|
6
|
+
CANCELLED: string;
|
|
7
|
+
ABANDONED: string;
|
|
6
8
|
};
|
|
7
9
|
export declare const onFormSubmitted: <S extends import('@pihanga2/core').ReduxState>(register: import('@pihanga2/core/types').PiRegisterMinimal, f: import('@pihanga2/core').ReduceF<S, import('@pihanga2/core').ReduxAction & {
|
|
8
10
|
cardID: string;
|
|
@@ -11,6 +13,60 @@ export declare const onFormSubmitted: <S extends import('@pihanga2/core').ReduxS
|
|
|
11
13
|
export declare const onPiFormSubmitted: <S extends import('@pihanga2/core').ReduxState>(register: import('@pihanga2/core/types').PiRegisterMinimal, f: import('@pihanga2/core').ReduceF<S, import('@pihanga2/core').ReduxAction & {
|
|
12
14
|
cardID: string;
|
|
13
15
|
} & PiFormSubmittedEvent>) => void;
|
|
16
|
+
/**
|
|
17
|
+
* Fires when a control inside the form's content explicitly requests
|
|
18
|
+
* cancellation — see `FormContext.requestAction("cancel")` (wired up
|
|
19
|
+
* automatically for any `Button` card with `formAction: "cancel"`).
|
|
20
|
+
* Does not submit the form and does not reset its field values.
|
|
21
|
+
*/
|
|
22
|
+
export declare const onFormCancelled: <S extends import('@pihanga2/core').ReduxState>(register: import('@pihanga2/core/types').PiRegisterMinimal, f: import('@pihanga2/core').ReduceF<S, import('@pihanga2/core').ReduxAction & {
|
|
23
|
+
cardID: string;
|
|
24
|
+
} & PiFormCancelledEvent>) => void;
|
|
25
|
+
/**
|
|
26
|
+
* Fires automatically when the Form card is removed from the screen
|
|
27
|
+
* (unmounted) *without* having been submitted or explicitly cancelled first
|
|
28
|
+
* — e.g. a parent `pi/dialog` is dismissed via the X button, Escape, or an
|
|
29
|
+
* outside click while the form is still open. Use this to clean up any
|
|
30
|
+
* related state (drafts, optimistic UI, etc.) regardless of how the host
|
|
31
|
+
* container went away.
|
|
32
|
+
*
|
|
33
|
+
* Never fires after `onSubmitted` or `onCancelled` for the same form
|
|
34
|
+
* instance.
|
|
35
|
+
*/
|
|
36
|
+
export declare const onFormAbandoned: <S extends import('@pihanga2/core').ReduxState>(register: import('@pihanga2/core/types').PiRegisterMinimal, f: import('@pihanga2/core').ReduceF<S, import('@pihanga2/core').ReduxAction & {
|
|
37
|
+
cardID: string;
|
|
38
|
+
} & PiFormAbandonedEvent>) => void;
|
|
39
|
+
/**
|
|
40
|
+
* Shared props for any card that binds a single value to a `pi/form`'s
|
|
41
|
+
* state (text inputs, toggles, selects, etc.).
|
|
42
|
+
*
|
|
43
|
+
* All form-bindable field cards (`pi/input`, `pi/text-field`,
|
|
44
|
+
* `pi/text-area`, `pi/switch`, `pi/checkbox`, `pi/toggle-group`,
|
|
45
|
+
* `pi/select`, …) extend this so the binding contract stays identical
|
|
46
|
+
* everywhere: set `name` to read/write the field via `FormContext` when
|
|
47
|
+
* rendered inside a `pi/form`; omit it (or render outside a form) to fall
|
|
48
|
+
* back to the card's own standalone `value`/`checked` prop + `onChanged`
|
|
49
|
+
* event.
|
|
50
|
+
*/
|
|
51
|
+
export type FormFieldProps = {
|
|
52
|
+
/**
|
|
53
|
+
* Field name used to bind to FormContext when inside a pi/form card.
|
|
54
|
+
* When provided the component reads its value from form state and writes
|
|
55
|
+
* back via form.handleChange.
|
|
56
|
+
*/
|
|
57
|
+
name?: string;
|
|
58
|
+
};
|
|
59
|
+
/**
|
|
60
|
+
* Actions a control nested inside a `pi/form` can request via
|
|
61
|
+
* `FormContext.requestAction()` — currently used by `Button`'s
|
|
62
|
+
* `formAction` prop, and by form-bindable fields' `submitOnEnter` prop.
|
|
63
|
+
*
|
|
64
|
+
* - `"submit"` — triggers the form's real submit flow (same as pressing its
|
|
65
|
+
* native submit button), which fires `onSubmitted`.
|
|
66
|
+
* - `"cancel"` — fires `onCancelled` without submitting or resetting the
|
|
67
|
+
* form.
|
|
68
|
+
*/
|
|
69
|
+
export type FormAction = "submit" | "cancel";
|
|
14
70
|
export type PiFormProps = {
|
|
15
71
|
/**
|
|
16
72
|
* Optional id passed through to events for identification.
|
|
@@ -36,12 +92,31 @@ export type PiFormProps = {
|
|
|
36
92
|
* Additional CSS classes for the <form> element.
|
|
37
93
|
*/
|
|
38
94
|
className?: string;
|
|
95
|
+
/**
|
|
96
|
+
* When true, the default footer "Submit" button is not rendered.
|
|
97
|
+
* Use this when the form's own content already provides a way to submit
|
|
98
|
+
* (e.g. a `Button` with `formAction: "submit"`).
|
|
99
|
+
* @default false
|
|
100
|
+
*/
|
|
101
|
+
hideSubmit?: boolean;
|
|
39
102
|
};
|
|
40
103
|
export type PiFormSubmittedEvent = {
|
|
41
104
|
id?: string;
|
|
42
105
|
/** Snapshot of form state at the time of submission. */
|
|
43
106
|
formData: Record<string, unknown>;
|
|
44
107
|
};
|
|
108
|
+
export type PiFormCancelledEvent = {
|
|
109
|
+
id?: string;
|
|
110
|
+
/** Snapshot of form state at the time of cancellation. */
|
|
111
|
+
formData: Record<string, unknown>;
|
|
112
|
+
};
|
|
113
|
+
export type PiFormAbandonedEvent = {
|
|
114
|
+
id?: string;
|
|
115
|
+
/** Snapshot of form state at the time the form was removed. */
|
|
116
|
+
formData: Record<string, unknown>;
|
|
117
|
+
};
|
|
45
118
|
export type PiFormEvents = {
|
|
46
119
|
onSubmitted: PiFormSubmittedEvent;
|
|
120
|
+
onCancelled: PiFormCancelledEvent;
|
|
121
|
+
onAbandoned: PiFormAbandonedEvent;
|
|
47
122
|
};
|
package/cards/form/form.types.js
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import { createCardDeclaration as e, createOnAction as t, registerActions as n } from "@pihanga2/core";
|
|
2
2
|
//#region src/cards/form/form.types.ts
|
|
3
|
-
var r = "pi/form", i = e(r), a = n(r, [
|
|
3
|
+
var r = "pi/form", i = e(r), a = n(r, [
|
|
4
|
+
"submitted",
|
|
5
|
+
"cancelled",
|
|
6
|
+
"abandoned"
|
|
7
|
+
]), o = t(a.SUBMITTED), s = o, c = t(a.CANCELLED), l = t(a.ABANDONED);
|
|
4
8
|
//#endregion
|
|
5
|
-
export { i as Form, a as PI_FORM_ACTION, r as PI_FORM_CARD, o as onFormSubmitted, s as onPiFormSubmitted };
|
|
9
|
+
export { i as Form, a as PI_FORM_ACTION, r as PI_FORM_CARD, l as onFormAbandoned, c as onFormCancelled, o as onFormSubmitted, s as onPiFormSubmitted };
|
|
6
10
|
|
|
7
11
|
//# sourceMappingURL=form.types.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"form.types.js","names":[],"sources":["../../../src/cards/form/form.types.ts"],"sourcesContent":["import {\n createCardDeclaration,\n createOnAction,\n type PiCardRef,\n registerActions,\n} from \"@pihanga2/core\";\n\nexport const PI_FORM_CARD = \"pi/form\";\n\nexport const Form = createCardDeclaration<PiFormProps, PiFormEvents>(\n PI_FORM_CARD,\n);\n\nexport const PI_FORM_ACTION = registerActions(PI_FORM_CARD, [\"submitted\"]);\n\nexport const onFormSubmitted = createOnAction<PiFormSubmittedEvent>(\n PI_FORM_ACTION.SUBMITTED,\n);\n\n/** @deprecated Use `onFormSubmitted` instead. */\nexport const onPiFormSubmitted = onFormSubmitted;\n\nexport type PiFormProps = {\n /**\n * Optional id passed through to events for identification.\n */\n id?: string;\n\n /**\n * Ordered list of field card refs to render inside the form.\n * Each card (e.g. TextField, Checkbox, FormSelect) will be rendered\n * in order and wrapped by a FormContext.Provider so they can read\n * and write shared form state.\n */\n content?: PiCardRef[];\n\n /**\n * Initial values for the form fields, keyed by field name.\n */\n initialValues?: Record<string, unknown>;\n\n /**\n * Label for the submit button.\n * @default \"Submit\"\n */\n submitLabel?: string;\n\n /**\n * Additional CSS classes for the <form> element.\n */\n className?: string;\n};\n\nexport type PiFormSubmittedEvent = {\n id?: string;\n /** Snapshot of form state at the time of submission. */\n formData: Record<string, unknown>;\n};\n\nexport type PiFormEvents = {\n onSubmitted: PiFormSubmittedEvent;\n};\n"],"mappings":";;AAOA,IAAa,IAAe,WAEf,IAAO,EAClB,CACF,GAEa,IAAiB,EAAgB,GAAc,CAAC,
|
|
1
|
+
{"version":3,"file":"form.types.js","names":[],"sources":["../../../src/cards/form/form.types.ts"],"sourcesContent":["import {\n createCardDeclaration,\n createOnAction,\n type PiCardRef,\n registerActions,\n} from \"@pihanga2/core\";\n\nexport const PI_FORM_CARD = \"pi/form\";\n\nexport const Form = createCardDeclaration<PiFormProps, PiFormEvents>(\n PI_FORM_CARD,\n);\n\nexport const PI_FORM_ACTION = registerActions(PI_FORM_CARD, [\n \"submitted\",\n \"cancelled\",\n \"abandoned\",\n]);\n\nexport const onFormSubmitted = createOnAction<PiFormSubmittedEvent>(\n PI_FORM_ACTION.SUBMITTED,\n);\n\n/** @deprecated Use `onFormSubmitted` instead. */\nexport const onPiFormSubmitted = onFormSubmitted;\n\n/**\n * Fires when a control inside the form's content explicitly requests\n * cancellation — see `FormContext.requestAction(\"cancel\")` (wired up\n * automatically for any `Button` card with `formAction: \"cancel\"`).\n * Does not submit the form and does not reset its field values.\n */\nexport const onFormCancelled = createOnAction<PiFormCancelledEvent>(\n PI_FORM_ACTION.CANCELLED,\n);\n\n/**\n * Fires automatically when the Form card is removed from the screen\n * (unmounted) *without* having been submitted or explicitly cancelled first\n * — e.g. a parent `pi/dialog` is dismissed via the X button, Escape, or an\n * outside click while the form is still open. Use this to clean up any\n * related state (drafts, optimistic UI, etc.) regardless of how the host\n * container went away.\n *\n * Never fires after `onSubmitted` or `onCancelled` for the same form\n * instance.\n */\nexport const onFormAbandoned = createOnAction<PiFormAbandonedEvent>(\n PI_FORM_ACTION.ABANDONED,\n);\n\n/**\n * Shared props for any card that binds a single value to a `pi/form`'s\n * state (text inputs, toggles, selects, etc.).\n *\n * All form-bindable field cards (`pi/input`, `pi/text-field`,\n * `pi/text-area`, `pi/switch`, `pi/checkbox`, `pi/toggle-group`,\n * `pi/select`, …) extend this so the binding contract stays identical\n * everywhere: set `name` to read/write the field via `FormContext` when\n * rendered inside a `pi/form`; omit it (or render outside a form) to fall\n * back to the card's own standalone `value`/`checked` prop + `onChanged`\n * event.\n */\nexport type FormFieldProps = {\n /**\n * Field name used to bind to FormContext when inside a pi/form card.\n * When provided the component reads its value from form state and writes\n * back via form.handleChange.\n */\n name?: string;\n};\n\n/**\n * Actions a control nested inside a `pi/form` can request via\n * `FormContext.requestAction()` — currently used by `Button`'s\n * `formAction` prop, and by form-bindable fields' `submitOnEnter` prop.\n *\n * - `\"submit\"` — triggers the form's real submit flow (same as pressing its\n * native submit button), which fires `onSubmitted`.\n * - `\"cancel\"` — fires `onCancelled` without submitting or resetting the\n * form.\n */\nexport type FormAction = \"submit\" | \"cancel\";\n\nexport type PiFormProps = {\n /**\n * Optional id passed through to events for identification.\n */\n id?: string;\n\n /**\n * Ordered list of field card refs to render inside the form.\n * Each card (e.g. TextField, Checkbox, FormSelect) will be rendered\n * in order and wrapped by a FormContext.Provider so they can read\n * and write shared form state.\n */\n content?: PiCardRef[];\n\n /**\n * Initial values for the form fields, keyed by field name.\n */\n initialValues?: Record<string, unknown>;\n\n /**\n * Label for the submit button.\n * @default \"Submit\"\n */\n submitLabel?: string;\n\n /**\n * Additional CSS classes for the <form> element.\n */\n className?: string;\n\n /**\n * When true, the default footer \"Submit\" button is not rendered.\n * Use this when the form's own content already provides a way to submit\n * (e.g. a `Button` with `formAction: \"submit\"`).\n * @default false\n */\n hideSubmit?: boolean;\n};\n\nexport type PiFormSubmittedEvent = {\n id?: string;\n /** Snapshot of form state at the time of submission. */\n formData: Record<string, unknown>;\n};\n\nexport type PiFormCancelledEvent = {\n id?: string;\n /** Snapshot of form state at the time of cancellation. */\n formData: Record<string, unknown>;\n};\n\nexport type PiFormAbandonedEvent = {\n id?: string;\n /** Snapshot of form state at the time the form was removed. */\n formData: Record<string, unknown>;\n};\n\nexport type PiFormEvents = {\n onSubmitted: PiFormSubmittedEvent;\n onCancelled: PiFormCancelledEvent;\n onAbandoned: PiFormAbandonedEvent;\n};\n"],"mappings":";;AAOA,IAAa,IAAe,WAEf,IAAO,EAClB,CACF,GAEa,IAAiB,EAAgB,GAAc;CAC1D;CACA;CACA;AACF,CAAC,GAEY,IAAkB,EAC7B,EAAe,SACjB,GAGa,IAAoB,GAQpB,IAAkB,EAC7B,EAAe,SACjB,GAaa,IAAkB,EAC7B,EAAe,SACjB"}
|
package/cards/form/index.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { FormContext as e, useFormContext as t } from "./form.context.js";
|
|
2
2
|
import { FormComponent as n } from "./form.component.js";
|
|
3
|
-
import { Form as r, PI_FORM_ACTION as i, PI_FORM_CARD as a,
|
|
4
|
-
import { actionTypesToEvents as
|
|
3
|
+
import { Form as r, PI_FORM_ACTION as i, PI_FORM_CARD as a, onFormAbandoned as o, onFormCancelled as s, onFormSubmitted as c, onPiFormSubmitted as l } from "./form.types.js";
|
|
4
|
+
import { actionTypesToEvents as u, registerCardComponent as d } from "@pihanga2/core";
|
|
5
5
|
//#region src/cards/form/index.ts
|
|
6
|
-
|
|
6
|
+
d({
|
|
7
7
|
name: a,
|
|
8
8
|
component: n,
|
|
9
|
-
events:
|
|
9
|
+
events: u(i)
|
|
10
10
|
});
|
|
11
11
|
//#endregion
|
|
12
|
-
export { r as Form, e as FormContext, i as PI_FORM_ACTION, a as PI_FORM_CARD, o as
|
|
12
|
+
export { r as Form, e as FormContext, i as PI_FORM_ACTION, a as PI_FORM_CARD, o as onFormAbandoned, s as onFormCancelled, c as onFormSubmitted, l as onPiFormSubmitted, t as useFormContext };
|
|
13
13
|
|
|
14
14
|
//# sourceMappingURL=index.js.map
|