@ikas/code-components-mcp 1.4.0-beta.5 → 1.4.0-beta.7
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/data/framework.json +34 -34
- package/data/migration.json +33 -19
- package/data/storefront-api.json +1 -1
- package/data/storefront-types.json +1 -1
- package/dist/index.js +87 -108
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/data/framework.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"project-structure": {
|
|
5
5
|
"title": "Project Structure",
|
|
6
6
|
"description": "How an ikas code components project is organized",
|
|
7
|
-
"content": "An ikas code components project follows this structure:\n\n```\nmy-project/\n
|
|
7
|
+
"content": "An ikas code components project follows this structure:\n\n```\nmy-project/\n\u251c\u2500\u2500 src/\n\u2502 \u251c\u2500\u2500 global.css # Optional: project-wide unscoped CSS (variables, resets)\n\u2502 \u2514\u2500\u2500 components/\n\u2502 \u251c\u2500\u2500 MyComponent/\n\u2502 \u2502 \u251c\u2500\u2500 index.tsx # Component implementation (Preact)\n\u2502 \u2502 \u251c\u2500\u2500 types.ts # TypeScript props interface\n\u2502 \u2502 \u2514\u2500\u2500 styles.css # Scoped component styles\n\u2502 \u2514\u2500\u2500 index.ts # Component exports barrel\n\u251c\u2500\u2500 ikas.config.json # Component definitions, props, metadata\n\u251c\u2500\u2500 package.json\n\u251c\u2500\u2500 tsconfig.json\n\u2514\u2500\u2500 vite.config.ts\n```\n\nEach component lives in its own directory under `src/components/`. The component's entry point is `index.tsx`, which exports a default Preact function component. The `types.ts` file defines the props interface matching the props declared in `ikas.config.json`. The `styles.css` file contains CSS that is automatically scoped to the component.\n\n### Project with Sub-Components\n\nWhen components grow complex, extract sub-components into `src/sub-components/`. See `get_framework_guide(\"sub-component-patterns\")` for full details.\n\n```\nmy-project/\n\u251c\u2500\u2500 src/\n\u2502 \u251c\u2500\u2500 components/\n\u2502 \u2502 \u251c\u2500\u2500 ProductList/\n\u2502 \u2502 \u2502 \u251c\u2500\u2500 index.tsx # Imports from ../../sub-components/...\n\u2502 \u2502 \u2502 \u251c\u2500\u2500 types.ts # Auto-generated (CLI-managed)\n\u2502 \u2502 \u2502 \u2514\u2500\u2500 styles.css # @import \"../../sub-components/.../styles.css\"\n\u2502 \u2502 \u2514\u2500\u2500 index.ts\n\u2502 \u2514\u2500\u2500 sub-components/\n\u2502 \u251c\u2500\u2500 ProductCard/\n\u2502 \u2502 \u251c\u2500\u2500 index.tsx # Sub-component with inline Props\n\u2502 \u2502 \u2514\u2500\u2500 styles.css # Sub-component styles\n\u2502 \u2514\u2500\u2500 FilterSidebar/\n\u2502 \u251c\u2500\u2500 index.tsx\n\u2502 \u2514\u2500\u2500 styles.css\n\u251c\u2500\u2500 ikas.config.json\n\u251c\u2500\u2500 package.json\n\u251c\u2500\u2500 tsconfig.json\n\u2514\u2500\u2500 vite.config.ts\n```\n\n**Key:** `src/components/` = registered components (in ikas.config.json). `src/sub-components/` = internal helpers (NOT in ikas.config.json).",
|
|
8
8
|
"tags": [
|
|
9
9
|
"structure",
|
|
10
10
|
"project",
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"ikas-config": {
|
|
18
18
|
"title": "ikas.config.json Schema",
|
|
19
19
|
"description": "Configuration file that defines components, their props, and metadata",
|
|
20
|
-
"content": "The `ikas.config.json` file is the central configuration for your code components project.\n\n```json\n{\n \"name\": \"my-project\",\n \"version\": \"1.0.0\",\n \"globalStyles\": \"./src/global.css\",\n \"components\": [\n {\n \"id\": \"my-component\",\n \"name\": \"My Component\",\n \"entry\": \"./src/components/MyComponent/index.tsx\",\n \"styles\": \"./src/components/MyComponent/styles.css\",\n \"props\": [\n {\n \"name\": \"title\",\n \"displayName\": \"Title\",\n \"type\": \"TEXT\",\n \"required\": true,\n \"defaultValue\": \"Hello\",\n \"description\": \"The main heading text\"\n }\n ]\n },\n {\n \"id\": \"hero-banner\",\n \"name\": \"Hero Banner\",\n \"type\": \"section\",\n \"entry\": \"./src/components/HeroBanner/index.tsx\",\n \"styles\": \"./src/components/HeroBanner/styles.css\",\n \"props\": [\n {\n \"name\": \"heading\",\n \"displayName\": \"Heading\",\n \"type\": \"TEXT\",\n \"required\": true,\n \"defaultValue\": \"Welcome\"\n }\n ]\n }\n ]\n}\n```\n\n### Top-level fields:\n- `name` (string): Project name\n- `version` (string): Project version\n- `globalStyles` (string, optional): Path to a global CSS file (e.g., `\"./src/global.css\"`). See `get_framework_guide(\"global-css\")` for details\n- `components` (array): List of component definitions\n\n### Component fields:\n- `id` (string): Unique identifier (kebab-case)\n- `name` (string): Display name in the editor\n- `type` (string, optional): `\"component\"` (default) or `\"section\"`
|
|
20
|
+
"content": "The `ikas.config.json` file is the central configuration for your code components project.\n\n```json\n{\n \"name\": \"my-project\",\n \"version\": \"1.0.0\",\n \"globalStyles\": \"./src/global.css\",\n \"components\": [\n {\n \"id\": \"my-component\",\n \"name\": \"My Component\",\n \"entry\": \"./src/components/MyComponent/index.tsx\",\n \"styles\": \"./src/components/MyComponent/styles.css\",\n \"props\": [\n {\n \"name\": \"title\",\n \"displayName\": \"Title\",\n \"type\": \"TEXT\",\n \"required\": true,\n \"defaultValue\": \"Hello\",\n \"description\": \"The main heading text\"\n }\n ]\n },\n {\n \"id\": \"hero-banner\",\n \"name\": \"Hero Banner\",\n \"type\": \"section\",\n \"entry\": \"./src/components/HeroBanner/index.tsx\",\n \"styles\": \"./src/components/HeroBanner/styles.css\",\n \"props\": [\n {\n \"name\": \"heading\",\n \"displayName\": \"Heading\",\n \"type\": \"TEXT\",\n \"required\": true,\n \"defaultValue\": \"Welcome\"\n }\n ]\n }\n ]\n}\n```\n\n### Top-level fields:\n- `name` (string): Project name\n- `version` (string): Project version\n- `globalStyles` (string, optional): Path to a global CSS file (e.g., `\"./src/global.css\"`). See `get_framework_guide(\"global-css\")` for details\n- `components` (array): List of component definitions\n\n### Component fields:\n- `id` (string): Unique identifier (kebab-case)\n- `name` (string): Display name in the editor\n- `type` (string, optional): `\"component\"` (default) or `\"section\"` \u2014 sections are page-level containers (e.g. header, hero banner, footer), components are child elements placed inside sections (e.g. button, card, badge)\n- `entry` (string): Path to the component's entry file (index.tsx)\n- `styles` (string): Path to the component's CSS file\n- `props` (array): List of prop definitions\n- `isHeader` (boolean, optional): Mark this section as the store's header. Only for `type: \"section\"`. At most one component should have `isHeader: true`\n- `isFooter` (boolean, optional): Mark this section as the store's footer. Only for `type: \"section\"`. At most one component should have `isFooter: true`\n\n### Prop fields:\n- `name` (string): Prop name used in code (camelCase)\n- `displayName` (string): Label shown in the editor UI\n- `type` (string): One of the available prop types\n- `required` (boolean): Whether the prop must be set\n- `defaultValue` (any): Default value matching the prop type\n- `description` (string): Help text shown in editor\n- `groupId` (string, optional): Assign this prop to a prop group (references a group's `id`)\n\n### Prop Groups (optional):\nAdd `propGroups` array on a component to organize props into collapsible groups in the editor.\n- `id` (string): Unique group identifier within the component\n- `name` (string): Display name in editor\n- `description` (string, optional): Group description\n- `children` (array, optional): Nested groups (max 1 level)\n\nSee `get_framework_guide(\"prop-groups\")` for full details and examples.\n\n### Header & Footer Flags\nFor section-type components that serve as the store's header or footer, set `isHeader: true` or `isFooter: true`:\n```json\n{\n \"id\": \"header\",\n \"name\": \"Header\",\n \"type\": \"section\",\n \"isHeader\": true,\n \"entry\": \"./src/components/Header/index.tsx\",\n \"styles\": \"./src/components/Header/styles.css\",\n \"props\": [...]\n}\n```\nThese flags tell the editor to treat the wrapper section as a common header/footer, which affects page composition (header/footer appear on all pages automatically).",
|
|
21
21
|
"tags": [
|
|
22
22
|
"config",
|
|
23
23
|
"configuration",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"prop-types": {
|
|
36
36
|
"title": "Available Prop Types",
|
|
37
37
|
"description": "All prop types that can be used in ikas.config.json",
|
|
38
|
-
"content": "Props define what the store editor can configure for each component. Each prop has a `type` that determines the editor UI and the TypeScript type received in your component.\n\n| Type | Editor UI | TypeScript Type | Description |\n|------|-----------|----------------|-------------|\n| `TEXT` | Text input | `string` | Single-line text |\n| `RICH_TEXT` | Rich text editor | `string` | HTML rich text content |\n| `NUMBER` | Number input | `number` | Numeric value |\n| `NUMBER_RANGE` | Number range input | `IkasNumberRange` | Number range with min, max, interval, and unit |\n| `BOOLEAN` | Toggle switch | `boolean` | True/false toggle |\n| `IMAGE` | Image picker | `IkasImage | null` | Image from editor. Use `getDefaultSrc(image)` for URL |\n| `IMAGE_LIST` | Image list picker | `IkasImageList` | Multiple images from editor |\n| `VIDEO` | Video picker | `IkasVideo | null` | Video from editor |\n| `DATE` | Date picker | `Date | string` | Date value |\n| `LINK` | Link editor | `IkasNavigationLink | null` | Navigation link with href, label, subLinks |\n| `LIST_OF_LINK` | Link list editor | `IkasNavigationLinkList` | List of navigation links |\n| `COLOR` | Color picker | `string` | CSS color value (hex, rgb, etc.) |\n| `PRODUCT` | Product picker | `IkasProduct | null` | Single product reference |\n| `PRODUCT_LIST` | Product list picker | `IkasProductList` | Product list with `.data` (products array), `.filters`, `.sort`, `.page`, etc. |\n| `PRODUCT_ATTRIBUTE` | Product attribute picker | `IkasProductAttributeValue | null` | Single product attribute value |\n| `PRODUCT_ATTRIBUTE_LIST` | Product attribute list picker | `IkasProductAttributeValue[]` | Multiple product attribute values |\n| `CATEGORY` | Category picker | `IkasCategory | null` | Single category reference |\n| `CATEGORY_LIST` | Category list picker | `IkasCategoryList` | Multiple category references |\n| `BRAND` | Brand picker | `IkasBrand | null` | Single brand reference |\n| `BRAND_LIST` | Brand list picker | `IkasBrandList` | Multiple brand references |\n| `BLOG` | Blog post picker | `IkasBlog | null` | Single blog post reference |\n| `BLOG_LIST` | Blog post list picker | `IkasBlogList` | Multiple blog post references |\n| `BLOG_CATEGORY` | Blog category picker | `IkasBlogCategory | null` | Single blog category reference |\n| `BLOG_CATEGORY_LIST` | Blog category list picker | `IkasBlogCategoryList` | Multiple blog category references |\n| `TYPE` | Type selector | Depends on typeId | Structured type (padding, margin, size, etc.). Available for both components and sections (sections have a restricted whitelist). |\n| `ENUM` | Dropdown selector | `string` | Enum-based style type (flex-direction, justify-content, align-items, etc.). Uses `enumTypeId`. Available for both components and sections. |\n| `COMPONENT` | Component slot | `any` | A single child component slot. Store owners can place a component in this slot from the editor. Render with `<IkasComponentRenderer>`. |\n| `COMPONENT_LIST` | Component list slot | `any` | A list of child components. Store owners can add multiple components from the editor. Render with `<IkasComponentRenderer>`. |\n\n### COMPONENT & COMPONENT_LIST props (child component slots)\n\nThese prop types enable **slot-based** architectures where store owners can drag child components into your section/component from the editor.\n\n- `COMPONENT` — a single child component slot\n- `COMPONENT_LIST` — a list of child components\n\n**Rendering:** Use the `<IkasComponentRenderer>` wrapper from `@ikas/bp-storefront`:\n```tsx\nimport { IkasComponentRenderer } from \"@ikas/bp-storefront\";\nimport { Props } from \"./types\";\n\nexport function MySection({ title, cardList, ...props }: Props) {\n return (\n <section>\n <h2>{title}</h2>\n <div className=\"cards\">\n {/* COMPONENT_LIST — render a list of child components */}\n <IkasComponentRenderer\n id=\"card-list\"\n components={cardList as any[]}\n parentProps={props}\n />\n </div>\n </section>\n );\n}\nexport default MySection;\n```\n\n**Key rules:**\n- Always pass `parentProps={props}` so child components can access parent data via dynamic values\n- Cast the prop value: `components={myList as any[]}` for COMPONENT_LIST, `components={[myComp] as any[]}` for COMPONENT\n- `<IkasComponentRenderer>` handles rendering, styling, and reactivity of child components automatically\n\n**Config example (COMPONENT_LIST):**\n```json\n{\n \"name\": \"cardList\",\n \"displayName\": \"Card List\",\n \"type\": \"COMPONENT_LIST\"\n}\n```\n\n**Config example (COMPONENT):**\n```json\n{\n \"name\": \"headerSlot\",\n \"displayName\": \"Header Slot\",\n \"type\": \"COMPONENT\"\n}\n```\n\n### Restricting which components can appear (`filteredComponentIds`)\n\nA COMPONENT or COMPONENT_LIST prop may include a `filteredComponentIds: string[]` field to limit which sibling components can be placed inside it. Each entry must be the **opaque random id** of a component already defined in `ikas.config.json` — e.g. `\"7ojrigep-Eml9n5sN3i\"`.\n\n**Ids are not derivable from component names. Do NOT compose them as `${projectId}-${name}`.** The CLI will reject any unknown id with a structured error listing the valid `{id, name}` pairs.\n\n**Recommended workflow:**\n\n1. Create the child component(s) first. The CLI prints the new id in its JSON response:\n ```bash\n npx ikas-component config add-component --name Navbar --type component\n # → { \"success\": true, \"componentId\": \"7ojrigep-Eml9n5sN3i\", ... }\n ```\n2. Capture the `componentId` and reuse it in the parent's `filteredComponentIds`:\n ```bash\n npx ikas-component config add-component --name Header --type section \\\n --props '[{\"name\":\"components\",\"type\":\"COMPONENT_LIST\",\"filteredComponentIds\":[\"7ojrigep-Eml9n5sN3i\"]}]'\n ```\n\nOr create the parent first without `filteredComponentIds`, then attach it once children exist:\n```bash\nnpx ikas-component config add-prop Header components --type COMPONENT_LIST \\\n --filteredComponentIds '[\"7ojrigep-Eml9n5sN3i\"]'\n```\n\n**Discovering existing ids:** `npx ikas-component config list` returns a JSON document with every component's `{ id, name, type, props… }`. Use that when joining a project mid-flight.\n\n### IMAGE type example:\n```json\n{\n \"name\": \"heroImage\",\n \"displayName\": \"Hero Image\",\n \"type\": \"IMAGE\",\n \"required\": false\n}\n```\nAccess in component: `getDefaultSrc(props.heroImage)` (import `getDefaultSrc` from `@ikas/bp-storefront`)\n\n### Prop grouping\nProps can be assigned to groups via `groupId` for organized editor sidebar display. See `get_framework_guide(\"prop-groups\")` for details.\n\n### Style Props: TYPE and ENUM\n\nThere are two prop types for style values:\n\n- **TYPE** — Structured types with numeric values and units (padding, margin, border-radius, sizes, etc.). Uses `typeId`.\n- **ENUM** — Enum types rendered as dropdown selectors (flex-direction, justify-content, align-items, etc.). Uses `enumTypeId`.\n\nBoth are available for components and sections. For sections, TYPE props are limited to a whitelist of style types. Use `list-types --component-type section` to see section-allowed types.\n\n### TYPE prop (structured types)\nThe `TYPE` prop lets you use structured storefront types like PaddingStyleType, MarginStyleType, SizeStyleType, etc. Available for both components and sections (sections have a restricted whitelist of style types).\n\n**Workflow:**\n1. Run `npx ikas-component config list-types` to get available types (requires dev server running with editor connected)\n2. Use the `typeId` from the output when adding the prop\n3. For sections: `npx ikas-component config list-types --component-type section` to see only section-allowed types\n\n**Example config:**\n```json\n{\n \"name\": \"spacing\",\n \"displayName\": \"Spacing\",\n \"type\": \"TYPE\",\n \"typeId\": \"@ikas/bp-storefront-models-PaddingStyleType\"\n}\n```\n\n**Array example** (append `_array` to typeId):\n```json\n{\n \"name\": \"margins\",\n \"displayName\": \"Margins\",\n \"type\": \"TYPE\",\n \"typeId\": \"@ikas/bp-storefront-models-MarginStyleType_array\"\n}\n```\nThis generates `margins?: MarginStyleType[]` in types.ts.\n\n**CLI command:**\n```bash\nnpx ikas-component config add-prop --component MyComp --name spacing --displayName Spacing --type TYPE --typeId \"@ikas/bp-storefront-models-PaddingStyleType\"\n```\n\n### ENUM prop (enum style types)\nThe `ENUM` prop lets you use enum-based types that render as dropdown selectors. There are two kinds of enum types:\n\n1. **Built-in enums** (prefix `@ikas/`): FlexDirectionStyleType, JustifyContentStyleType, AlignItemsStyleType, etc. Available from `list-types` when editor is connected.\n2. **Custom enums**: Created with `config add-enum`. Works offline, no editor needed.\n\n**IMPORTANT:** `add-prop --type ENUM` validates that the `enumTypeId` references an existing enum. You MUST create custom enums first with `config add-enum` before using them. The command will reject unknown enum IDs.\n\n**Custom enum workflow (recommended for AI-driven generation):**\n```bash\n# Step 1: Create the enum FIRST\nnpx ikas-component config add-enum --name \"AspectRatio\" --options '{\"Square\":\"1/1\",\"Landscape\":\"16/9\",\"Portrait\":\"3/4\"}'\n# Returns: {\"success\":true,\"enumId\":\"aBcDeFgHiJ\",...}\n\n# Step 2: Use the returned enumId in add-prop\nnpx ikas-component config add-prop --component MyComp --name aspectRatio --displayName \"Aspect Ratio\" --type ENUM --enumTypeId aBcDeFgHiJ\n```\n\n**Built-in enum workflow:**\n1. Run `npx ikas-component config list-types` — enum types have `category: \"enum\"` in the output\n2. Use the `enumTypeId` from the output when adding the prop\n\n**Example config (built-in enum):**\n```json\n{\n \"name\": \"direction\",\n \"displayName\": \"Direction\",\n \"type\": \"ENUM\",\n \"enumTypeId\": \"@ikas/bp-storefront-models-FlexDirectionStyleType\"\n}\n```\nThis generates `direction?: string` in types.ts.\n\n**CLI command (built-in enum):**\n```bash\nnpx ikas-component config add-prop --component MyComp --name direction --displayName Direction --type ENUM --enumTypeId \"@ikas/bp-storefront-models-FlexDirectionStyleType\"\n```\n\n**Note:** `list-types` requires dev server with editor connected. Custom enums (`config add-enum`) work offline.",
|
|
38
|
+
"content": "Props define what the store editor can configure for each component. Each prop has a `type` that determines the editor UI and the TypeScript type received in your component.\n\n| Type | Editor UI | TypeScript Type | Description |\n|------|-----------|----------------|-------------|\n| `TEXT` | Text input | `string` | Single-line text |\n| `RICH_TEXT` | Rich text editor | `string` | HTML rich text content |\n| `NUMBER` | Number input | `number` | Numeric value |\n| `NUMBER_RANGE` | Number range input | `IkasNumberRange` | Number range with min, max, interval, and unit |\n| `BOOLEAN` | Toggle switch | `boolean` | True/false toggle |\n| `IMAGE` | Image picker | `IkasImage | null` | Image from editor. Use `getDefaultSrc(image)` for URL |\n| `IMAGE_LIST` | Image list picker | `IkasImageList` | Multiple images from editor |\n| `VIDEO` | Video picker | `IkasVideo | null` | Video from editor |\n| `DATE` | Date picker | `Date | string` | Date value |\n| `LINK` | Link editor | `IkasNavigationLink | null` | Navigation link with href, label, subLinks |\n| `LIST_OF_LINK` | Link list editor | `IkasNavigationLinkList` | List of navigation links |\n| `COLOR` | Color picker | `string` | CSS color value (hex, rgb, etc.) |\n| `PRODUCT` | Product picker | `IkasProduct | null` | Single product reference |\n| `PRODUCT_LIST` | Product list picker | `IkasProductList` | Product list with `.data` (products array), `.filters`, `.sort`, `.page`, etc. |\n| `PRODUCT_ATTRIBUTE` | Product attribute picker | `IkasProductAttributeValue | null` | Single product attribute value |\n| `PRODUCT_ATTRIBUTE_LIST` | Product attribute list picker | `IkasProductAttributeValue[]` | Multiple product attribute values |\n| `CATEGORY` | Category picker | `IkasCategory | null` | Single category reference |\n| `CATEGORY_LIST` | Category list picker | `IkasCategoryList` | Multiple category references |\n| `BRAND` | Brand picker | `IkasBrand | null` | Single brand reference |\n| `BRAND_LIST` | Brand list picker | `IkasBrandList` | Multiple brand references |\n| `BLOG` | Blog post picker | `IkasBlog | null` | Single blog post reference |\n| `BLOG_LIST` | Blog post list picker | `IkasBlogList` | Multiple blog post references |\n| `BLOG_CATEGORY` | Blog category picker | `IkasBlogCategory | null` | Single blog category reference |\n| `BLOG_CATEGORY_LIST` | Blog category list picker | `IkasBlogCategoryList` | Multiple blog category references |\n| `TYPE` | Type selector | Depends on typeId | Structured type (padding, margin, size, etc.). Available for both components and sections (sections have a restricted whitelist). |\n| `ENUM` | Dropdown selector | `string` | Enum-based style type (flex-direction, justify-content, align-items, etc.). Uses `enumTypeId`. Available for both components and sections. |\n| `COMPONENT` | Component slot | `any` | A single child component slot. Store owners can place a component in this slot from the editor. Render with `<IkasComponentRenderer>`. Before using, see `get_migration_guide(\"component-composition-decision-guide\")`. |\n| `COMPONENT_LIST` | Component list slot | `any` | A list of child components. Store owners can add multiple components from the editor. Render with `<IkasComponentRenderer>`. Before using, see `get_migration_guide(\"component-composition-decision-guide\")`. |\n\n### COMPONENT & COMPONENT_LIST props (child component slots)\n\nThese prop types enable **slot-based** architectures where store owners can drag child components into your section/component from the editor.\n\n- `COMPONENT` \u2014 a single child component slot\n- `COMPONENT_LIST` \u2014 a list of child components\n\n**Rendering:** Use the `<IkasComponentRenderer>` wrapper from `@ikas/bp-storefront`:\n```tsx\nimport { IkasComponentRenderer } from \"@ikas/bp-storefront\";\nimport { Props } from \"./types\";\n\nexport function MySection({ title, cardList, ...props }: Props) {\n return (\n <section>\n <h2>{title}</h2>\n <div className=\"cards\">\n {/* COMPONENT_LIST \u2014 render a list of child components */}\n <IkasComponentRenderer\n id=\"card-list\"\n components={cardList as any[]}\n parentProps={props}\n />\n </div>\n </section>\n );\n}\nexport default MySection;\n```\n\n**Key rules:**\n- Always pass `parentProps={props}` so child components can access parent data via dynamic values\n- Cast the prop value: `components={myList as any[]}` for COMPONENT_LIST, `components={[myComp] as any[]}` for COMPONENT\n- `<IkasComponentRenderer>` handles rendering, styling, and reactivity of child components automatically\n\n**Config example (COMPONENT_LIST):**\n```json\n{\n \"name\": \"cardList\",\n \"displayName\": \"Card List\",\n \"type\": \"COMPONENT_LIST\"\n}\n```\n\n**Config example (COMPONENT):**\n```json\n{\n \"name\": \"headerSlot\",\n \"displayName\": \"Header Slot\",\n \"type\": \"COMPONENT\"\n}\n```\n\n### Restricting which components can appear (`filteredComponentIds`)\n\nA COMPONENT or COMPONENT_LIST prop may include a `filteredComponentIds: string[]` field to limit which sibling components can be placed inside it. Each entry must be the **opaque random id** of a component already defined in `ikas.config.json` \u2014 e.g. `\"7ojrigep-Eml9n5sN3i\"`.\n\n**Ids are not derivable from component names. Do NOT compose them as `${projectId}-${name}`.** The CLI will reject any unknown id with a structured error listing the valid `{id, name}` pairs.\n\n**Recommended workflow:**\n\n1. Create the child component(s) first. The CLI prints the new id in its JSON response:\n ```bash\n npx ikas-component config add-component --name Navbar --type component\n # \u2192 { \"success\": true, \"componentId\": \"7ojrigep-Eml9n5sN3i\", ... }\n ```\n2. Capture the `componentId` and reuse it in the parent's `filteredComponentIds`:\n ```bash\n npx ikas-component config add-component --name Header --type section \\\n --props '[{\"name\":\"components\",\"type\":\"COMPONENT_LIST\",\"filteredComponentIds\":[\"7ojrigep-Eml9n5sN3i\"]}]'\n ```\n\nOr create the parent first without `filteredComponentIds`, then attach it once children exist:\n```bash\nnpx ikas-component config add-prop Header components --type COMPONENT_LIST \\\n --filteredComponentIds '[\"7ojrigep-Eml9n5sN3i\"]'\n```\n\n**Discovering existing ids:** `npx ikas-component config list` returns a JSON document with every component's `{ id, name, type, props\u2026 }`. Use that when joining a project mid-flight.\n\n### IMAGE type example:\n```json\n{\n \"name\": \"heroImage\",\n \"displayName\": \"Hero Image\",\n \"type\": \"IMAGE\",\n \"required\": false\n}\n```\nAccess in component: `getDefaultSrc(props.heroImage)` (import `getDefaultSrc` from `@ikas/bp-storefront`)\n\n### Prop grouping\nProps can be assigned to groups via `groupId` for organized editor sidebar display. See `get_framework_guide(\"prop-groups\")` for details.\n\n### Style Props: TYPE and ENUM\n\nThere are two prop types for style values:\n\n- **TYPE** \u2014 Structured types with numeric values and units (padding, margin, border-radius, sizes, etc.). Uses `typeId`.\n- **ENUM** \u2014 Enum types rendered as dropdown selectors (flex-direction, justify-content, align-items, etc.). Uses `enumTypeId`.\n\nBoth are available for components and sections. For sections, TYPE props are limited to a whitelist of style types. Use `list-types --component-type section` to see section-allowed types.\n\n### TYPE prop (structured types)\nThe `TYPE` prop lets you use structured storefront types like PaddingStyleType, MarginStyleType, SizeStyleType, etc. Available for both components and sections (sections have a restricted whitelist of style types).\n\n**Workflow:**\n1. Run `npx ikas-component config list-types` to get available types (requires dev server running with editor connected)\n2. Use the `typeId` from the output when adding the prop\n3. For sections: `npx ikas-component config list-types --component-type section` to see only section-allowed types\n\n**Example config:**\n```json\n{\n \"name\": \"spacing\",\n \"displayName\": \"Spacing\",\n \"type\": \"TYPE\",\n \"typeId\": \"@ikas/bp-storefront-models-PaddingStyleType\"\n}\n```\n\n**Array example** (append `_array` to typeId):\n```json\n{\n \"name\": \"margins\",\n \"displayName\": \"Margins\",\n \"type\": \"TYPE\",\n \"typeId\": \"@ikas/bp-storefront-models-MarginStyleType_array\"\n}\n```\nThis generates `margins?: MarginStyleType[]` in types.ts.\n\n**CLI command:**\n```bash\nnpx ikas-component config add-prop --component MyComp --name spacing --displayName Spacing --type TYPE --typeId \"@ikas/bp-storefront-models-PaddingStyleType\"\n```\n\n### ENUM prop (enum style types)\nThe `ENUM` prop lets you use enum-based types that render as dropdown selectors. There are two kinds of enum types:\n\n1. **Built-in enums** (prefix `@ikas/`): FlexDirectionStyleType, JustifyContentStyleType, AlignItemsStyleType, etc. Available from `list-types` when editor is connected.\n2. **Custom enums**: Created with `config add-enum`. Works offline, no editor needed.\n\n**IMPORTANT:** `add-prop --type ENUM` validates that the `enumTypeId` references an existing enum. You MUST create custom enums first with `config add-enum` before using them. The command will reject unknown enum IDs.\n\n**Custom enum workflow (recommended for AI-driven generation):**\n```bash\n# Step 1: Create the enum FIRST\nnpx ikas-component config add-enum --name \"AspectRatio\" --options '{\"Square\":\"1/1\",\"Landscape\":\"16/9\",\"Portrait\":\"3/4\"}'\n# Returns: {\"success\":true,\"enumId\":\"aBcDeFgHiJ\",...}\n\n# Step 2: Use the returned enumId in add-prop\nnpx ikas-component config add-prop --component MyComp --name aspectRatio --displayName \"Aspect Ratio\" --type ENUM --enumTypeId aBcDeFgHiJ\n```\n\n**Built-in enum workflow:**\n1. Run `npx ikas-component config list-types` \u2014 enum types have `category: \"enum\"` in the output\n2. Use the `enumTypeId` from the output when adding the prop\n\n**Example config (built-in enum):**\n```json\n{\n \"name\": \"direction\",\n \"displayName\": \"Direction\",\n \"type\": \"ENUM\",\n \"enumTypeId\": \"@ikas/bp-storefront-models-FlexDirectionStyleType\"\n}\n```\nThis generates `direction?: string` in types.ts.\n\n**CLI command (built-in enum):**\n```bash\nnpx ikas-component config add-prop --component MyComp --name direction --displayName Direction --type ENUM --enumTypeId \"@ikas/bp-storefront-models-FlexDirectionStyleType\"\n```\n\n**Note:** `list-types` requires dev server with editor connected. Custom enums (`config add-enum`) work offline.",
|
|
39
39
|
"tags": [
|
|
40
40
|
"props",
|
|
41
41
|
"types",
|
|
@@ -67,7 +67,7 @@
|
|
|
67
67
|
"component-structure": {
|
|
68
68
|
"title": "Component Structure",
|
|
69
69
|
"description": "How to write a Preact component for ikas",
|
|
70
|
-
"content": "Each component consists of three files. There are two patterns: **components** (child elements) and **sections** (page-level containers).\n\n## Component Pattern (child elements like buttons, cards, badges)\n\n### index.tsx - Component Implementation\n```tsx\nimport { Props } from \"./types\";\n\nexport function MyComponent({ title, showButton }: Props) {\n return (\n <div className=\"my-component\">\n <h1>{title}</h1>\n {showButton && <button>Click me</button>}\n </div>\n );\n}\n\nexport default MyComponent;\n```\n\n### types.ts - Props Interface\n```typescript\nexport interface Props {\n title: string; // TEXT prop -> string\n showButton?: boolean; // optional BOOLEAN prop\n count: number; // NUMBER prop -> number\n}\n```\n\n### styles.css\n```css\n.my-component {\n padding: 16px;\n}\n\n.my-component h1 {\n font-size: 24px;\n}\n```\n\n## Section Pattern (page-level containers like headers, hero banners, footers)\n\nSections use a `<section>` root element, full-width styling, and a `Props` interface. In `ikas.config.json` they have `\"type\": \"section\"`. **Every section MUST include a `backgroundColor` COLOR prop** (default: `#ffffff`) so store owners can customize the section background. Consider also adding `textColor` COLOR props for text elements sitting directly on the section background, so they remain readable when the background changes.\n\n### index.tsx - Section Implementation\n```tsx\nimport { Props } from \"./types\";\n\nexport function HeroBanner({ heading, subtitle, backgroundColor }: Props) {\n return (\n <section className=\"hero-banner\" style={backgroundColor ? { backgroundColor } : undefined}>\n <div className=\"hero-banner-inner\">\n <h2>{heading}</h2>\n {subtitle && <p>{subtitle}</p>}\n </div>\n </section>\n );\n}\n\nexport default HeroBanner;\n```\n\n### types.ts - Section Props Interface\n```typescript\nexport interface Props {\n heading: string;\n subtitle?: string;\n backgroundColor?: string;\n}\n```\n\n### styles.css - Full-width section styles\n```css\n.hero-banner {\n width: 100%;\n padding: 64px 24px;\n}\n\n.hero-banner-inner {\n max-width: 1200px;\n margin: 0 auto;\n}\n```\n\n## Key Rules\n- Export the component as both a named export and a default export: `export function X() {...}` + `export default X;`\n- Use Preact (not React) - but JSX syntax is the same\n- Import types from `./types` for props\n- Use `className` not `class` for CSS classes\n- Storefront functions and types come from `@ikas/bp-storefront`\n- CSS classes are automatically scoped to your component at build time. Use plain CSS class selectors - they won't conflict with other components or the page.\n- **All user-visible text MUST be TEXT props**
|
|
70
|
+
"content": "Each component consists of three files. There are two patterns: **components** (child elements) and **sections** (page-level containers).\n\n## Component Pattern (child elements like buttons, cards, badges)\n\n### index.tsx - Component Implementation\n```tsx\nimport { Props } from \"./types\";\n\nexport function MyComponent({ title, showButton }: Props) {\n return (\n <div className=\"my-component\">\n <h1>{title}</h1>\n {showButton && <button>Click me</button>}\n </div>\n );\n}\n\nexport default MyComponent;\n```\n\n### types.ts - Props Interface\n```typescript\nexport interface Props {\n title: string; // TEXT prop -> string\n showButton?: boolean; // optional BOOLEAN prop\n count: number; // NUMBER prop -> number\n}\n```\n\n### styles.css\n```css\n.my-component {\n padding: 16px;\n}\n\n.my-component h1 {\n font-size: 24px;\n}\n```\n\n## Section Pattern (page-level containers like headers, hero banners, footers)\n\nSections use a `<section>` root element, full-width styling, and a `Props` interface. In `ikas.config.json` they have `\"type\": \"section\"`. **Every section MUST include a `backgroundColor` COLOR prop** (default: `#ffffff`) so store owners can customize the section background. Consider also adding `textColor` COLOR props for text elements sitting directly on the section background, so they remain readable when the background changes.\n\n### index.tsx - Section Implementation\n```tsx\nimport { Props } from \"./types\";\n\nexport function HeroBanner({ heading, subtitle, backgroundColor }: Props) {\n return (\n <section className=\"hero-banner\" style={backgroundColor ? { backgroundColor } : undefined}>\n <div className=\"hero-banner-inner\">\n <h2>{heading}</h2>\n {subtitle && <p>{subtitle}</p>}\n </div>\n </section>\n );\n}\n\nexport default HeroBanner;\n```\n\n### types.ts - Section Props Interface\n```typescript\nexport interface Props {\n heading: string;\n subtitle?: string;\n backgroundColor?: string;\n}\n```\n\n### styles.css - Full-width section styles\n```css\n.hero-banner {\n width: 100%;\n padding: 64px 24px;\n}\n\n.hero-banner-inner {\n max-width: 1200px;\n margin: 0 auto;\n}\n```\n\n## Key Rules\n- Export the component as both a named export and a default export: `export function X() {...}` + `export default X;`\n- Use Preact (not React) - but JSX syntax is the same\n- Import types from `./types` for props\n- Use `className` not `class` for CSS classes\n- Storefront functions and types come from `@ikas/bp-storefront`\n- CSS classes are automatically scoped to your component at build time. Use plain CSS class selectors - they won't conflict with other components or the page.\n- **All user-visible text MUST be TEXT props** \u2014 never hardcode strings in JSX. This ensures all text is translatable. Use `defaultValue` in config for English defaults.\n- **Sections MUST include a `backgroundColor` COLOR prop** (default: `#ffffff`). Apply via `style={backgroundColor ? { backgroundColor } : undefined}` on the root `<section>` element. Optionally add `textColor` for text directly on the section background.\n\n## Root Components Are Automatically Reactive\n\nThe ikas runtime wraps every root component render in a MobX `autorun()`, so **root exports are already reactive** \u2014 they automatically re-render when any MobX store they read (`cartStore`, `customerStore`, etc.) changes. You do **not** need `observer()` on root component exports.\n\n### Correct: Plain root export reading stores\n```tsx\nimport { cartStore } from \"@ikas/bp-storefront\";\nimport { Props } from \"./types\";\n\nexport function CartSummary({ title }: Props) {\n const itemCount = cartStore.cart?.orderLineItems.length ?? 0;\n return (\n <section className=\"cart-summary\">\n <h2>{title}</h2>\n <p>{itemCount} items in cart</p>\n </section>\n );\n}\n\nexport default CartSummary;\n```\n\n### Anti-pattern: Do NOT wrap root exports with observer\n```tsx\n// WRONG \u2014 observer() is redundant on root components\nconst CartSummary = observer(function CartSummary({ title }: Props) {\n ...\n});\nexport default CartSummary;\n```\n\n## Using observer for Sub-Components\n\nWhen you extract a **sub-component** that independently reads MobX stores, wrap it with `observer()` from `@ikas/component-utils` so it re-renders when store data changes.\n\n### When to use observer\n- A **sub-component** (not the root export) reads `cartStore.cart`, `customerStore.customer`, or any other MobX observable\n- You extract part of a component into a separate function that needs independent reactivity\n\n### When observer is NOT needed\n- **Root component exports** \u2014 the ikas runtime handles reactivity via `autorun()`\n- Components that only use props passed from parent \u2014 no store reads\n- Static components with no reactive data\n\n### Example: Root export with observer sub-component\n```tsx\nimport { observer } from \"@ikas/component-utils\";\nimport { cartStore } from \"@ikas/bp-storefront\";\nimport { Props } from \"./types\";\n\n// Sub-component: needs observer() for independent reactivity\nconst CartBadge = observer(function CartBadge() {\n const itemCount = cartStore.cart?.orderLineItems.length ?? 0;\n return <span className=\"cart-badge\">{itemCount}</span>;\n});\n\n// Root export: NO observer needed \u2014 autorun() handles reactivity\nexport function Header({ logo }: Props) {\n return (\n <header>\n <img src={logo} alt=\"Logo\" />\n <CartBadge />\n </header>\n );\n}\n\nexport default Header;\n```\n\n**Important:** When using `observer` on sub-components, define the component as a named function expression (not arrow function) and assign it to a `const`. This ensures proper display names in React DevTools.\n\n## Sub-Component File Organization\n\nWhen you extract sub-components, **ALWAYS** place them in `src/sub-components/` with their own folder containing `index.tsx` and `styles.css`. Never create flat `.tsx` files inside a component folder. See `get_framework_guide(\"sub-component-patterns\")` for the full pattern, directory structure, CSS @import rules, and examples.",
|
|
71
71
|
"tags": [
|
|
72
72
|
"component",
|
|
73
73
|
"preact",
|
|
@@ -100,7 +100,7 @@
|
|
|
100
100
|
"css-scoping": {
|
|
101
101
|
"title": "CSS Scoping Rules",
|
|
102
102
|
"description": "How CSS is scoped to prevent conflicts between components",
|
|
103
|
-
"content": "CSS files referenced in `ikas.config.json` are automatically scoped at build time.\n\n### How it works:\n- Each component's CSS selectors are prefixed with a unique scope identifier\n- `.my-class` becomes `.[scope]-my-class` in the output\n- This prevents style conflicts between different components on the same page\n\n### Best practices:\n- Use class selectors (`.my-class`) rather than element selectors (`div`, `h1`)\n- Avoid using `!important` - scoping handles isolation\n- Don't use global selectors or `@global` - they won't be scoped\n- Keep CSS in the component's `styles.css` file referenced in config\n- Nested selectors work: `.parent .child` both get scoped\n\n### Example:\n```css\n/* What you write */\n.product-card {\n padding: 16px;\n border: 1px solid #eee;\n}\n\n.product-card .title {\n font-size: 18px;\n font-weight: bold;\n}\n\n.product-card .price {\n color: #e53e3e;\n}\n```\n\nThe build system transforms this so your `.product-card`, `.title`, and `.price` classes are unique to your component and won't clash with any other component or theme styles.\n\n### Global CSS Exception\n\nThe one intentional exception to scoping is **Global CSS**
|
|
103
|
+
"content": "CSS files referenced in `ikas.config.json` are automatically scoped at build time.\n\n### How it works:\n- Each component's CSS selectors are prefixed with a unique scope identifier\n- `.my-class` becomes `.[scope]-my-class` in the output\n- This prevents style conflicts between different components on the same page\n\n### Best practices:\n- Use class selectors (`.my-class`) rather than element selectors (`div`, `h1`)\n- Avoid using `!important` - scoping handles isolation\n- Don't use global selectors or `@global` - they won't be scoped\n- Keep CSS in the component's `styles.css` file referenced in config\n- Nested selectors work: `.parent .child` both get scoped\n\n### Example:\n```css\n/* What you write */\n.product-card {\n padding: 16px;\n border: 1px solid #eee;\n}\n\n.product-card .title {\n font-size: 18px;\n font-weight: bold;\n}\n\n.product-card .price {\n color: #e53e3e;\n}\n```\n\nThe build system transforms this so your `.product-card`, `.title`, and `.price` classes are unique to your component and won't clash with any other component or theme styles.\n\n### Global CSS Exception\n\nThe one intentional exception to scoping is **Global CSS** \u2014 project-wide styles defined in `src/global.css` that are loaded unscoped before any component CSS. Global CSS is ideal for CSS custom properties (variables), resets, and shared utility classes. The loading order is: **Global CSS \u2192 Shared chunk CSS \u2192 Component CSS**, so global styles have the lowest specificity and component styles always win.\n\nSee `get_framework_guide(\"global-css\")` for full details on global CSS setup and usage.",
|
|
104
104
|
"tags": [
|
|
105
105
|
"css",
|
|
106
106
|
"scoping",
|
|
@@ -113,7 +113,7 @@
|
|
|
113
113
|
"global-css": {
|
|
114
114
|
"title": "Global CSS",
|
|
115
115
|
"description": "Project-wide unscoped CSS for variables, resets, and shared utilities",
|
|
116
|
-
"content": "## Global CSS\n\nGlobal CSS lets you define project-wide styles that are shared across all components
|
|
116
|
+
"content": "## Global CSS\n\nGlobal CSS lets you define project-wide styles that are shared across all components \u2014 without scoping. This is ideal for:\n\n- **CSS custom properties (variables)** \u2014 colors, spacing, typography tokens\n- **CSS resets** \u2014 normalize/reset base styles\n- **Base typography** \u2014 font-family, line-height defaults\n- **Utility classes** \u2014 common layout helpers\n\n### Setup\n\n1. Create a `src/global.css` file in your project root:\n\n```css\n/* src/global.css */\n:root {\n --color-primary: #2563eb;\n --color-secondary: #7c3aed;\n --color-text: #1f2937;\n --color-text-muted: #6b7280;\n --color-border: #e5e7eb;\n --color-background: #ffffff;\n\n --spacing-xs: 4px;\n --spacing-sm: 8px;\n --spacing-md: 16px;\n --spacing-lg: 24px;\n --spacing-xl: 48px;\n\n --font-sans: system-ui, -apple-system, sans-serif;\n --font-mono: ui-monospace, monospace;\n\n --radius-sm: 4px;\n --radius-md: 8px;\n --radius-lg: 16px;\n}\n```\n\n2. Add the `globalStyles` field to `ikas.config.json`:\n\n```json\n{\n \"name\": \"my-project\",\n \"version\": \"1.0.0\",\n \"globalStyles\": \"./src/global.css\",\n \"components\": [...]\n}\n```\n\n### Using Global CSS Variables in Components\n\nOnce defined in `global.css`, CSS custom properties are available in any component's `styles.css`:\n\n```css\n/* src/components/ProductCard/styles.css */\n.product-card {\n border: 1px solid var(--color-border);\n border-radius: var(--radius-md);\n padding: var(--spacing-md);\n font-family: var(--font-sans);\n}\n\n.product-card .title {\n color: var(--color-text);\n}\n\n.product-card .price {\n color: var(--color-primary);\n}\n```\n\n### Loading Order\n\nCSS is loaded in this order, from lowest to highest specificity:\n\n1. **Global CSS** (`global.css`) \u2014 unscoped, loaded first\n2. **Shared chunk CSS** \u2014 scoped styles from shared internal components\n3. **Component CSS** (`styles.css`) \u2014 scoped per-component styles\n\nBecause global CSS loads first and is unscoped, component-scoped styles will always override global styles when selectors conflict. This means you can safely define base defaults in `global.css` and override them in component `styles.css` files.\n\n### Key Differences from Component CSS\n\n| | Global CSS | Component CSS |\n|---|---|---|\n| **File** | `src/global.css` | `src/components/X/styles.css` |\n| **Scoping** | NOT scoped (no `.cc_` prefix) | Scoped with `.cc_{componentId}` prefix |\n| **Config** | `globalStyles` in ikas.config.json | `styles` on each component |\n| **Purpose** | CSS variables, resets, utilities | Component-specific styles |\n| **Load order** | First (lowest specificity) | Last (highest specificity) |\n\n### Best Practices\n\n- **Use CSS custom properties** for design tokens (colors, spacing, fonts) \u2014 they cascade naturally and components can reference them\n- **Keep global CSS minimal** \u2014 only put truly shared values here. Component-specific styles belong in the component's `styles.css`\n- **Avoid class selectors in global CSS** that target component internals \u2014 use custom properties instead for themeable values\n- **Don't duplicate** \u2014 if a style only applies to one component, put it in that component's `styles.css`\n\n### Project Structure with Global CSS\n\n```\nmy-project/\n\u251c\u2500\u2500 src/\n\u2502 \u251c\u2500\u2500 global.css # Project-wide unscoped CSS\n\u2502 \u2514\u2500\u2500 components/\n\u2502 \u251c\u2500\u2500 ProductCard/\n\u2502 \u2502 \u251c\u2500\u2500 index.tsx\n\u2502 \u2502 \u251c\u2500\u2500 types.ts\n\u2502 \u2502 \u2514\u2500\u2500 styles.css # Uses var(--color-primary) etc.\n\u2502 \u2514\u2500\u2500 index.ts\n\u251c\u2500\u2500 ikas.config.json # Has globalStyles: \"./src/global.css\"\n\u251c\u2500\u2500 package.json\n\u2514\u2500\u2500 tsconfig.json\n```\n\nSee `get_framework_guide(\"css-scoping\")` for details on how component CSS scoping works.",
|
|
117
117
|
"tags": [
|
|
118
118
|
"css",
|
|
119
119
|
"global",
|
|
@@ -142,7 +142,7 @@
|
|
|
142
142
|
"imports": {
|
|
143
143
|
"title": "Import Patterns",
|
|
144
144
|
"description": "How to import storefront functions and types",
|
|
145
|
-
"content": "### Storefront Functions & Types\nImport API functions and TypeScript types from `@ikas/bp-storefront`:\n```typescript\nimport {\n // Product functions\n getSelectedProductVariant,\n getDisplayedProductVariantTypes,\n selectVariantValue,\n hasProductVariantStock,\n getProductVariantFormattedFinalPrice,\n \n // Cart functions\n addItemToCart,\n cartStore,\n \n // Navigation\n Router,\n \n // Customer\n customerStore,\n customerLogin,\n hasCustomer,\n \n // Favorites\n addIkasProductToFavorites,\n isFavoriteIkasProduct,\n\n // Type models\n IkasProduct,\n IkasProductVariant,\n IkasCart,\n IkasOrderLineItem,\n IkasCustomer,\n IkasOrder,\n IkasCategory,\n IkasBrand,\n IkasBlog\n} from \"@ikas/bp-storefront\";\n```\n\n### Store Instances\nPre-initialized MobX stores:\n```typescript\nimport { cartStore, customerStore } from \"@ikas/bp-storefront\";\n\n// Cart data\ncartStore.cart?.orderLineItems\n\n// Customer data \ncustomerStore.customer?.email\n```\n\n### Observer (for sub-components with reactive updates)\n```typescript\nimport { observer } from \"@ikas/component-utils\";\nimport { cartStore } from \"@ikas/bp-storefront\";\nimport { Props } from \"./types\";\n\n// Sub-component: needs observer() for independent reactivity\nconst CartBadge = observer(function CartBadge() {\n const itemCount = cartStore.cart?.orderLineItems.length ?? 0;\n return <span>Cart: {itemCount}</span>;\n});\n\n// Root export: plain function
|
|
145
|
+
"content": "### Storefront Functions & Types\nImport API functions and TypeScript types from `@ikas/bp-storefront`:\n```typescript\nimport {\n // Product functions\n getSelectedProductVariant,\n getDisplayedProductVariantTypes,\n selectVariantValue,\n hasProductVariantStock,\n getProductVariantFormattedFinalPrice,\n \n // Cart functions\n addItemToCart,\n cartStore,\n \n // Navigation\n Router,\n \n // Customer\n customerStore,\n customerLogin,\n hasCustomer,\n \n // Favorites\n addIkasProductToFavorites,\n isFavoriteIkasProduct,\n\n // Type models\n IkasProduct,\n IkasProductVariant,\n IkasCart,\n IkasOrderLineItem,\n IkasCustomer,\n IkasOrder,\n IkasCategory,\n IkasBrand,\n IkasBlog\n} from \"@ikas/bp-storefront\";\n```\n\n### Store Instances\nPre-initialized MobX stores:\n```typescript\nimport { cartStore, customerStore } from \"@ikas/bp-storefront\";\n\n// Cart data\ncartStore.cart?.orderLineItems\n\n// Customer data \ncustomerStore.customer?.email\n```\n\n### Observer (for sub-components with reactive updates)\n```typescript\nimport { observer } from \"@ikas/component-utils\";\nimport { cartStore } from \"@ikas/bp-storefront\";\nimport { Props } from \"./types\";\n\n// Sub-component: needs observer() for independent reactivity\nconst CartBadge = observer(function CartBadge() {\n const itemCount = cartStore.cart?.orderLineItems.length ?? 0;\n return <span>Cart: {itemCount}</span>;\n});\n\n// Root export: plain function \u2014 ikas runtime handles reactivity via autorun()\nexport function MySection({ title }: Props) {\n return (\n <div>\n <h1>{title}</h1>\n <CartBadge />\n </div>\n );\n}\n\nexport default MySection;\n```\n\nDo **NOT** wrap root component exports with `observer()`. The ikas runtime handles root reactivity automatically via `autorun()`. Only wrap **sub-components** with `observer()` when they independently read from stores (`cartStore`, `customerStore`).",
|
|
146
146
|
"tags": [
|
|
147
147
|
"import",
|
|
148
148
|
"storefront",
|
|
@@ -155,7 +155,7 @@
|
|
|
155
155
|
"sections-vs-components": {
|
|
156
156
|
"title": "Sections vs Components",
|
|
157
157
|
"description": "The difference between sections (page-level containers) and components (child elements)",
|
|
158
|
-
"content": "ikas code components have two types: **sections** and **components**.\n\n## Sections\nSections are page-level, full-width containers that make up the structure of a page. Examples: Header, Hero Banner, Footer, Product Grid, Featured Collection.\n\n- Set `\"type\": \"section\"` in `ikas.config.json`\n- Use a `<section>` root element with full-width styling\n- Name the props interface `Props` in `types.ts`\n- Typically have full-width layout with inner max-width container\n- Styles: `width: 100%; padding: 64px 24px;` with `.inner { max-width: 1200px; margin: 0 auto; }`\n- **MUST include a `backgroundColor` COLOR prop** (default: `#ffffff`). Apply via inline style on the `<section>` element: `style={backgroundColor ? { backgroundColor } : undefined}`\n- Consider adding `textColor` COLOR props for text elements sitting directly on the section background, so they remain readable when the background changes\n\n## Components\nComponents are child elements that are placed inside sections. Examples: Button, Product Card, Badge, Price Display, Image Gallery.\n\n- No `type` field needed in `ikas.config.json` (defaults to `\"component\"`)\n- Use a `<div>` root element\n- Name the props interface `Props` in `types.ts`\n- Sized by their content or parent container\n\n## Config Difference\nThe only config difference is the `type` field on the component definition:\n\n```json\n{\n \"components\": [\n {\n \"id\": \"product-card\",\n \"name\": \"Product Card\",\n \"entry\": \"./src/components/ProductCard/index.tsx\",\n \"styles\": \"./src/components/ProductCard/styles.css\",\n \"props\": []\n },\n {\n \"id\": \"hero-banner\",\n \"name\": \"Hero Banner\",\n \"type\": \"section\",\n \"entry\": \"./src/components/HeroBanner/index.tsx\",\n \"styles\": \"./src/components/HeroBanner/styles.css\",\n \"props\": []\n }\n ]\n}\n```\n\n## Required Section Prop: backgroundColor\n\nEvery section MUST include a `backgroundColor` COLOR prop so store owners can customize the section background.\n\n### Config:\n```json\n{ \"name\": \"backgroundColor\", \"displayName\": \"Background Color\", \"type\": \"COLOR\", \"defaultValue\": \"#ffffff\" }\n```\n\n### Usage in index.tsx:\n```tsx\nexport function MySection({ title, backgroundColor = \"#ffffff\" }: Props) {\n return (\n <section className=\"my-section\" style={backgroundColor ? { backgroundColor } : undefined}>\n <div className=\"my-section-inner\">\n <h1>{title}</h1>\n </div>\n </section>\n );\n}\n```\n\nOptionally, also add `textColor` and/or `headingColor` COLOR props for text elements sitting directly on the section background. This ensures text remains readable when the store owner changes the background color.\n\n## Header & Footer Sections\n\nTo designate a section as the store's header or footer, add `isHeader: true` or `isFooter: true` in `ikas.config.json`. This tells the editor to treat the section as a common header/footer that appears on all pages automatically.\n\n```json\n{\n \"id\": \"header\",\n \"name\": \"Header\",\n \"type\": \"section\",\n \"isHeader\": true,\n \"entry\": \"./src/components/Header/index.tsx\",\n \"styles\": \"./src/components/Header/styles.css\",\n \"props\": [...]\n}\n```\n\nOnly one component should have `isHeader: true` and one should have `isFooter: true`. These flags only apply to section-type components.\n\n## Container Sections (sections that host children via COMPONENT_LIST)\n\nSome sections are **containers**
|
|
158
|
+
"content": "ikas code components have two types: **sections** and **components**.\n\n## Sections\nSections are page-level, full-width containers that make up the structure of a page. Examples: Header, Hero Banner, Footer, Product Grid, Featured Collection.\n\n- Set `\"type\": \"section\"` in `ikas.config.json`\n- Use a `<section>` root element with full-width styling\n- Name the props interface `Props` in `types.ts`\n- Typically have full-width layout with inner max-width container\n- Styles: `width: 100%; padding: 64px 24px;` with `.inner { max-width: 1200px; margin: 0 auto; }`\n- **MUST include a `backgroundColor` COLOR prop** (default: `#ffffff`). Apply via inline style on the `<section>` element: `style={backgroundColor ? { backgroundColor } : undefined}`\n- Consider adding `textColor` COLOR props for text elements sitting directly on the section background, so they remain readable when the background changes\n\n## Components\nComponents are child elements that are placed inside sections. Examples: Button, Product Card, Badge, Price Display, Image Gallery.\n\n- No `type` field needed in `ikas.config.json` (defaults to `\"component\"`)\n- Use a `<div>` root element\n- Name the props interface `Props` in `types.ts`\n- Sized by their content or parent container\n\n## Config Difference\nThe only config difference is the `type` field on the component definition:\n\n```json\n{\n \"components\": [\n {\n \"id\": \"product-card\",\n \"name\": \"Product Card\",\n \"entry\": \"./src/components/ProductCard/index.tsx\",\n \"styles\": \"./src/components/ProductCard/styles.css\",\n \"props\": []\n },\n {\n \"id\": \"hero-banner\",\n \"name\": \"Hero Banner\",\n \"type\": \"section\",\n \"entry\": \"./src/components/HeroBanner/index.tsx\",\n \"styles\": \"./src/components/HeroBanner/styles.css\",\n \"props\": []\n }\n ]\n}\n```\n\n## Required Section Prop: backgroundColor\n\nEvery section MUST include a `backgroundColor` COLOR prop so store owners can customize the section background.\n\n### Config:\n```json\n{ \"name\": \"backgroundColor\", \"displayName\": \"Background Color\", \"type\": \"COLOR\", \"defaultValue\": \"#ffffff\" }\n```\n\n### Usage in index.tsx:\n```tsx\nexport function MySection({ title, backgroundColor = \"#ffffff\" }: Props) {\n return (\n <section className=\"my-section\" style={backgroundColor ? { backgroundColor } : undefined}>\n <div className=\"my-section-inner\">\n <h1>{title}</h1>\n </div>\n </section>\n );\n}\n```\n\nOptionally, also add `textColor` and/or `headingColor` COLOR props for text elements sitting directly on the section background. This ensures text remains readable when the store owner changes the background color.\n\n## Header & Footer Sections\n\nTo designate a section as the store's header or footer, add `isHeader: true` or `isFooter: true` in `ikas.config.json`. This tells the editor to treat the section as a common header/footer that appears on all pages automatically.\n\n```json\n{\n \"id\": \"header\",\n \"name\": \"Header\",\n \"type\": \"section\",\n \"isHeader\": true,\n \"entry\": \"./src/components/Header/index.tsx\",\n \"styles\": \"./src/components/Header/styles.css\",\n \"props\": [...]\n}\n```\n\nOnly one component should have `isHeader: true` and one should have `isFooter: true`. These flags only apply to section-type components.\n\n## Container Sections (sections that host children via COMPONENT_LIST)\n\nSome sections are **containers** \u2014 they don't render content directly, they host other components in editable slots. Headers, footers, and product-detail sections are the canonical examples.\n\nA container section's config has a `COMPONENT_LIST` (or `COMPONENT`) prop with `filteredComponentIds` restricting which siblings can be placed in the slot:\n\n```json\n{\n \"name\": \"components\",\n \"type\": \"COMPONENT_LIST\",\n \"filteredComponentIds\": [\"<NAVBAR_ID>\", \"<ANNOUNCEMENTS_ID>\"]\n}\n```\n\nBuilding a container section is **always** a 3-step recipe \u2014 you cannot do it in one CLI call because the parent's `filteredComponentIds` must reference child ids that don't exist until the children are created:\n\n1. **Create each child component first** with `config add-component --type component`. Capture the `componentId` returned in each JSON response \u2014 those are the opaque ids you'll wire into the parent.\n2. **Create the parent section** with `config add-component --type section` (omit `filteredComponentIds` at this point \u2014 they're wired in step 3).\n3. **Wire the slot** with `config update-prop --component <Parent> --prop <slotName> --filteredComponentIds '[\"<CHILD_1_ID>\", \"<CHILD_2_ID>\"]'`.\n\nSkipping step 3 leaves the slot unrestricted and the section non-functional. The CLI rejects unknown ids with a structured error listing valid `{id, name}` pairs, so there is no silent failure mode. See `get_framework_guide(\"header-footer-patterns\")` for a complete worked example, or call `get_section_template(\"header-section\")` for an auto-generated recipe.\n\n## How to Decide\n- Will it span the full width of the page and sit at the top level? \u2192 **Section**\n- Will it be placed inside another container or repeated in a list? \u2192 **Component**\n- Is it the store's main header or footer, or a multi-slot layout? \u2192 **Container section** with one or more COMPONENT_LIST props \u2014 follow the 3-step recipe above\n- Is it the store's main header or footer? \u2192 **Section** with `isHeader`/`isFooter`",
|
|
159
159
|
"tags": [
|
|
160
160
|
"section",
|
|
161
161
|
"component",
|
|
@@ -173,7 +173,7 @@
|
|
|
173
173
|
"common-pitfalls": {
|
|
174
174
|
"title": "Common Pitfalls",
|
|
175
175
|
"description": "Frequent mistakes LLMs and developers make when building ikas code components",
|
|
176
|
-
"content": "## Common Pitfalls\n\nThese are the most common mistakes when building ikas code components. Avoid them for correct, working code.\n\n### 1. Root Component Should NOT Use observer\n\nThe ikas runtime wraps root component renders in `autorun()`, making them automatically reactive. Wrapping a root export with `observer()` is redundant and misleading.\n\n**Wrong** — observer on root export:\n```tsx\nimport { observer } from \"@ikas/component-utils\";\nimport { cartStore } from \"@ikas/bp-storefront\";\n\nconst CartSection = observer(function CartSection({ title }: Props) {\n const items = cartStore.cart?.orderLineItems ?? [];\n return <section>{title}: {items.length} items</section>;\n});\nexport default CartSection;\n```\n\n**Correct** — plain named export:\n```tsx\nimport { cartStore } from \"@ikas/bp-storefront\";\n\nexport function CartSection({ title }: Props) {\n const items = cartStore.cart?.orderLineItems ?? [];\n return <section>{title}: {items.length} items</section>;\n}\n\nexport default CartSection;\n```\n\n### 2. Observer Sub-Component Naming\n\nWhen using `observer()` on **sub-components**, use a named function expression — not an arrow function — for proper DevTools display names.\n\n**Wrong** — arrow function loses display name:\n```tsx\nconst CartBadge = observer(() => {\n return <span>{cartStore.cart?.orderLineItems.length ?? 0}</span>;\n});\n```\n\n**Correct** — named function expression:\n```tsx\nconst CartBadge = observer(function CartBadge() {\n return <span>{cartStore.cart?.orderLineItems.length ?? 0}</span>;\n});\n```\n\n### 3. Mutation Semantics\n\nMany storefront functions (122+) return `void` and **mutate their arguments in place**. Do NOT try to capture a return value:\n\n```tsx\n// WRONG — selectVariantValue returns void, not a new product\nconst updated = selectVariantValue(product, value);\n\n// CORRECT — mutates product in place, observer re-renders automatically\nselectVariantValue(product, dvv.variantValue);\n```\n\nOther mutation functions: `initLoginForm()`, `setLoginFormEmail()`, `clearFilter()`, `selectFilterValue()`.\n\n### 4. CSS Scoping Limits\n\nOnly **class selectors** in `styles.css` are reliably scoped. Element selectors are NOT scoped:\n\n```css\n/* SAFE — scoped to your component */\n.product-card { padding: 16px; }\n.product-card .title { font-size: 18px; }\n\n/* UNSAFE — NOT reliably scoped, may affect other components */\ndiv { margin: 0; }\nh1 { font-size: 24px; }\n```\n\nAlways use class selectors for all styles.\n\n### 5. Prop Null Handling\n\nProps from the editor can be `undefined` when the store owner hasn't set them. Always use optional chaining:\n\n```tsx\n// WRONG — will crash if product is undefined\n<h1>{props.product.name}</h1>\n\n// CORRECT\n<h1>{props.product?.name}</h1>\n{props.heroImage && <img src={getDefaultSrc(props.heroImage)} />}\n```\n\n### 6. IkasProductImage vs IkasImage\n\n`variant.images` is `IkasProductImage[]`, NOT `IkasImage[]`. You must access the `.image` property to get the `IkasImage` needed by CDN helpers:\n\n```tsx\n// WRONG — productImage is IkasProductImage, not IkasImage\ngetDefaultSrc(productImage);\n\n// CORRECT — access .image to get IkasImage\ngetDefaultSrc(productImage.image);\n\n// Full pattern:\nconst images: IkasImage[] = variant.images\n .map((pi) => pi.image)\n .filter((img): img is IkasImage => img != null);\n```\n\n### 7. Type Assertion Pattern\n\nSome storefront functions have type inference gaps. Use `as unknown as` casts when needed — this is a known pattern:\n\n```tsx\nconst inStock = hasProductStock(product) as unknown as boolean;\nconst finalPrice = getProductVariantFormattedFinalPrice(variant) as unknown as string;\nconst canAddToCart = isAddToCartEnabled(product) as unknown as boolean;\n```\n\nThis applies to functions like `hasProductStock`, `hasProductVariantStock`, `isAddToCartEnabled`, `hasProductVariantDiscount`, `getProductVariantDiscountPercentage`, `getProductVariantFormattedFinalPrice`, `getProductVariantFormattedSellPrice`, `getProductVariantFormattedDiscountAmount`, and `getProductVariantFormattedCampaignPrice`.\n\nNote: `getProductVariantMainImage` returns `IkasProductImage | undefined` (NOT `IkasImage`). Access `.image` to get the `IkasImage` for CDN helpers like `getDefaultSrc()`.\n\n### 8. Store Data Null Safety\n\nStore data (`customerStore.customer`, `cartStore.cart`, `baseStore`) is `null` before initialization completes. Always guard access:\n\n```tsx\n// WRONG — crashes if customer is null\n<h1>{customerStore.customer.firstName}</h1>\n\n// CORRECT — null check first\nif (!customerStore.customer) return <div>Loading...</div>;\n<h1>{customerStore.customer.firstName}</h1>\n\n// Also correct — optional chaining\n<h1>{customerStore.customer?.firstName ?? \"Guest\"}</h1>\n```\n\nSame for `cartStore.cart` — always use `cartStore.cart?.orderLineItems ?? []`.\n\n### 9. ProductList/BlogList Data Access\n\n`productList.data` is the correct way to access products in a product list. Similarly, `blogList.data` for blogs. The display names `.products` / `.blogs` are only used by the blueprint editor — they do NOT exist at runtime:\n\n```tsx\n// CORRECT — use .data for both product lists and blog lists\nconst products = productList?.data ?? [];\nconst blogs = blogList?.data ?? [];\n\n// WRONG — .products / .blogs are display names, not actual fields\nconst products = productList?.products ?? [];\nconst blogs = blogList?.blogs ?? [];\n```\n\n### 10. Form Field Access Pattern\n\nForm fields are objects with `.value`, `.label`, `.hasError`, `.message`. Never access the field directly as a primitive:\n\n```tsx\n// WRONG — loginForm.email is a field object, not a string\n<input value={loginForm.email} />\n\n// CORRECT — access .value for the actual value\n<input value={loginForm.email.value} />\n{loginForm.email.hasError && <span>{loginForm.email.message}</span>}\n```\n\n### 11. Optional Chaining for Editor Props\n\nAll props from `ikas.config.json` can be `undefined` in the editor before the store owner sets them. Always use optional chaining and defaults:\n\n```tsx\n// WRONG — will crash in the editor\n<h1>{props.title}</h1>\n<img src={getDefaultSrc(props.image)} />\n{props.links.links.map(...)}\n\n// CORRECT — safe access with defaults\n<h1>{props.title ?? \"Default Title\"}</h1>\n{props.image && <img src={getDefaultSrc(props.image)} />}\n{(props.links?.links ?? []).map(...)}\n```\n\n### 12. Event Handler Typing\n\nPreact uses different event types than React. Use `(e: Event)` not `(e: React.ChangeEvent)`. Access values with casting. Preact uses `onInput` not `onChange` for text inputs:\n\n```tsx\n// WRONG — React patterns don't work in Preact\nonChange={(e: React.ChangeEvent<HTMLInputElement>) => setValue(e.target.value)}\n\n// CORRECT — Preact event handling\nonInput={(e: Event) => setValue((e.target as HTMLInputElement).value)}\n```\n\nFor select elements:\n```tsx\nonChange={(e: Event) => setOption((e.target as HTMLSelectElement).value)}\n```\n\n### 13. Function Parameter Order\n\nMany storefront functions take specific parameter orders. Always verify with `get_function_doc()` before using:\n\n```tsx\n// WRONG — submitLoginForm only takes the form, not the store\nsubmitLoginForm(customerStore, loginForm);\n\n// CORRECT\nsubmitLoginForm(loginForm);\n\n// WRONG — wrong parameter order for addItemToCart\naddItemToCart(product, variant, 1);\n\n// CORRECT — variant first, then product, then quantity\naddItemToCart(variant, product, 1);\n\n// WRONG — selectVariantValue takes product and variantValue\nselectVariantValue(variant, value);\n\n// CORRECT\nselectVariantValue(product, dvv.variantValue);\n```\n\nWhen in doubt, use the `get_function_doc(functionName)` MCP tool to check the exact signature.\n\n### 14. Missing backgroundColor Prop on Sections\n\nEvery section MUST include a `backgroundColor` COLOR prop so store owners can customize the section background. Without it, the section has a hardcoded background that cannot be changed in the editor.\n\n**Wrong** — section with no backgroundColor prop:\n```json\n// ikas.config.json\n{ \"id\": \"my-section\", \"type\": \"section\", \"props\": [\n { \"name\": \"title\", \"type\": \"TEXT\" }\n]}\n```\n```tsx\nexport function MySection({ title }: Props) {\n return <section className=\"my-section\"><h1>{title}</h1></section>;\n}\n```\n\n**Correct** — section with backgroundColor prop:\n```json\n// ikas.config.json\n{ \"id\": \"my-section\", \"type\": \"section\", \"props\": [\n { \"name\": \"title\", \"type\": \"TEXT\" },\n { \"name\": \"backgroundColor\", \"displayName\": \"Background Color\", \"type\": \"COLOR\", \"defaultValue\": \"#ffffff\" }\n]}\n```\n```tsx\nexport function MySection({ title, backgroundColor = \"#ffffff\" }: Props) {\n return (\n <section className=\"my-section\" style={backgroundColor ? { backgroundColor } : undefined}>\n <h1>{title}</h1>\n </section>\n );\n}\n```\n\nOptionally, also consider adding `textColor` COLOR props for text elements sitting directly on the section background, so they remain readable when the store owner changes the background color.\n\n### 15. Forgetting to Group Props for Complex Sections\n\nSections with 5+ props should use prop groups to organize the editor sidebar. Without groups, store owners see a long flat list of unrelated props.\n\n**Better** — organize related props into groups:\n```json\n{\n \"propGroups\": [\n { \"id\": \"content\", \"name\": \"Content\" },\n { \"id\": \"appearance\", \"name\": \"Appearance\" }\n ],\n \"props\": [\n { \"name\": \"heading\", \"groupId\": \"content\", ... },\n { \"name\": \"bgColor\", \"groupId\": \"appearance\", ... }\n ]\n}\n```\n\nSee `get_framework_guide(\"prop-groups\")` for full details.\n\n### 16. Hardcoded Static Text in Components\n\nAll user-visible text MUST be TEXT props — never hardcode strings in JSX. Hardcoded text cannot be translated for multilingual storefronts.\n\n**Wrong** — hardcoded strings:\n```tsx\nexport function LoginSection({ backgroundColor }: Props) {\n return (\n <section style={backgroundColor ? { backgroundColor } : undefined}>\n <h1>Sign In</h1>\n <button>{form.isSubmitting ? \"Signing in...\" : \"Sign In\"}</button>\n <p>Don't have an account? <a href=\"/register\">Create one</a></p>\n </section>\n );\n}\n```\n\n**Correct** — all text as TEXT props with defaultValues:\n```json\n// ikas.config.json\n{ \"props\": [\n { \"name\": \"title\", \"type\": \"TEXT\", \"defaultValue\": \"Sign In\", \"groupId\": \"texts\" },\n { \"name\": \"submitButtonText\", \"type\": \"TEXT\", \"defaultValue\": \"Sign In\", \"groupId\": \"texts\" },\n { \"name\": \"submittingButtonText\", \"type\": \"TEXT\", \"defaultValue\": \"Signing in...\", \"groupId\": \"texts\" },\n { \"name\": \"noAccountText\", \"type\": \"TEXT\", \"defaultValue\": \"Don't have an account?\", \"groupId\": \"texts\" },\n { \"name\": \"createAccountLinkText\", \"type\": \"TEXT\", \"defaultValue\": \"Create one\", \"groupId\": \"texts\" }\n],\n\"propGroups\": [{ \"id\": \"texts\", \"name\": \"Texts\" }]}\n```\n```tsx\nexport function LoginSection({ title = \"Sign In\", submitButtonText = \"Sign In\",\n submittingButtonText = \"Signing in...\", noAccountText = \"Don't have an account?\",\n createAccountLinkText = \"Create one\", backgroundColor }: Props) {\n return (\n <section style={backgroundColor ? { backgroundColor } : undefined}>\n <h1>{title}</h1>\n <button>{form.isSubmitting ? submittingButtonText : submitButtonText}</button>\n <p>{noAccountText} <a href=\"/register\">{createAccountLinkText}</a></p>\n </section>\n );\n}\n```\n\nFor button loading states, use two separate TEXT props (e.g., `submitButtonText` + `submittingButtonText`). Group text props under a \"Texts\" propGroup when the component has 5+ props.\n\n### 17. IkasOrderLineItemOption.values is an Array, Not a Scalar\n\n`IkasOrderLineItemOption` (the per-item options on cart/order line items) has a `.values` field that is an **array** of `IkasOrderLineItemOptionValue`, NOT a scalar `.value`. Each value in the array has its own `.name` / `.value` / `.formattedPrice`.\n\n```tsx\n// WRONG — there is no .value on IkasOrderLineItemOption\n<span>{option.value}</span>\n\n// CORRECT — .values is an array; map over it\n{option.values.map((v) => (\n <span key={v.name}>{v.name}: {v.value}</span>\n))}\n```\n\nThis is the most common confusion when rendering order summary lines or cart line item options. See `get_migration_guide(\"prop-runtime-shapes\")` for the broader catalog of order-shape gotchas.\n\n### 18. IkasOrderAdjustment Has No .formattedAmount — Use the Helper\n\n`IkasOrderAdjustment` does not expose a `.formattedAmount` property directly. Call the helper `getOrderAdjustmentFormattedAmount(adjustment)` from `@ikas/bp-storefront` — it returns the formatted currency string with the correct sign (negated for decrements).\n\n```tsx\nimport { getOrderAdjustmentFormattedAmount } from \"@ikas/bp-storefront\";\n\n// WRONG — .formattedAmount does not exist\n<td>{adjustment.formattedAmount}</td>\n\n// CORRECT\n<td>{getOrderAdjustmentFormattedAmount(adjustment)}</td>\n```\n\nSee `get_function_doc(\"getOrderAdjustmentFormattedAmount\")` for the exact signature.\n",
|
|
176
|
+
"content": "## Common Pitfalls\n\nThese are the most common mistakes when building ikas code components. Avoid them for correct, working code.\n\n### 1. Root Component Should NOT Use observer\n\nThe ikas runtime wraps root component renders in `autorun()`, making them automatically reactive. Wrapping a root export with `observer()` is redundant and misleading.\n\n**Wrong** \u2014 observer on root export:\n```tsx\nimport { observer } from \"@ikas/component-utils\";\nimport { cartStore } from \"@ikas/bp-storefront\";\n\nconst CartSection = observer(function CartSection({ title }: Props) {\n const items = cartStore.cart?.orderLineItems ?? [];\n return <section>{title}: {items.length} items</section>;\n});\nexport default CartSection;\n```\n\n**Correct** \u2014 plain named export:\n```tsx\nimport { cartStore } from \"@ikas/bp-storefront\";\n\nexport function CartSection({ title }: Props) {\n const items = cartStore.cart?.orderLineItems ?? [];\n return <section>{title}: {items.length} items</section>;\n}\n\nexport default CartSection;\n```\n\n### 2. Observer Sub-Component Naming\n\nWhen using `observer()` on **sub-components**, use a named function expression \u2014 not an arrow function \u2014 for proper DevTools display names.\n\n**Wrong** \u2014 arrow function loses display name:\n```tsx\nconst CartBadge = observer(() => {\n return <span>{cartStore.cart?.orderLineItems.length ?? 0}</span>;\n});\n```\n\n**Correct** \u2014 named function expression:\n```tsx\nconst CartBadge = observer(function CartBadge() {\n return <span>{cartStore.cart?.orderLineItems.length ?? 0}</span>;\n});\n```\n\n### 3. Mutation Semantics\n\nMany storefront functions (122+) return `void` and **mutate their arguments in place**. Do NOT try to capture a return value:\n\n```tsx\n// WRONG \u2014 selectVariantValue returns void, not a new product\nconst updated = selectVariantValue(product, value);\n\n// CORRECT \u2014 mutates product in place, observer re-renders automatically\nselectVariantValue(product, dvv.variantValue);\n```\n\nOther mutation functions: `initLoginForm()`, `setLoginFormEmail()`, `clearFilter()`, `selectFilterValue()`.\n\n### 4. CSS Scoping Limits\n\nOnly **class selectors** in `styles.css` are reliably scoped. Element selectors are NOT scoped:\n\n```css\n/* SAFE \u2014 scoped to your component */\n.product-card { padding: 16px; }\n.product-card .title { font-size: 18px; }\n\n/* UNSAFE \u2014 NOT reliably scoped, may affect other components */\ndiv { margin: 0; }\nh1 { font-size: 24px; }\n```\n\nAlways use class selectors for all styles.\n\n### 5. Prop Null Handling\n\nProps from the editor can be `undefined` when the store owner hasn't set them. Always use optional chaining:\n\n```tsx\n// WRONG \u2014 will crash if product is undefined\n<h1>{props.product.name}</h1>\n\n// CORRECT\n<h1>{props.product?.name}</h1>\n{props.heroImage && <img src={getDefaultSrc(props.heroImage)} />}\n```\n\n### 6. IkasProductImage vs IkasImage\n\n`variant.images` is `IkasProductImage[]`, NOT `IkasImage[]`. You must access the `.image` property to get the `IkasImage` needed by CDN helpers:\n\n```tsx\n// WRONG \u2014 productImage is IkasProductImage, not IkasImage\ngetDefaultSrc(productImage);\n\n// CORRECT \u2014 access .image to get IkasImage\ngetDefaultSrc(productImage.image);\n\n// Full pattern:\nconst images: IkasImage[] = variant.images\n .map((pi) => pi.image)\n .filter((img): img is IkasImage => img != null);\n```\n\n### 7. Type Assertion Pattern\n\nSome storefront functions have type inference gaps. Use `as unknown as` casts when needed \u2014 this is a known pattern:\n\n```tsx\nconst inStock = hasProductStock(product) as unknown as boolean;\nconst finalPrice = getProductVariantFormattedFinalPrice(variant) as unknown as string;\nconst canAddToCart = isAddToCartEnabled(product) as unknown as boolean;\n```\n\nThis applies to functions like `hasProductStock`, `hasProductVariantStock`, `isAddToCartEnabled`, `hasProductVariantDiscount`, `getProductVariantDiscountPercentage`, `getProductVariantFormattedFinalPrice`, `getProductVariantFormattedSellPrice`, `getProductVariantFormattedDiscountAmount`, and `getProductVariantFormattedCampaignPrice`.\n\nNote: `getProductVariantMainImage` returns `IkasProductImage | undefined` (NOT `IkasImage`). Access `.image` to get the `IkasImage` for CDN helpers like `getDefaultSrc()`.\n\n### 8. Store Data Null Safety\n\nStore data (`customerStore.customer`, `cartStore.cart`, `baseStore`) is `null` before initialization completes. Always guard access:\n\n```tsx\n// WRONG \u2014 crashes if customer is null\n<h1>{customerStore.customer.firstName}</h1>\n\n// CORRECT \u2014 null check first\nif (!customerStore.customer) return <div>Loading...</div>;\n<h1>{customerStore.customer.firstName}</h1>\n\n// Also correct \u2014 optional chaining\n<h1>{customerStore.customer?.firstName ?? \"Guest\"}</h1>\n```\n\nSame for `cartStore.cart` \u2014 always use `cartStore.cart?.orderLineItems ?? []`.\n\n### 9. ProductList/BlogList Data Access\n\n`productList.data` is the correct way to access products in a product list. Similarly, `blogList.data` for blogs. The display names `.products` / `.blogs` are only used by the blueprint editor \u2014 they do NOT exist at runtime:\n\n```tsx\n// CORRECT \u2014 use .data for both product lists and blog lists\nconst products = productList?.data ?? [];\nconst blogs = blogList?.data ?? [];\n\n// WRONG \u2014 .products / .blogs are display names, not actual fields\nconst products = productList?.products ?? [];\nconst blogs = blogList?.blogs ?? [];\n```\n\n### 10. Form Field Access Pattern\n\nForm fields are objects with `.value`, `.label`, `.hasError`, `.message`. Never access the field directly as a primitive:\n\n```tsx\n// WRONG \u2014 loginForm.email is a field object, not a string\n<input value={loginForm.email} />\n\n// CORRECT \u2014 access .value for the actual value\n<input value={loginForm.email.value} />\n{loginForm.email.hasError && <span>{loginForm.email.message}</span>}\n```\n\n### 11. Optional Chaining for Editor Props\n\nAll props from `ikas.config.json` can be `undefined` in the editor before the store owner sets them. Always use optional chaining and defaults:\n\n```tsx\n// WRONG \u2014 will crash in the editor\n<h1>{props.title}</h1>\n<img src={getDefaultSrc(props.image)} />\n{props.links.links.map(...)}\n\n// CORRECT \u2014 safe access with defaults\n<h1>{props.title ?? \"Default Title\"}</h1>\n{props.image && <img src={getDefaultSrc(props.image)} />}\n{(props.links?.links ?? []).map(...)}\n```\n\n### 12. Event Handler Typing\n\nPreact uses different event types than React. Use `(e: Event)` not `(e: React.ChangeEvent)`. Access values with casting. Preact uses `onInput` not `onChange` for text inputs:\n\n```tsx\n// WRONG \u2014 React patterns don't work in Preact\nonChange={(e: React.ChangeEvent<HTMLInputElement>) => setValue(e.target.value)}\n\n// CORRECT \u2014 Preact event handling\nonInput={(e: Event) => setValue((e.target as HTMLInputElement).value)}\n```\n\nFor select elements:\n```tsx\nonChange={(e: Event) => setOption((e.target as HTMLSelectElement).value)}\n```\n\n### 13. Function Parameter Order\n\nMany storefront functions take specific parameter orders. Always verify with `get_function_doc()` before using:\n\n```tsx\n// WRONG \u2014 submitLoginForm only takes the form, not the store\nsubmitLoginForm(customerStore, loginForm);\n\n// CORRECT\nsubmitLoginForm(loginForm);\n\n// WRONG \u2014 wrong parameter order for addItemToCart\naddItemToCart(product, variant, 1);\n\n// CORRECT \u2014 variant first, then product, then quantity\naddItemToCart(variant, product, 1);\n\n// WRONG \u2014 selectVariantValue takes product and variantValue\nselectVariantValue(variant, value);\n\n// CORRECT\nselectVariantValue(product, dvv.variantValue);\n```\n\nWhen in doubt, use the `get_function_doc(functionName)` MCP tool to check the exact signature.\n\n### 14. Missing backgroundColor Prop on Sections\n\nEvery section MUST include a `backgroundColor` COLOR prop so store owners can customize the section background. Without it, the section has a hardcoded background that cannot be changed in the editor.\n\n**Wrong** \u2014 section with no backgroundColor prop:\n```json\n// ikas.config.json\n{ \"id\": \"my-section\", \"type\": \"section\", \"props\": [\n { \"name\": \"title\", \"type\": \"TEXT\" }\n]}\n```\n```tsx\nexport function MySection({ title }: Props) {\n return <section className=\"my-section\"><h1>{title}</h1></section>;\n}\n```\n\n**Correct** \u2014 section with backgroundColor prop:\n```json\n// ikas.config.json\n{ \"id\": \"my-section\", \"type\": \"section\", \"props\": [\n { \"name\": \"title\", \"type\": \"TEXT\" },\n { \"name\": \"backgroundColor\", \"displayName\": \"Background Color\", \"type\": \"COLOR\", \"defaultValue\": \"#ffffff\" }\n]}\n```\n```tsx\nexport function MySection({ title, backgroundColor = \"#ffffff\" }: Props) {\n return (\n <section className=\"my-section\" style={backgroundColor ? { backgroundColor } : undefined}>\n <h1>{title}</h1>\n </section>\n );\n}\n```\n\nOptionally, also consider adding `textColor` COLOR props for text elements sitting directly on the section background, so they remain readable when the store owner changes the background color.\n\n### 15. Forgetting to Group Props for Complex Sections\n\nSections with 5+ props should use prop groups to organize the editor sidebar. Without groups, store owners see a long flat list of unrelated props.\n\n**Better** \u2014 organize related props into groups:\n```json\n{\n \"propGroups\": [\n { \"id\": \"content\", \"name\": \"Content\" },\n { \"id\": \"appearance\", \"name\": \"Appearance\" }\n ],\n \"props\": [\n { \"name\": \"heading\", \"groupId\": \"content\", ... },\n { \"name\": \"bgColor\", \"groupId\": \"appearance\", ... }\n ]\n}\n```\n\nSee `get_framework_guide(\"prop-groups\")` for full details.\n\n### 16. Hardcoded Static Text in Components\n\nAll user-visible text MUST be TEXT props \u2014 never hardcode strings in JSX. Hardcoded text cannot be translated for multilingual storefronts.\n\n**Wrong** \u2014 hardcoded strings:\n```tsx\nexport function LoginSection({ backgroundColor }: Props) {\n return (\n <section style={backgroundColor ? { backgroundColor } : undefined}>\n <h1>Sign In</h1>\n <button>{form.isSubmitting ? \"Signing in...\" : \"Sign In\"}</button>\n <p>Don't have an account? <a href=\"/register\">Create one</a></p>\n </section>\n );\n}\n```\n\n**Correct** \u2014 all text as TEXT props with defaultValues:\n```json\n// ikas.config.json\n{ \"props\": [\n { \"name\": \"title\", \"type\": \"TEXT\", \"defaultValue\": \"Sign In\", \"groupId\": \"texts\" },\n { \"name\": \"submitButtonText\", \"type\": \"TEXT\", \"defaultValue\": \"Sign In\", \"groupId\": \"texts\" },\n { \"name\": \"submittingButtonText\", \"type\": \"TEXT\", \"defaultValue\": \"Signing in...\", \"groupId\": \"texts\" },\n { \"name\": \"noAccountText\", \"type\": \"TEXT\", \"defaultValue\": \"Don't have an account?\", \"groupId\": \"texts\" },\n { \"name\": \"createAccountLinkText\", \"type\": \"TEXT\", \"defaultValue\": \"Create one\", \"groupId\": \"texts\" }\n],\n\"propGroups\": [{ \"id\": \"texts\", \"name\": \"Texts\" }]}\n```\n```tsx\nexport function LoginSection({ title = \"Sign In\", submitButtonText = \"Sign In\",\n submittingButtonText = \"Signing in...\", noAccountText = \"Don't have an account?\",\n createAccountLinkText = \"Create one\", backgroundColor }: Props) {\n return (\n <section style={backgroundColor ? { backgroundColor } : undefined}>\n <h1>{title}</h1>\n <button>{form.isSubmitting ? submittingButtonText : submitButtonText}</button>\n <p>{noAccountText} <a href=\"/register\">{createAccountLinkText}</a></p>\n </section>\n );\n}\n```\n\nFor button loading states, use two separate TEXT props (e.g., `submitButtonText` + `submittingButtonText`). Group text props under a \"Texts\" propGroup when the component has 5+ props.\n\n### 17. IkasOrderLineItemOption.values is an Array, Not a Scalar\n\n`IkasOrderLineItemOption` (the per-item options on cart/order line items) has a `.values` field that is an **array** of `IkasOrderLineItemOptionValue`, NOT a scalar `.value`. Each value in the array has its own `.name` / `.value` / `.formattedPrice`.\n\n```tsx\n// WRONG \u2014 there is no .value on IkasOrderLineItemOption\n<span>{option.value}</span>\n\n// CORRECT \u2014 .values is an array; map over it\n{option.values.map((v) => (\n <span key={v.name}>{v.name}: {v.value}</span>\n))}\n```\n\nThis is the most common confusion when rendering order summary lines or cart line item options. See `get_migration_guide(\"prop-runtime-shapes\")` for the broader catalog of order-shape gotchas.\n\n### 18. IkasOrderAdjustment Has No .formattedAmount \u2014 Use the Helper\n\n`IkasOrderAdjustment` does not expose a `.formattedAmount` property directly. Call the helper `getOrderAdjustmentFormattedAmount(adjustment)` from `@ikas/bp-storefront` \u2014 it returns the formatted currency string with the correct sign (negated for decrements).\n\n```tsx\nimport { getOrderAdjustmentFormattedAmount } from \"@ikas/bp-storefront\";\n\n// WRONG \u2014 .formattedAmount does not exist\n<td>{adjustment.formattedAmount}</td>\n\n// CORRECT\n<td>{getOrderAdjustmentFormattedAmount(adjustment)}</td>\n```\n\nSee `get_function_doc(\"getOrderAdjustmentFormattedAmount\")` for the exact signature.\n\n### 19. Old-System Property Patterns That Don't Exist in the New System\n\nSeveral `@ikas/storefront` properties look like they should still work but don't exist on the corresponding `@ikas/bp-storefront` types. Use the helper function instead. **If TypeScript reports TS2339 (\"Property X does not exist on type Y\") on a `.selectedVariant`, `.mainImage`, `.href`, or `.checkoutUrl` access, you're hitting one of these.**\n\n| Old pattern | New replacement |\n|---|---|\n| `product.selectedVariant` | `getSelectedProductVariant(product)` \u2192 `IkasProductVariant \\| undefined` |\n| `product.href` | `getProductHref(product)` (one of several `get*Href` helpers \u2014 see `navigation-patterns`) |\n| `variant.mainImage` | `getProductVariantMainImage(variant)` \u2192 `IkasProductImage`, then access `.image` for the `IkasImage` |\n| `cartStore.checkoutUrl` | `getCheckoutUrlFromCartStore(cartStore)` (handles auth-state branching internally) |\n\nThe variant-to-image chain in particular is 4 hops, which trips up most migrations:\n\n```tsx\nconst variant = getSelectedProductVariant(product);\nconst productImage = variant && getProductVariantMainImage(variant);\nconst image = productImage?.image;\nif (image) {\n const src = getDefaultSrc(image);\n}\n```\n\nEach hop has a real reason: variant selection (which size/color) \u2192 that variant's image set \u2192 the image record (carrying metadata like `order`, `isMain`) \u2192 the actual `IkasImage` the CDN helpers accept. Memorize the chain \u2014 it appears everywhere product imagery is rendered.",
|
|
177
177
|
"tags": [
|
|
178
178
|
"pitfalls",
|
|
179
179
|
"gotchas",
|
|
@@ -194,7 +194,7 @@
|
|
|
194
194
|
"sub-component-patterns": {
|
|
195
195
|
"title": "Sub-Component File Organization",
|
|
196
196
|
"description": "How to organize sub-components in their own folders under src/sub-components/ with proper imports and CSS",
|
|
197
|
-
"content": "## Sub-Component File Organization\n\n**ALWAYS create sub-components in `src/sub-components/` with their own folder containing `index.tsx` and `styles.css`. NEVER create flat .tsx files inside a component folder.**\n\n### Directory Structure\n\n```\nsrc/\n
|
|
197
|
+
"content": "## Sub-Component File Organization\n\n**ALWAYS create sub-components in `src/sub-components/` with their own folder containing `index.tsx` and `styles.css`. NEVER create flat .tsx files inside a component folder.**\n\n### Directory Structure\n\n```\nsrc/\n\u251c\u2500\u2500 components/\n\u2502 \u251c\u2500\u2500 ProductList/\n\u2502 \u2502 \u251c\u2500\u2500 index.tsx # imports from ../../sub-components/...\n\u2502 \u2502 \u251c\u2500\u2500 types.ts # auto-generated (CLI-managed)\n\u2502 \u2502 \u2514\u2500\u2500 styles.css # @import \"../../sub-components/.../styles.css\"\n\u2502 \u2514\u2500\u2500 index.ts\n\u2514\u2500\u2500 sub-components/\n \u251c\u2500\u2500 ProductCard/\n \u2502 \u251c\u2500\u2500 index.tsx # sub-component\n \u2502 \u2514\u2500\u2500 styles.css # sub-component styles\n \u2514\u2500\u2500 FilterSidebar/\n \u251c\u2500\u2500 index.tsx # sub-component\n \u2514\u2500\u2500 styles.css # sub-component styles\n```\n\n### Key Rules\n\n1. **`src/components/`** = registered components (listed in `ikas.config.json`)\n2. **`src/sub-components/`** = internal helpers (NOT in `ikas.config.json`)\n3. Sub-components do **NOT** have `types.ts` \u2014 define `Props` interface inline in `index.tsx`\n4. Sub-components that read MobX stores need `observer()` from `@ikas/component-utils`\n5. Sub-components that only receive props do NOT need `observer()`\n6. CSS is scoped with the parent component's `.cc_` prefix (same scope \u2014 no conflicts)\n7. Sub-components can be shared across multiple parent sections\n\n### CSS Import Pattern\n\nIn the parent component's `styles.css`, import sub-component styles with `@import`:\n\n```css\n/* src/components/ProductList/styles.css */\n@import \"../../sub-components/ProductCard/styles.css\";\n@import \"../../sub-components/FilterSidebar/styles.css\";\n\n.product-list-section {\n width: 100%;\n padding: 40px 24px;\n}\n```\n\nThe build system's CSS import resolver recursively resolves `@import` statements with relative paths. All imported CSS gets scoped with the parent component's `.cc_{componentId}` prefix.\n\n### TSX Import Pattern\n\n```tsx\n// src/components/ProductList/index.tsx\nimport ProductCard from \"../../sub-components/ProductCard\";\nimport FilterSidebar from \"../../sub-components/FilterSidebar\";\nimport { Props } from \"./types\";\n\nexport function ProductListSection({ productList, title }: Props) {\n const products = productList?.data ?? [];\n return (\n <section className=\"product-list-section\">\n <FilterSidebar productList={productList} />\n <div className=\"product-grid\">\n {products.map((product) => (\n <ProductCard key={product.id} product={product} />\n ))}\n </div>\n </section>\n );\n}\n\nexport default ProductListSection;\n```\n\n### Sub-Component Example\n\n```tsx\n// src/sub-components/ProductCard/index.tsx\nimport {\n IkasProduct,\n getSelectedProductVariant,\n getProductVariantFormattedFinalPrice,\n getProductVariantMainImage,\n getSelectedProductVariantHref,\n getDefaultSrc,\n} from \"@ikas/bp-storefront\";\n\ninterface Props {\n product: IkasProduct;\n}\n\nexport default function ProductCard({ product }: Props) {\n const variant = getSelectedProductVariant(product);\n const productImage = getProductVariantMainImage(variant);\n const image = productImage?.image ?? null;\n const price = getProductVariantFormattedFinalPrice(variant) as unknown as string;\n\n return (\n <a href={getSelectedProductVariantHref(product)} className=\"product-card\">\n {image && <img src={getDefaultSrc(image)} alt={product.name} className=\"product-card-image\" />}\n <h3 className=\"product-card-name\">{product.name}</h3>\n <span className=\"product-card-price\">{price}</span>\n </a>\n );\n}\n```\n\nNotice:\n- `Props` is defined inline \u2014 no separate `types.ts` file\n- No `observer()` needed because this component only uses props passed from the parent\n- Default export like registered components\n\n### When to Extract Sub-Components\n\n- A section's `index.tsx` exceeds ~150 lines\n- A distinct UI block (card, sidebar, modal) has its own styles and logic\n- The same UI block is used by multiple parent sections\n- A part of the component independently reads MobX stores (extract + wrap with `observer()`)\n\n### When observer() Is Needed on Sub-Components\n\n```tsx\n// src/sub-components/CartBadge/index.tsx\nimport { observer } from \"@ikas/component-utils\";\nimport { cartStore } from \"@ikas/bp-storefront\";\n\n// This sub-component independently reads a MobX store \u2192 needs observer()\nconst CartBadge = observer(function CartBadge() {\n const count = cartStore.cart?.orderLineItems.length ?? 0;\n return <span className=\"cart-badge\">{count}</span>;\n});\n\nexport default CartBadge;\n```",
|
|
198
198
|
"tags": [
|
|
199
199
|
"sub-component",
|
|
200
200
|
"folder",
|
|
@@ -209,7 +209,7 @@
|
|
|
209
209
|
"ai-workflow": {
|
|
210
210
|
"title": "AI Agent Workflow for Building ikas Components",
|
|
211
211
|
"description": "Step-by-step guide for AI coding agents building ikas storefront components using CLI commands and MCP tools",
|
|
212
|
-
"content": "## AI Agent Workflow\n\nThis is the recommended step-by-step workflow for AI agents building ikas code components. Follow these steps in order for reliable, error-free results.\n\n**IMPORTANT: NEVER create or edit `types.ts` manually
|
|
212
|
+
"content": "## AI Agent Workflow\n\nThis is the recommended step-by-step workflow for AI agents building ikas code components. Follow these steps in order for reliable, error-free results.\n\n**IMPORTANT: NEVER create or edit `types.ts` manually \u2014 it is auto-generated by the CLI.** The CLI commands below update BOTH `ikas.config.json` AND `types.ts` automatically.\n\n### Step 1: Create Component with Props\n\nUse `get_section_template(sectionType)` to get a starter template \u2014 it includes a ready-to-run CLI command with `--props`.\n\n**Important:** When creating a section, always include a `backgroundColor` COLOR prop (default: `#ffffff`). Optionally consider adding `textColor` for text elements directly on the section background.\n\n**Important:** When creating a header or footer section, use `--isHeader` or `--isFooter` flags on the `add-component` command. This marks the section as the store's common header/footer so it appears on all pages automatically:\n```bash\nnpx ikas-component config add-component --name \"Header\" --type section --isHeader --props '[...]'\nnpx ikas-component config add-component --name \"Footer\" --type section --isFooter --props '[...]'\n```\n\n**Important:** Ensure ALL user-visible text (headings, buttons, labels, empty states, loading text) is exposed as TEXT props. Never hardcode text strings in JSX. Use `defaultValue` for English defaults. For button loading states, use two separate props (e.g., `submitButtonText` + `submittingButtonText`). Group text props under a \"Texts\" propGroup when 5+ props exist.\n\nThen run the single `add-component --props` command to create the component scaffold WITH all props in one step:\n```bash\nnpx ikas-component config add-component --name \"HeroSection\" --type section --props '[{\"name\":\"title\",\"displayName\":\"Title\",\"type\":\"TEXT\",\"required\":true},{\"name\":\"backgroundImage\",\"displayName\":\"Background Image\",\"type\":\"IMAGE\"},{\"name\":\"showButton\",\"displayName\":\"Show Button\",\"type\":\"BOOLEAN\"}]'\n```\n\nThis creates the component directory with `index.tsx`, `types.ts` (with correct Props interface), `styles.css`, updates `ikas.config.json`, and updates the barrel export. The output is JSON:\n```json\n{\"success\": true, \"componentId\": \"abc123-hero-section\", \"componentName\": \"HeroSection\", \"type\": \"section\", \"propsCount\": 3, \"files\": [...]}\n```\n\nThe `--props` flag accepts a JSON array of prop objects. Each prop needs `name` + `type` at minimum. `displayName` is auto-generated from camelCase name if omitted (e.g. `backgroundImage` \u2192 `\"Background Image\"`).\n\nTo add more props later, use `add-prop`:\n```bash\nnpx ikas-component config add-prop --component \"HeroSection\" --name \"subtitle\" --displayName \"Subtitle\" --type TEXT\n```\n\n### Step 2: Get Reference Material\n\nBefore writing component code, use MCP tools to get the right patterns:\n- `get_section_template(sectionType)` \u2014 Get the API integration pattern reference for common section types (product-detail, cart, login, header, footer, etc.). Study the function calls and data access patterns only.\n- `get_framework_guide(\"common-pitfalls\")` \u2014 Review common mistakes to avoid\n- `get_framework_guide(\"component-structure\")` \u2014 Review component structure patterns\n- `get_function_doc(functionName)` \u2014 Look up exact function signatures before using them\n- `get_model_guide(typeName)` \u2014 Get comprehensive info about a model type\n\n### Step 2.5: Design Your Component (IMPORTANT)\n\nBefore writing code, plan an ORIGINAL visual design:\n- Do NOT reproduce layout, CSS class names, or HTML structure from templates/examples\n- Templates and examples demonstrate correct API usage only \u2014 they are not designs to copy\n- **KEEP from references:** imports, function calls, data access patterns, form handling flows, type casts, null safety patterns\n- **CREATE fresh:** all JSX/HTML structure, all CSS class names and rules, all visual layout and spacing, all UX patterns (loading states, empty states, transitions, responsive breakpoints)\n- Think about the specific store's brand and design language when creating your component\n\n### Step 3: Write the Component Code\n\nEdit `src/components/{ComponentName}/index.tsx` with the implementation. **Do NOT edit `types.ts`** \u2014 it was already generated correctly in Step 1. Key rules:\n- Import props from `./types` (auto-generated in Step 1)\n- Import storefront functions from `@ikas/bp-storefront`\n- Root export should use named + default export: `export function X({ ... }: Props) { ... }` + `export default X;`\n- Only wrap sub-components with `observer()` when they independently read MobX stores\n- Use optional chaining for all props: `props.title ?? \"Default\"`\n- Use `as unknown as boolean` cast for functions like `hasProductStock`, `isAddToCartEnabled`\n- **Sections MUST apply backgroundColor**: destructure `backgroundColor = \"#ffffff\"` and add `style={backgroundColor ? { backgroundColor } : undefined}` on the root `<section>` element\n\n### Step 3.5: Organize Props into Groups (if 5+ props)\n\nIf the component has 5 or more props, organize them into groups for better editor UX:\n```bash\nnpx ikas-component config add-prop-group --component \"HeroSection\" --id content --name \"Content\"\nnpx ikas-component config add-prop-group --component \"HeroSection\" --id appearance --name \"Appearance\"\nnpx ikas-component config update-prop --component \"HeroSection\" --prop title --group content\nnpx ikas-component config update-prop --component \"HeroSection\" --prop backgroundImage --group appearance\n```\n\n### Step 4: Write Styles\n\nEdit `src/components/{ComponentName}/styles.css`. Key rules:\n- Use class selectors only (`.my-class`), never bare element selectors\n- CSS is auto-scoped at build time \u2014 no manual namespacing needed\n- For sections: `width: 100%; padding: 64px 24px;` with inner max-width container\n\n### Step 5: Verify with Type Checking\n\nRun the lightweight type checker:\n```bash\nnpx ikas-component check --json\n```\n\nSuccess output:\n```json\n{\"success\": true, \"errors\": []}\n```\n\nError output:\n```json\n{\"success\": false, \"errorCount\": 2, \"errors\": [{\"file\": \"src/components/Hero/index.tsx\", \"line\": 15, \"column\": 3, \"code\": \"TS2339\", \"message\": \"Property 'title' does not exist on type 'Props'\"}]}\n```\n\n### Step 6: Fix Errors and Re-check\n\nFor each error:\n1. Read the file and line number from the error\n2. Fix the issue (common fixes below)\n3. Re-run `npx ikas-component check --json`\n\n**Common error fixes:**\n- `Property 'x' does not exist on type 'Props'` \u2014 Prop wasn't added via CLI. Run `npx ikas-component config add-prop`.\n- `Cannot find module '@ikas/bp-storefront'` \u2014 Normal in type-check if node_modules not fully installed. Focus on component-level errors.\n- `Type 'X' is not assignable to type 'Y'` \u2014 Check function signature with `get_function_doc()`.\n\n### Step 7: Full Build Validation\n\nOnce type checking passes, run the full build:\n```bash\nnpx ikas-component build\n```\n\nThis runs type checking + esbuild compilation + CSS scoping.\n\n### Quick Reference: CLI Commands\n\n| Command | Purpose |\n|---------|--------|\n| `npx ikas-component config add-component --name X --type section --props '[...]'` | **Primary** \u2014 create component with all props |\n| `npx ikas-component config add-component --name X --type section --isHeader --props '[...]'` | Create header section |\n| `npx ikas-component config add-component --name X --type section --isFooter --props '[...]'` | Create footer section |\n| `npx ikas-component config add-component --name X --type section` | Create component with no props |\n| `npx ikas-component config add-prop --component X --name y --displayName Y --type TEXT` | Add a prop incrementally |\n| `npx ikas-component config update-prop --component X --prop y --required true` | Update a prop |\n| `npx ikas-component config remove-prop --component X --prop y` | Remove a prop |\n| `npx ikas-component config remove-component --name X` | Remove a component |\n| `npx ikas-component config list` | List all components and props |\n| `npx ikas-component check --json` | Type-check with JSON output |\n| `npx ikas-component build` | Full production build |\n\n### Quick Reference: MCP Tools\n\n| Tool | When to Use |\n|------|------------|\n| `get_section_template(type)` | Starting a new section \u2014 get API integration pattern reference + CLI command (create original design, don't copy layout) |\n| `get_framework_guide(topic)` | Understanding patterns, pitfalls, architecture |\n| `get_function_doc(name)` | Looking up exact function signature |\n| `get_model_guide(type)` | Working with a model type (IkasProduct, IkasCart, etc.) |\n| `get_prop_types()` | Checking available prop types for ikas.config.json |\n| `search_docs(query)` | Finding relevant functions/docs by keyword |",
|
|
213
213
|
"tags": [
|
|
214
214
|
"ai",
|
|
215
215
|
"workflow",
|
|
@@ -223,7 +223,7 @@
|
|
|
223
223
|
"form-handling": {
|
|
224
224
|
"title": "Form Handling",
|
|
225
225
|
"description": "How to use the form model pattern for login, registration, address, and other forms",
|
|
226
|
-
"content": "## Form Handling Pattern\n\nikas storefront uses a consistent form model pattern across all form types: `init*Form()`
|
|
226
|
+
"content": "## Form Handling Pattern\n\nikas storefront uses a consistent form model pattern across all form types: `init*Form()` \u2192 `set*FormField()` \u2192 `submit*Form()`.\n\n### Form Model Structure\n\nEach form field has:\n```typescript\n{\n value: string; // Current field value\n hasError: boolean; // Whether validation failed\n message?: string; // Error message to display\n label: string; // Display label\n placeholder: string; // Input placeholder\n}\n```\n\nEach form tracks:\n```typescript\n{\n isSubmitted: boolean; // Has submit been attempted\n isSubmitting: boolean; // Is submission in progress\n isSuccess?: boolean; // Did last submit succeed\n isFailure?: boolean; // Did last submit fail\n responseMessage?: string; // Server response message\n}\n```\n\n### Login Form Example\n\n```tsx\nimport { useEffect } from \"preact/hooks\";\nimport {\n customerStore,\n initLoginForm,\n setLoginFormEmail,\n setLoginFormPassword,\n submitLoginForm,\n Router,\n} from \"@ikas/bp-storefront\";\n\n// Root export \u2014 no observer() needed, ikas runtime handles reactivity via autorun()\nexport function LoginForm() {\n const loginForm = customerStore.loginForm;\n\n useEffect(() => {\n initLoginForm(loginForm);\n }, []);\n\n const handleSubmit = async (e: Event) => {\n e.preventDefault();\n const success = await submitLoginForm(loginForm);\n if (success) {\n Router.navigate(\"/account\");\n }\n };\n\n return (\n <form onSubmit={handleSubmit}>\n {loginForm.isFailure && <div>{loginForm.responseMessage}</div>}\n\n <input\n type=\"email\"\n value={loginForm.email.value}\n onInput={(e) => setLoginFormEmail(loginForm, e.target.value)}\n />\n {loginForm.email.hasError && <span>{loginForm.email.message}</span>}\n\n <input\n type=\"password\"\n value={loginForm.password.value}\n onInput={(e) => setLoginFormPassword(loginForm, e.target.value)}\n />\n {loginForm.password.hasError && <span>{loginForm.password.message}</span>}\n\n <button type=\"submit\" disabled={loginForm.isSubmitting}>\n {loginForm.isSubmitting ? \"Signing in...\" : \"Sign In\"}\n </button>\n </form>\n );\n}\n\nexport default LoginForm;\n```\n\n### Available Form Types\n\n| Form | Init | Setter Functions | Submit |\n|------|------|-----------------|--------|\n| Login | `initLoginForm(form)` | `setLoginFormEmail()`, `setLoginFormPassword()` | `submitLoginForm(form)` |\n| Register | `initRegisterForm(form)` | `setRegisterFormEmail()`, `setRegisterFormPassword()`, `setRegisterFormFirstName()`, `setRegisterFormLastName()` | `submitRegisterForm(form)` |\n| Forgot Password | `initForgotPasswordForm(form)` | `setForgotPasswordFormEmail()` | `submitForgotPasswordForm(form)` |\n| Address | `initAddressForm(form)` | `setAddressFormField()` for each field | `submitAddressForm(form)` |\n| Contact | `initContactForm(form)` | `setContactFormField()` for each field | `submitContactForm(form)` |\n| Account Info | `initAccountInfoForm(form)` | `setAccountInfoFormField()` for each field | `submitAccountInfoForm(form)` |\n| Newsletter | `initNewsletterSubscriptionForm(form)` | `setNewsletterEmail()` | `submitNewsletterSubscriptionForm(form)` |\n| Coupon Code | \u2014 | `setCouponCode()` | `applyCouponCode()` |\n\n### Key Rules\n\n1. **Always call init first** \u2014 `init*Form()` sets up default field values, labels, and placeholders\n2. **Setter functions auto-validate** \u2014 if `form.isSubmitted` is true, calling any setter re-validates the form automatically\n3. **Check field.hasError for display** \u2014 show error messages only when `field.hasError` is true\n4. **Root components are automatically reactive** \u2014 forms use MobX observables which are automatically tracked in root components via `autorun()`. Use `observer()` only if you extract form logic into a sub-component\n5. **Submit returns boolean** \u2014 `submit*Form()` returns `true` on success, `false` on validation error or server failure",
|
|
227
227
|
"tags": [
|
|
228
228
|
"form",
|
|
229
229
|
"login",
|
|
@@ -236,7 +236,7 @@
|
|
|
236
236
|
"async-data-patterns": {
|
|
237
237
|
"title": "Async Data & Loading Patterns",
|
|
238
238
|
"description": "How to handle async operations, loading states, and store data in ikas components",
|
|
239
|
-
"content": "## Async Data & Loading Patterns\n\n### 1. Store State is Null Until Initialized\n\n**Note:** Root component exports are automatically reactive via the ikas runtime's `autorun()`
|
|
239
|
+
"content": "## Async Data & Loading Patterns\n\n### 1. Store State is Null Until Initialized\n\n**Note:** Root component exports are automatically reactive via the ikas runtime's `autorun()` \u2014 no `observer()` needed. The examples below show `observer()` on **sub-components** that independently read store data.\n\nStore data (`cartStore.cart`, `customerStore.customer`) is `null` until the store initializes. Use null-safe access:\n\n```tsx\nimport { observer } from \"@ikas/component-utils\";\nimport { cartStore } from \"@ikas/bp-storefront\";\n\n// Sub-component: needs observer() for independent reactivity\nconst CartBadge = observer(function CartBadge() {\n // cart is null until initialized \u2014 use optional chaining\n const itemCount = cartStore.cart?.orderLineItems.length ?? 0;\n return <span className=\"cart-badge\">{itemCount}</span>;\n});\n```\n\n### 2. Loading Pattern for Async Operations\n\nUse `useState` for local loading flags. Always use `try/finally` to clear the loading state:\n\n```tsx\nimport { useState } from \"preact/hooks\";\nimport { addItemToCart } from \"@ikas/bp-storefront\";\n\nconst [isLoading, setIsLoading] = useState(false);\n\nconst handleAddToCart = async () => {\n if (isLoading) return; // prevent double-click\n setIsLoading(true);\n try {\n await addItemToCart(variant, product, 1);\n } finally {\n setIsLoading(false);\n }\n};\n\n// In JSX:\n<button disabled={isLoading} onClick={handleAddToCart}>\n {isLoading ? \"Adding...\" : \"Add to Cart\"}\n</button>\n```\n\n### 3. Cart Operation Results\n\n`addItemToCart` returns a result object. Check for validation errors:\n\n```tsx\nconst result = await addItemToCart(variant, product, 1);\nif (result.success) {\n // Item added successfully\n} else if (result.validationError === \"INSUFFICIENT_STOCK\") {\n // Not enough stock\n} else if (result.validationError === \"INVALID_PRODUCT_OPTION_VALUES\") {\n // Variant options not fully selected\n}\n```\n\n### 4. Form Submission Results\n\nForm submit functions return `boolean` and set status flags on the form:\n\n```tsx\nconst success = await submitLoginForm(loginForm);\nif (success) {\n // loginForm.isSuccess is true\n Router.navigate(\"/account\");\n} else {\n // loginForm.isFailure is true\n // loginForm.responseMessage has the error message\n console.log(loginForm.responseMessage);\n}\n```\n\n### 5. Observer Auto Re-rendering\n\nStore updates automatically trigger re-renders. Root components get this via `autorun()`, sub-components via `observer()`. You do NOT need manual state management for store-derived values:\n\n```tsx\n// WRONG \u2014 unnecessary useState for store data\nconst [cartItems, setCartItems] = useState([]);\nuseEffect(() => { setCartItems(cartStore.cart?.orderLineItems ?? []); }, [cartStore.cart]);\n\n// CORRECT \u2014 observer sub-component for independent reactivity\nconst CartList = observer(function CartList() {\n const items = cartStore.cart?.orderLineItems ?? [];\n return (\n <div>\n {items.map((item) => (\n <div key={item.id}>{item.variant?.name}</div>\n ))}\n </div>\n );\n});\n\n// Root export uses CartList \u2014 no observer needed on root\nexport function CartPage({ title }: Props) {\n return (\n <section>\n <h1>{title}</h1>\n <CartList />\n </section>\n );\n}\n\nexport default CartPage;\n```\n\n### 6. Pagination / Data Fetching\n\nList stores (blog, brand, category) use async pagination functions:\n\n```tsx\nimport { hasBlogListNextPage, getBlogListNextPage } from \"@ikas/bp-storefront\";\n\n// Check if more pages exist\nif (hasBlogListNextPage(blogList)) {\n // Fetch next page \u2014 mutates blogList in place, observer re-renders\n await getBlogListNextPage(blogList);\n}\n```\n\nThese functions mutate the list model in place. If your component uses `observer()`, it will re-render automatically when new data arrives.",
|
|
240
240
|
"tags": [
|
|
241
241
|
"async",
|
|
242
242
|
"loading",
|
|
@@ -249,7 +249,7 @@
|
|
|
249
249
|
"product-detail-patterns": {
|
|
250
250
|
"title": "Product Detail Patterns (Serel Reference)",
|
|
251
251
|
"description": "Serel ProductDetail with 12 child components in two COMPONENT_LIST slots, ProductGallery local component, Breadcrumb navigation, and component slot architecture",
|
|
252
|
-
"content": "## Product Detail Patterns (Serel Reference)\n\nThe serel ProductDetail section is the most complex section in the theme, with **12 child components** distributed across **two COMPONENT_LIST slots** (`components` and `bottomComponents`), plus a local `ProductGallery` component and Breadcrumb navigation.\n\n### Two-Slot Architecture\n\nThe section declares two separate COMPONENT_LIST props:\n\n```json\n{\n \"props\": [\n {\n \"name\": \"components\",\n \"type\": \"COMPONENT_LIST\",\n \"filteredComponentIds\": [\n \"nb34u3yu-product-detail-name-favorite\",\n \"nb34u3yu-product-detail-sku\",\n \"nb34u3yu-product-detail-prices\",\n \"nb34u3yu-product-detail-product-group\",\n \"nb34u3yu-product-detail-variant\",\n \"nb34u3yu-product-detail-add-to-cart\",\n \"nb34u3yu-product-detail-features\",\n \"nb34u3yu-product-detail-description\",\n \"nb34u3yu-product-detail-bundle-product\",\n \"nb34u3yu-product-detail-option-set\",\n \"nb34u3yu-product-detail-offer\",\n \"nb34u3yu-product-detail-bundle-furniture\"\n ]\n },\n {\n \"name\": \"bottomComponents\",\n \"type\": \"COMPONENT_LIST\",\n \"filteredComponentIds\": [\"nb34u3yu-product-detail-description\"]\n }\n ]\n}\n```\n\n### Section Layout\n\n```tsx\nimport { IkasComponentRenderer } from \"@ikas/bp-storefront\";\nimport Breadcrumb from \"../../sub-components/Breadcrumb\";\nimport ProductGallery from \"./ProductGallery\"; // local component\nimport { Props } from \"./types\";\n\nexport function ProductDetail({ product, components, bottomComponents, backgroundColor, ...props }: Props) {\n if (!product) return null;\n return (\n <section className=\"product-detail\" style={backgroundColor ? { backgroundColor } : undefined}>\n <Breadcrumb product={product} />\n <div className=\"product-detail-inner\">\n <div className=\"product-detail-gallery\">\n <ProductGallery product={product} />\n </div>\n <div className=\"product-detail-info\">\n <IkasComponentRenderer id=\"product-detail-components\" components={components} parentProps={props} />\n </div>\n </div>\n <div className=\"product-detail-bottom\">\n <IkasComponentRenderer id=\"product-detail-bottom-components\" components={bottomComponents} parentProps={props} />\n </div>\n </section>\n );\n}\nexport default ProductDetail;\n```\n\n### 12 Child Components\n\n| Child Component | Purpose |\n|-----------------|--------|\n| ProductDetailNameFavorite | Product name + favorites toggle |\n| ProductDetailSku | SKU display |\n| ProductDetailPrices | Price display with discounts |\n| ProductDetailProductGroup | Product group/collection links |\n| ProductDetailVariant | Variant selector (size, color) |\n| ProductDetailAddToCart | Quantity selector + add-to-cart button |\n| ProductDetailFeatures | Feature list (uses FeatureItem children) |\n| ProductDetailDescription | Collapsible description (uses CollapsibleContent children) |\n| ProductDetailBundleProduct | Bundle product display |\n| ProductDetailBundleFurniture | Bundle furniture configuration |\n| ProductDetailOptionSet | Custom option sets |\n| ProductDetailOffer | Special offer display |\n\n### ProductGallery (Local Component)\n\nProductGallery is defined locally within the ProductDetail folder (not a registered component or sub-component). It handles image display with thumbnail navigation, zoom, and responsive srcset:\n\n```tsx\nconst images = (variant?.images ?? [])\n .map((pi) => pi.image)\n .filter((img): img is IkasImage => img != null);\n```\n\n### Breadcrumb Navigation\n\nThe Breadcrumb sub-component (from `src/sub-components/Breadcrumb/`) renders category path navigation:\n\n```tsx\nimport { getProductCategoryPath, getIkasCategoryHref } from \"@ikas/bp-storefront\";\nconst categoryPath = getProductCategoryPath(product);\n```\n\n### Key Pattern: Multiple COMPONENT_LIST Slots\n\nUsing two slots (`components` and `bottomComponents`) allows the store owner to arrange child components differently:\n- `components`
|
|
252
|
+
"content": "## Product Detail Patterns (Serel Reference)\n\nThe serel ProductDetail section is the most complex section in the theme, with **12 child components** distributed across **two COMPONENT_LIST slots** (`components` and `bottomComponents`), plus a local `ProductGallery` component and Breadcrumb navigation.\n\n### Two-Slot Architecture\n\nThe section declares two separate COMPONENT_LIST props:\n\n```json\n{\n \"props\": [\n {\n \"name\": \"components\",\n \"type\": \"COMPONENT_LIST\",\n \"filteredComponentIds\": [\n \"nb34u3yu-product-detail-name-favorite\",\n \"nb34u3yu-product-detail-sku\",\n \"nb34u3yu-product-detail-prices\",\n \"nb34u3yu-product-detail-product-group\",\n \"nb34u3yu-product-detail-variant\",\n \"nb34u3yu-product-detail-add-to-cart\",\n \"nb34u3yu-product-detail-features\",\n \"nb34u3yu-product-detail-description\",\n \"nb34u3yu-product-detail-bundle-product\",\n \"nb34u3yu-product-detail-option-set\",\n \"nb34u3yu-product-detail-offer\",\n \"nb34u3yu-product-detail-bundle-furniture\"\n ]\n },\n {\n \"name\": \"bottomComponents\",\n \"type\": \"COMPONENT_LIST\",\n \"filteredComponentIds\": [\"nb34u3yu-product-detail-description\"]\n }\n ]\n}\n```\n\n### Section Layout\n\n```tsx\nimport { IkasComponentRenderer } from \"@ikas/bp-storefront\";\nimport Breadcrumb from \"../../sub-components/Breadcrumb\";\nimport ProductGallery from \"./ProductGallery\"; // local component\nimport { Props } from \"./types\";\n\nexport function ProductDetail({ product, components, bottomComponents, backgroundColor, ...props }: Props) {\n if (!product) return null;\n return (\n <section className=\"product-detail\" style={backgroundColor ? { backgroundColor } : undefined}>\n <Breadcrumb product={product} />\n <div className=\"product-detail-inner\">\n <div className=\"product-detail-gallery\">\n <ProductGallery product={product} />\n </div>\n <div className=\"product-detail-info\">\n <IkasComponentRenderer id=\"product-detail-components\" components={components} parentProps={props} />\n </div>\n </div>\n <div className=\"product-detail-bottom\">\n <IkasComponentRenderer id=\"product-detail-bottom-components\" components={bottomComponents} parentProps={props} />\n </div>\n </section>\n );\n}\nexport default ProductDetail;\n```\n\n### 12 Child Components\n\n| Child Component | Purpose |\n|-----------------|--------|\n| ProductDetailNameFavorite | Product name + favorites toggle |\n| ProductDetailSku | SKU display |\n| ProductDetailPrices | Price display with discounts |\n| ProductDetailProductGroup | Product group/collection links |\n| ProductDetailVariant | Variant selector (size, color) |\n| ProductDetailAddToCart | Quantity selector + add-to-cart button |\n| ProductDetailFeatures | Feature list (uses FeatureItem children) |\n| ProductDetailDescription | Collapsible description (uses CollapsibleContent children) |\n| ProductDetailBundleProduct | Bundle product display |\n| ProductDetailBundleFurniture | Bundle furniture configuration |\n| ProductDetailOptionSet | Custom option sets |\n| ProductDetailOffer | Special offer display |\n\n### ProductGallery (Local Component)\n\nProductGallery is defined locally within the ProductDetail folder (not a registered component or sub-component). It handles image display with thumbnail navigation, zoom, and responsive srcset:\n\n```tsx\nconst images = (variant?.images ?? [])\n .map((pi) => pi.image)\n .filter((img): img is IkasImage => img != null);\n```\n\n### Breadcrumb Navigation\n\nThe Breadcrumb sub-component (from `src/sub-components/Breadcrumb/`) renders category path navigation:\n\n```tsx\nimport { getProductCategoryPath, getIkasCategoryHref } from \"@ikas/bp-storefront\";\nconst categoryPath = getProductCategoryPath(product);\n```\n\n### Key Pattern: Multiple COMPONENT_LIST Slots\n\nUsing two slots (`components` and `bottomComponents`) allows the store owner to arrange child components differently:\n- `components` \u2014 displayed next to the gallery in a side panel\n- `bottomComponents` \u2014 displayed full-width below the gallery\n\nThis pattern can be applied to any section that needs multiple layout regions.",
|
|
253
253
|
"tags": [
|
|
254
254
|
"product",
|
|
255
255
|
"detail",
|
|
@@ -267,7 +267,7 @@
|
|
|
267
267
|
"product-list-patterns": {
|
|
268
268
|
"title": "Product List & Filtering Patterns",
|
|
269
269
|
"description": "Category pages, product filtering with display-type-aware rendering (swatch, number range, checkbox), sorting, pagination, and search patterns",
|
|
270
|
-
"content": "## Product List & Filtering Patterns\n\nProduct list sections power category pages, search results, and collection displays. They combine filtering, sorting, and pagination.\n\n### Product List Prop Setup\n\nIn `ikas.config.json`, use the `PRODUCT_LIST` prop type:\n```json\n{\n \"name\": \"productList\",\n \"displayName\": \"Product List\",\n \"type\": \"PRODUCT_LIST\",\n \"required\": true\n}\n```\n\nThis provides an `IkasProductList` object with products, filters, sorting, and pagination data.\n\n### Displaying Products\n\n```tsx\nimport {\n IkasProductList,\n getSelectedProductVariant,\n getProductVariantFormattedFinalPrice,\n getProductVariantMainImage,\n getSelectedProductVariantHref,\n getDefaultSrc,\n} from \"@ikas/bp-storefront\";\n\n// In your component:\nconst products = productList?.data ?? [];\n\n{products.map((product) => {\n const variant = getSelectedProductVariant(product);\n const productImage = getProductVariantMainImage(variant);\n const image = productImage?.image ?? null;\n const price = getProductVariantFormattedFinalPrice(variant) as unknown as string;\n const href = getSelectedProductVariantHref(product);\n\n return (\n <a key={product.id} href={href} className=\"product-card\">\n {image && <img src={getDefaultSrc(image)} alt={product.name} />}\n <h3>{product.name}</h3>\n <span>{price}</span>\n </a>\n );\n})}\n```\n\n### Filtering\n\nFilters let customers narrow down products by attributes (size, color, price, etc.). Each filter has a `displayType` that determines how it should be rendered. **Always check the display type and render accordingly**
|
|
270
|
+
"content": "## Product List & Filtering Patterns\n\nProduct list sections power category pages, search results, and collection displays. They combine filtering, sorting, and pagination.\n\n### Product List Prop Setup\n\nIn `ikas.config.json`, use the `PRODUCT_LIST` prop type:\n```json\n{\n \"name\": \"productList\",\n \"displayName\": \"Product List\",\n \"type\": \"PRODUCT_LIST\",\n \"required\": true\n}\n```\n\nThis provides an `IkasProductList` object with products, filters, sorting, and pagination data.\n\n### Displaying Products\n\n```tsx\nimport {\n IkasProductList,\n getSelectedProductVariant,\n getProductVariantFormattedFinalPrice,\n getProductVariantMainImage,\n getSelectedProductVariantHref,\n getDefaultSrc,\n} from \"@ikas/bp-storefront\";\n\n// In your component:\nconst products = productList?.data ?? [];\n\n{products.map((product) => {\n const variant = getSelectedProductVariant(product);\n const productImage = getProductVariantMainImage(variant);\n const image = productImage?.image ?? null;\n const price = getProductVariantFormattedFinalPrice(variant) as unknown as string;\n const href = getSelectedProductVariantHref(product);\n\n return (\n <a key={product.id} href={href} className=\"product-card\">\n {image && <img src={getDefaultSrc(image)} alt={product.name} />}\n <h3>{product.name}</h3>\n <span>{price}</span>\n </a>\n );\n})}\n```\n\n### Filtering\n\nFilters let customers narrow down products by attributes (size, color, price, etc.). Each filter has a `displayType` that determines how it should be rendered. **Always check the display type and render accordingly** \u2014 do not render all filters as checkboxes.\n\n#### Display Types\n\n- **BOX / LIST** \u2014 Standard checkbox filters (size, brand, etc.)\n- **SWATCH** \u2014 Color/pattern swatches with `fv.colorCode` or thumbnail images\n- **NUMBER_RANGE_LIST** \u2014 Predefined price/number range buttons (e.g., $0-$50, $50-$100)\n\n#### Universal Data Pattern\n\nThe data flow is the same for all display types:\n- `getFilterDisplayedValues(filter)` \u2014 get sorted filter values (works for ALL types)\n- `handleFilterValueClick(productList, filter, filterValue)` \u2014 toggle selection + auto-refetch (works for BOX/LIST/SWATCH)\n- `handleNumberRangeOptionClick(productList, filter, option)` \u2014 toggle a range option + auto-refetch (for NUMBER_RANGE_LIST)\n\n#### Imports\n\n```tsx\nimport {\n getFilterDisplayedValues,\n handleFilterValueClick,\n handleNumberRangeOptionClick,\n getProductListFilterCategories,\n isBoxFilter,\n isListFilter,\n isSwatchFilter,\n getIkasFilterThumbnailImage,\n getDefaultSrc,\n IkasProductList,\n IkasProductFilter,\n} from \"@ikas/bp-storefront\";\n```\n\n#### Display-Type-Aware Rendering\n\n```tsx\nconst filters = productList.filters ?? [];\n\n{filters.map((filter) => {\n const values = getFilterDisplayedValues(filter);\n if (values.length === 0 && !filter.numberRangeListOptions?.length) return null;\n\n return (\n <div key={filter.id}>\n <h4>{filter.name}</h4>\n\n {/* SWATCH: render color circles / thumbnail images */}\n {isSwatchFilter(filter) ? (\n <div className=\"swatch-grid\">\n {values.map((fv) => {\n const thumbnail = getIkasFilterThumbnailImage(fv);\n return (\n <button\n key={fv.name}\n className={fv.isSelected === true ? \"swatch selected\" : \"swatch\"}\n onClick={() => handleFilterValueClick(productList, filter, fv)}\n title={fv.name}\n >\n {thumbnail ? (\n <img src={getDefaultSrc(thumbnail)} alt={fv.name} />\n ) : (\n <span\n className=\"swatch-color\"\n style={{ backgroundColor: fv.colorCode ?? \"#ccc\" }}\n />\n )}\n </button>\n );\n })}\n </div>\n ) : (\n /* BOX / LIST: render checkboxes */\n values.map((fv) => (\n <label key={fv.name}>\n <input\n type=\"checkbox\"\n checked={fv.isSelected === true}\n onChange={() => handleFilterValueClick(productList, filter, fv)}\n />\n {fv.name} {fv.resultCount != null && `(${fv.resultCount})`}\n </label>\n ))\n )}\n\n {/* NUMBER_RANGE_LIST: render predefined range buttons */}\n {filter.numberRangeListOptions?.map((option) => (\n <button\n key={`${option.from}-${option.to}`}\n className={option.isSelected ? \"range-btn selected\" : \"range-btn\"}\n onClick={() => handleNumberRangeOptionClick(productList, filter, option)}\n >\n {option.from} - {option.to ?? \"+\"}\n </button>\n ))}\n </div>\n );\n})}\n```\n\n### Sorting\n\n```tsx\nimport { getProductListSortOptions, setSortType, IkasProductListSortType } from \"@ikas/bp-storefront\";\n\nconst sortOptions = getProductListSortOptions(productList);\n\n<select\n value={productList.sort}\n onChange={(e) => {\n setSortType(productList, (e.target as HTMLSelectElement).value as IkasProductListSortType);\n }}\n>\n {sortOptions.map((opt) => (\n <option key={opt.value} value={opt.value}>{opt.label}</option>\n ))}\n</select>\n```\n\n### Pagination\n\n```tsx\nimport {\n hasProductListNextPage,\n hasProductListPrevPage,\n getProductListNextPage,\n getProductListPrevPage,\n} from \"@ikas/bp-storefront\";\n\nconst hasNext = hasProductListNextPage(productList);\nconst hasPrev = hasProductListPrevPage(productList);\n\n<div className=\"pagination\">\n <button disabled={!hasPrev} onClick={() => getProductListPrevPage(productList)}>\n Previous\n </button>\n <button disabled={!hasNext} onClick={() => getProductListNextPage(productList)}>\n Next\n </button>\n</div>\n```\n\n### Key Functions Reference\n\n| Function | Purpose |\n|----------|---------|\n| `getProductListFilterCategories(list)` | Get available filter categories |\n| `getFilterDisplayedValues(filter)` | Get sorted filter values \u2014 works for ALL display types |\n| `handleFilterValueClick(list, filter, value)` | Toggle a filter value + auto-refetch (BOX/LIST/SWATCH) |\n| `handleNumberRangeOptionClick(list, filter, option)` | Toggle a range option + auto-refetch (NUMBER_RANGE_LIST) |\n| `isBoxFilter(filter)` | Check if filter displayType is BOX |\n| `isListFilter(filter)` | Check if filter displayType is LIST |\n| `isSwatchFilter(filter)` | Check if filter displayType is SWATCH |\n| `isCustomValueFilter(filter)` | Check if filter displayType is CUSTOM_VALUE |\n| `getIkasFilterThumbnailImage(fv)` | Get swatch thumbnail IkasImage from a filter value |\n| `getProductListSortOptions(list)` | Get sort dropdown options |\n| `hasProductListNextPage(list)` / `getProductListNextPage(list)` | Next page |\n| `hasProductListPrevPage(list)` / `getProductListPrevPage(list)` | Previous page |",
|
|
271
271
|
"tags": [
|
|
272
272
|
"product-list",
|
|
273
273
|
"filtering",
|
|
@@ -283,7 +283,7 @@
|
|
|
283
283
|
"cart-patterns": {
|
|
284
284
|
"title": "Cart Management Patterns (Serel Reference)",
|
|
285
285
|
"description": "Serel CartPage section with CartItem sub-component, OrderSummary/CouponCode/EmptyState local components, cartStore patterns, and checkout flow",
|
|
286
|
-
"content": "## Cart Management Patterns (Serel Reference)\n\nThe serel CartPage is a section that uses local internal components (OrderSummary, CouponCode, EmptyState) and the shared `CartItem` sub-component from `src/sub-components/CartItem/`. Unlike most sections, CartPage does NOT use IkasComponentRenderer
|
|
286
|
+
"content": "## Cart Management Patterns (Serel Reference)\n\nThe serel CartPage is a section that uses local internal components (OrderSummary, CouponCode, EmptyState) and the shared `CartItem` sub-component from `src/sub-components/CartItem/`. Unlike most sections, CartPage does NOT use IkasComponentRenderer \u2014 it directly composes its UI from sub-components.\n\n### CartPage Section Structure\n\n```tsx\nimport { cartStore } from \"@ikas/bp-storefront\";\nimport CartItem from \"../../sub-components/CartItem\";\nimport { Props } from \"./types\";\n\nexport function CartPage({ backgroundColor, emptyCartText, ...props }: Props) {\n const cart = cartStore.cart;\n const lineItems = cart?.orderLineItems ?? [];\n const isEmpty = lineItems.length === 0;\n\n if (isEmpty) return <EmptyState text={emptyCartText} />;\n\n return (\n <section className=\"cart-page\" style={backgroundColor ? { backgroundColor } : undefined}>\n <div className=\"cart-page-inner\">\n <div className=\"cart-items\">\n {lineItems.map((item) => (\n <CartItem key={item.id} item={item} />\n ))}\n </div>\n <div className=\"cart-sidebar\">\n <CouponCode cart={cart} />\n <OrderSummary cart={cart} />\n </div>\n </div>\n </section>\n );\n}\nexport default CartPage;\n```\n\n### CartItem Sub-Component\n\nThe `CartItem` sub-component (in `src/sub-components/CartItem/`) displays a single line item with image, variant name, quantity controls, price, and remove button:\n\n```tsx\nimport {\n getOrderLineItemFormattedFinalPrice,\n getOrderLineItemFormattedUnitPrice,\n getIkasOrderLineVariantMainImage,\n getDefaultSrc,\n changeItemQuantity,\n removeItem,\n IkasOrderLineItem,\n} from \"@ikas/bp-storefront\";\nimport QuantitySelector from \"../QuantitySelector\";\n\ninterface Props { item: IkasOrderLineItem; }\n\nexport default function CartItem({ item }: Props) {\n const image = item.variant ? getIkasOrderLineVariantMainImage(item.variant) : null;\n\n return (\n <div className=\"cart-item\">\n {image && <img src={getDefaultSrc(image)} alt={item.variant?.name} />}\n <div className=\"cart-item-info\">\n <span>{item.variant?.name}</span>\n <span>{getOrderLineItemFormattedUnitPrice(item)}</span>\n </div>\n <QuantitySelector\n quantity={item.quantity}\n onIncrement={() => changeItemQuantity(item, item.quantity + 1)}\n onDecrement={() => changeItemQuantity(item, Math.max(1, item.quantity - 1))}\n />\n <span>{getOrderLineItemFormattedFinalPrice(item)}</span>\n <button onClick={() => removeItem(item)}>Remove</button>\n </div>\n );\n}\n```\n\n### OrderSummary Local Component\n\nDisplays cart totals using `getIkasOrderFormattedTotalFinalPrice(cart)` and `getIkasOrderFormattedTotalPrice(cart)`, plus a checkout button:\n\n```tsx\nimport { Router, getIkasOrderFormattedTotalFinalPrice } from \"@ikas/bp-storefront\";\n\nfunction OrderSummary({ cart }) {\n return (\n <div className=\"order-summary\">\n <span>Total: {getIkasOrderFormattedTotalFinalPrice(cart)}</span>\n <button onClick={() => Router.navigateToPage(\"CHECKOUT\")}>Checkout</button>\n </div>\n );\n}\n```\n\n### CouponCode Local Component\n\nHandles coupon code input and submission:\n\n```tsx\nimport { initCouponCodeForm, setCouponCode, submitCouponCodeForm } from \"@ikas/bp-storefront\";\n\nfunction CouponCode({ cart }) {\n useEffect(() => { initCouponCodeForm(cart); }, []);\n const [code, setCode] = useState(\"\");\n\n const handleApply = async () => {\n setCouponCode(cart, code);\n await submitCouponCodeForm(cart);\n };\n\n return (\n <div className=\"coupon\">\n <input value={code} onInput={(e) => setCode((e.target as HTMLInputElement).value)} />\n <button onClick={handleApply}>Apply</button>\n </div>\n );\n}\n```\n\n### cartStore Usage Patterns\n\nThe `cartStore` is a MobX store available globally. Root components are automatically reactive via autorun, so reads from `cartStore.cart` trigger re-renders:\n\n```tsx\nimport { cartStore } from \"@ikas/bp-storefront\";\n\n// Always null-safe:\nconst cart = cartStore.cart;\nconst lineItems = cart?.orderLineItems ?? [];\nconst itemCount = lineItems.length;\nconst isEmpty = itemCount === 0;\n```\n\n### Key Pattern: Section Without IkasComponentRenderer\n\nCartPage demonstrates that not all sections need COMPONENT_LIST slots. Simple sections can directly compose sub-components and local components when the layout is fixed and does not need editor-level child component placement.",
|
|
287
287
|
"tags": [
|
|
288
288
|
"cart",
|
|
289
289
|
"checkout",
|
|
@@ -313,7 +313,7 @@
|
|
|
313
313
|
"header-footer-patterns": {
|
|
314
314
|
"title": "Header & Footer Patterns (Serel Reference)",
|
|
315
315
|
"description": "IkasComponentRenderer-based Header with Navbar/Announcements/CookieBar children, Footer with SocialMediaIcon children, Toast system, isHeader/isFooter flags",
|
|
316
|
-
"content": "## How to build a container section (Header/Footer pattern)\n\nContainer sections are NOT self-contained. The pattern is **always** 3 steps:\n\n1. **Create each child component first.** Capture the `componentId` from each CLI response:\n ```bash\n npx ikas-component config add-component --name Navbar --type component --props '[...]'\n #
|
|
316
|
+
"content": "## How to build a container section (Header/Footer pattern)\n\nContainer sections are NOT self-contained. The pattern is **always** 3 steps:\n\n1. **Create each child component first.** Capture the `componentId` from each CLI response:\n ```bash\n npx ikas-component config add-component --name Navbar --type component --props '[...]'\n # \u2192 { \"success\": true, \"componentId\": \"7ojrigep-Eml9n5sN3i\", ... }\n npx ikas-component config add-component --name Announcements --type component --props '[...]'\n npx ikas-component config add-component --name CookieBar --type component --props '[...]'\n ```\n\n2. **Create the parent section** (without `filteredComponentIds` \u2014 those reference ids that don't exist yet):\n ```bash\n npx ikas-component config add-component --name Header --type section --isHeader \\\n --props '[{\"name\":\"components\",\"type\":\"COMPONENT_LIST\"},{\"name\":\"backgroundColor\",\"type\":\"COLOR\"}]'\n ```\n\n3. **Wire the parent's COMPONENT_LIST slot** to the captured child ids:\n ```bash\n npx ikas-component config update-prop --component Header --prop components \\\n --filteredComponentIds '[\"7ojrigep-Eml9n5sN3i\", \"<ANNOUNCEMENTS_ID>\", \"<COOKIEBAR_ID>\"]'\n ```\n\nIf you skip Step 3 the slot is empty and the editor will let the store owner drag in ANY component \u2014 usually not what you want. Component ids are opaque random strings; you cannot guess or derive them from names. Use `config list` to look up existing ids at any time.\n\n---\n\n## Header & Footer Patterns (Serel Reference)\n\nIn the serel theme, the Header and Footer are sections that use `IkasComponentRenderer` to render child components. This is the recommended production pattern.\n\n### Header Architecture\n\nThe serel Header section declares a `COMPONENT_LIST` prop with `filteredComponentIds` restricting it to three child components: **Navbar**, **Announcements**, and **CookieBar**.\n\n```tsx\nimport { IkasComponentRenderer } from \"@ikas/bp-storefront\";\nimport { Props } from \"./types\";\n\nexport function Header({ components, backgroundColor, ...props }: Props) {\n return (\n <header className=\"header\" style={backgroundColor ? { backgroundColor } : undefined}>\n <IkasComponentRenderer id=\"header-components\" components={components} parentProps={props} />\n <ToastContainer />\n </header>\n );\n}\nexport default Header;\n```\n\nConfig setup with `isHeader: true` and `filteredComponentIds`:\n```json\n{\n \"id\": \"7ojrigep-IBBg5nI1PC\",\n \"name\": \"Header\",\n \"type\": \"section\",\n \"isHeader\": true,\n \"props\": [\n {\n \"name\": \"components\",\n \"type\": \"COMPONENT_LIST\",\n \"filteredComponentIds\": [\"<NAVBAR_ID>\", \"<ANNOUNCEMENTS_ID>\", \"<COOKIEBAR_ID>\"]\n }\n ]\n}\n```\n\nThe `isHeader: true` flag ensures this section appears on every page automatically.\n\n### Toast Notification System\n\nThe serel Header hosts a `ToastContainer` component. The `useToast` hook provides a global notification system:\n\n```tsx\nimport { useToast } from \"../../hooks/useToast\";\n\n// In any component:\nconst { showToast } = useToast();\nshowToast({ message: \"Added to cart!\", type: \"success\" });\n```\n\nThe ToastContainer renders as a portal to ensure toasts appear above all content. Toasts stack vertically and auto-dismiss after a timeout.\n\n### Navbar Child Component\n\nThe Navbar child component handles: logo display, navigation links (`LIST_OF_LINK`), cart badge (reads `cartStore`), account icon (reads `customerStore`), search functionality, and a mobile hamburger menu. It also has its own `COMPONENT_LIST` slot for product search results using CardProductName/CardProductVariants/CardProductPrice with `privateVarMap` for the product variable.\n\n### Announcements Child Component\n\nThe Announcements child component uses `IkasThemeSlider` for a rotating announcement bar. It has its own child component slot for individual Announcement items.\n\n### CookieBar Child Component\n\nThe CookieBar manages cookie consent with accept/decline actions and persists the choice to localStorage.\n\n### Footer Architecture\n\nThe serel Footer section uses `IkasComponentRenderer` with a `COMPONENT_LIST` filtered to **SocialMediaIcon** children:\n\n```tsx\nimport { IkasComponentRenderer } from \"@ikas/bp-storefront\";\nimport { Props } from \"./types\";\n\nexport function Footer({ components, backgroundColor, ...props }: Props) {\n return (\n <footer className=\"footer\" style={backgroundColor ? { backgroundColor } : undefined}>\n <div className=\"footer-inner\">\n {/* Logo, link columns, contact info */}\n <div className=\"footer-social\">\n <IkasComponentRenderer id=\"footer-components\" components={components} parentProps={props} />\n </div>\n {/* Copyright */}\n </div>\n </footer>\n );\n}\nexport default Footer;\n```\n\nConfig with `isFooter: true`:\n```json\n{\n \"id\": \"7ojrigep-fTZb2qP9aV\",\n \"name\": \"Footer\",\n \"type\": \"section\",\n \"isFooter\": true,\n \"props\": [\n {\n \"name\": \"components\",\n \"type\": \"COMPONENT_LIST\",\n \"filteredComponentIds\": [\"<SOCIALMEDIAICON_ID>\"]\n }\n ]\n}\n```\n\n### Key Patterns\n\n- Headers and footers use `IkasComponentRenderer` with `filteredComponentIds` for controlled child composition\n- `isHeader: true` / `isFooter: true` flags in config make them appear on all pages\n- Root exports are automatically reactive (cartStore/customerStore tracked by autorun)\n- Toast system lives in the Header for global accessibility\n- `parentProps` passes section data to child components\n- The Navbar child component itself nests additional child components via its own COMPONENT_LIST\n\n### Mega-Menu / MenuItem (When Per-Link Items Are Rich)\n\nIf the header needs items with metadata beyond `href + label` (image, badge, column count, etc.), `LIST_OF_LINK` cannot carry it. Build a `MenuItem` child and wire it into a `COMPONENT_LIST` slot exactly like Navbar/Announcements/CookieBar above. For the full pick-the-right-prop-type tree (and when `LIST_OF_LINK` IS sufficient), see `get_migration_guide(\"link-prop-decision-guide\")` and `get_migration_guide(\"component-composition-decision-guide\")`.\n",
|
|
317
317
|
"tags": [
|
|
318
318
|
"header",
|
|
319
319
|
"footer",
|
|
@@ -333,7 +333,7 @@
|
|
|
333
333
|
"review-patterns": {
|
|
334
334
|
"title": "Customer Review Patterns",
|
|
335
335
|
"description": "Review display, star ratings, review submission form, and login-required checks",
|
|
336
|
-
"content": "## Customer Review Patterns\n\nProduct reviews allow customers to rate and comment on products. The pattern includes displaying existing reviews and a submission form.\n\n### Displaying Reviews\n\n```tsx\nimport {\n getProductCustomerReviews,\n getIkasCustomerReviewFormattedDate,\n} from \"@ikas/bp-storefront\";\n\n// In your component (product prop):\nconst reviews = getProductCustomerReviews(product) ?? [];\n\n{reviews.map((review) => (\n <div key={review.id} className=\"review-card\">\n <div className=\"review-stars\">\n {[1, 2, 3, 4, 5].map((star) => (\n <span key={star} className={star <= review.star ? \"filled\" : \"empty\"}
|
|
336
|
+
"content": "## Customer Review Patterns\n\nProduct reviews allow customers to rate and comment on products. The pattern includes displaying existing reviews and a submission form.\n\n### Displaying Reviews\n\n```tsx\nimport {\n getProductCustomerReviews,\n getIkasCustomerReviewFormattedDate,\n} from \"@ikas/bp-storefront\";\n\n// In your component (product prop):\nconst reviews = getProductCustomerReviews(product) ?? [];\n\n{reviews.map((review) => (\n <div key={review.id} className=\"review-card\">\n <div className=\"review-stars\">\n {[1, 2, 3, 4, 5].map((star) => (\n <span key={star} className={star <= review.star ? \"filled\" : \"empty\"}>\u2605</span>\n ))}\n </div>\n <h4>{review.title}</h4>\n <p>{review.comment}</p>\n <span className=\"review-date\">{getIkasCustomerReviewFormattedDate(review)}</span>\n <span className=\"review-author\">{review.customerName}</span>\n </div>\n))}\n```\n\n### Review Submission Form\n\n```tsx\nimport {\n getIkasProductCustomerReviewForm,\n setCustomerReviewFormTitle,\n setCustomerReviewFormStar,\n setCustomerReviewFormComment,\n submitCustomerReviewForm,\n isCustomerReviewLoginRequired,\n customerStore,\n hasCustomer,\n Router,\n} from \"@ikas/bp-storefront\";\n\nconst reviewForm = getIkasProductCustomerReviewForm(product);\nconst loginRequired = isCustomerReviewLoginRequired() as unknown as boolean;\nconst isLoggedIn = hasCustomer(customerStore) as unknown as boolean;\n\n// If login is required and user is not logged in\nif (loginRequired && !isLoggedIn) {\n return (\n <div>\n <p>Please log in to write a review.</p>\n <button onClick={() => Router.navigateToPage(\"LOGIN\")}>Log In</button>\n </div>\n );\n}\n\n// Star rating input\nconst [hoverStar, setHoverStar] = useState(0);\n\n<div className=\"star-input\">\n {[1, 2, 3, 4, 5].map((star) => (\n <button\n key={star}\n className={star <= (hoverStar || reviewForm.star.value) ? \"filled\" : \"empty\"}\n onMouseEnter={() => setHoverStar(star)}\n onMouseLeave={() => setHoverStar(0)}\n onClick={() => setCustomerReviewFormStar(reviewForm, star)}\n >\n \u2605\n </button>\n ))}\n</div>\n\n// Title and comment inputs\n<input\n value={reviewForm.title.value}\n onInput={(e) => setCustomerReviewFormTitle(reviewForm, (e.target as HTMLInputElement).value)}\n/>\n<textarea\n value={reviewForm.comment.value}\n onInput={(e) => setCustomerReviewFormComment(reviewForm, (e.target as HTMLTextAreaElement).value)}\n/>\n\n// Submit\nconst handleSubmit = async (e: Event) => {\n e.preventDefault();\n const success = await submitCustomerReviewForm(reviewForm);\n if (success) {\n // Review submitted successfully\n }\n};\n```\n\n### Key Functions\n\n| Function | Purpose |\n|----------|---------|\n| `getProductCustomerReviews(product)` | Get reviews for a product |\n| `getIkasProductCustomerReviewForm(product)` | Get the review form model |\n| `setCustomerReviewFormTitle(form, value)` | Set review title |\n| `setCustomerReviewFormStar(form, value)` | Set star rating (1-5) |\n| `setCustomerReviewFormComment(form, value)` | Set review comment |\n| `submitCustomerReviewForm(form)` | Submit the review |\n| `isCustomerReviewLoginRequired()` | Check if login is needed to review |\n| `getIkasCustomerReviewFormattedDate(review)` | Format review date |",
|
|
337
337
|
"tags": [
|
|
338
338
|
"reviews",
|
|
339
339
|
"ratings",
|
|
@@ -378,7 +378,7 @@
|
|
|
378
378
|
"navigation-patterns": {
|
|
379
379
|
"title": "Navigation & Routing Patterns",
|
|
380
380
|
"description": "Router.navigate, page navigation, href getters, breadcrumbs, and query parameters",
|
|
381
|
-
"content": "## Navigation & Routing Patterns\n\nikas provides a `Router` utility and various href getter functions for navigation.\n\n### Basic Navigation\n\n```tsx\nimport { Router } from \"@ikas/bp-storefront\";\n\n// Navigate to a URL\nRouter.navigate(\"/products\");\n\n// Navigate with options\nRouter.navigate(\"/products\", true); // shallow navigation (no full page reload)\nRouter.navigate(\"/products\", false, true); // open in new tab\n// Signature: Router.navigate(path: string, shallow?: boolean, openInNewTab?: boolean)\n\n// Navigate to a named page\nRouter.navigateToPage(\"LOGIN\");\nRouter.navigateToPage(\"REGISTER\");\nRouter.navigateToPage(\"FORGOT_PASSWORD\");\nRouter.navigateToPage(\"ACCOUNT\");\nRouter.navigateToPage(\"CART\");\nRouter.navigateToPage(\"CHECKOUT\");\n\n// Navigate with options\nRouter.navigateToPage(\"CATEGORY\", {\n queryParams: { sort: \"price_asc\" },\n shallow: false,\n newTab: false,\n});\n\n// Go back\nRouter.goBack();\n```\n\n### Page Type Constants\n\nAvailable page types for `navigateToPage()`:\n- `INDEX`
|
|
381
|
+
"content": "## Navigation & Routing Patterns\n\nikas provides a `Router` utility and various href getter functions for navigation.\n\n### Basic Navigation\n\n```tsx\nimport { Router } from \"@ikas/bp-storefront\";\n\n// Navigate to a URL\nRouter.navigate(\"/products\");\n\n// Navigate with options\nRouter.navigate(\"/products\", true); // shallow navigation (no full page reload)\nRouter.navigate(\"/products\", false, true); // open in new tab\n// Signature: Router.navigate(path: string, shallow?: boolean, openInNewTab?: boolean)\n\n// Navigate to a named page\nRouter.navigateToPage(\"LOGIN\");\nRouter.navigateToPage(\"REGISTER\");\nRouter.navigateToPage(\"FORGOT_PASSWORD\");\nRouter.navigateToPage(\"ACCOUNT\");\nRouter.navigateToPage(\"CART\");\nRouter.navigateToPage(\"CHECKOUT\");\n\n// Navigate with options\nRouter.navigateToPage(\"CATEGORY\", {\n queryParams: { sort: \"price_asc\" },\n shallow: false,\n newTab: false,\n});\n\n// Go back\nRouter.goBack();\n```\n\n### Page Type Constants\n\nAvailable page types for `navigateToPage()`:\n- `INDEX` \u2014 Home page\n- `CATEGORY` \u2014 Category listing\n- `PRODUCT_DETAIL` \u2014 Product detail\n- `BLOG` \u2014 Blog listing\n- `BLOG` \u2014 Blog post detail\n- `CART` \u2014 Shopping cart\n- `CHECKOUT` \u2014 Checkout flow\n- `LOGIN` \u2014 Login page\n- `REGISTER` \u2014 Registration page\n- `FORGOT_PASSWORD` \u2014 Password recovery\n- `ACCOUNT` \u2014 Customer account\n- `SEARCH` \u2014 Search results\n- `CUSTOM` \u2014 Custom pages\n\n### Href Getter Functions\n\nThese return URL strings for use in `<a href>` tags:\n\n```tsx\nimport {\n getSelectedProductVariantHref,\n getIkasCategoryHref,\n getIkasOrderHref,\n getIkasBlogHref,\n getIkasBrandHref,\n} from \"@ikas/bp-storefront\";\n\n// Product link\n<a href={getSelectedProductVariantHref(product)}>{product.name}</a>\n\n// Category link\n<a href={getIkasCategoryHref(category)}>{category.name}</a>\n\n// Order link\n<a href={getIkasOrderHref(order)}>Order #{order.orderNumber}</a>\n\n// Blog link\n<a href={getIkasBlogHref(blog)}>{blog.title}</a>\n\n// Brand link\n<a href={getIkasBrandHref(brand)}>{brand.name}</a>\n```\n\n### Breadcrumbs\n\n```tsx\nimport {\n getCategoryPath,\n getProductCategoryPath,\n getIkasCategoryHref,\n} from \"@ikas/bp-storefront\";\n\n// For category pages\nconst categoryPath = getCategoryPath(category);\n\n// For product pages\nconst productPath = getProductCategoryPath(product);\n\n<nav className=\"breadcrumbs\">\n <a href=\"/\">Home</a>\n {categoryPath?.map((cat, i) => (\n <span key={cat.id}>\n <span className=\"separator\">/</span>\n <a href={getIkasCategoryHref(cat)}>{cat.name}</a>\n </span>\n ))}\n</nav>\n```\n\n### Using Links vs Navigation\n\n- Use `<a href={getXxxHref(...)}>` for standard links (SEO-friendly, right-click works)\n- Use `Router.navigate()` for programmatic navigation after form submission or actions\n- Use `Router.navigateToPage()` for navigating to known page types\n- Use `Router.goBack()` for back buttons\n\n### Rich Navigation Items (When LIST_OF_LINK Is Insufficient)\n\n`LIST_OF_LINK` gives you `IkasNavigationLinkList` \u2192 an array of `IkasNavigationLink` items with `{ href, label, subLinks, openInNewTab, itemId, type }`. There is **no slot for an image, color, icon, column count, or any other custom field per link**.\n\nIf the navigation needs richer per-item data \u2014 a mega-menu with thumbnails, colored category chips, multi-column submenus, badges, custom icons \u2014 `LIST_OF_LINK` is the wrong tool. Build a dedicated component instead:\n\n```bash\nnpx ikas-component config add-component --name \"MenuItem\" --type component \\\n --props '[{\"name\":\"link\",\"type\":\"LINK\"},{\"name\":\"image\",\"type\":\"IMAGE\"},{\"name\":\"title\",\"type\":\"TEXT\"}]'\n```\n\nThen wire the parent header/navbar with a `COMPONENT_LIST` prop and `filteredComponentIds: [\"<MenuItem id>\"]`. Each menu item becomes a self-contained component instance with its own link, image, and title.\n\nSee `get_framework_guide(\"header-footer-patterns\")` for the COMPONENT_LIST recipe and `get_migration_guide(\"custom-data-conversion\")` for the broader enum-vs-component decision framework (the mega-menu case is one of the worked examples).\n",
|
|
382
382
|
"tags": [
|
|
383
383
|
"navigation",
|
|
384
384
|
"router",
|
|
@@ -391,8 +391,8 @@
|
|
|
391
391
|
},
|
|
392
392
|
"real-world-architecture": {
|
|
393
393
|
"title": "Real-World Theme Architecture (Serel Reference)",
|
|
394
|
-
"description": "Production theme architecture based on the serel theme: 20 sections, 30 child components, 35 sub-components, 8 hooks, 9 utils
|
|
395
|
-
"content": "## Real-World Theme Architecture (Serel Reference)\n\nThis guide describes the architecture of the **serel** production theme (project ID: `nb34u3yu`), a complete e-commerce storefront with 20 sections, 30 child components, 35 sub-components, 8 custom hooks, and 9 utility modules.\n\n### Three-Tier Component Hierarchy\n\nikas themes use a strict three-tier architecture:\n\n**Tier 1
|
|
394
|
+
"description": "Production theme architecture based on the serel theme: 20 sections, 30 child components, 35 sub-components, 8 hooks, 9 utils \u2014 three-tier component hierarchy",
|
|
395
|
+
"content": "## Real-World Theme Architecture (Serel Reference)\n\nThis guide describes the architecture of the **serel** production theme (project ID: `nb34u3yu`), a complete e-commerce storefront with 20 sections, 30 child components, 35 sub-components, 8 custom hooks, and 9 utility modules.\n\n### Three-Tier Component Hierarchy\n\nikas themes use a strict three-tier architecture:\n\n**Tier 1 \u2014 Sections (20):** Page-level containers registered in `ikas.config.json` with `type: \"section\"`. They own the layout, declare COMPONENT_LIST slots, and render via `<IkasComponentRenderer>`. Examples: Header, HeroSlider, ProductDetail, CartPage, Footer.\n\n**Tier 2 \u2014 Child Components (30):** Registered in `ikas.config.json` without `type` (default `\"component\"`). Placed inside sections via COMPONENT_LIST slots. The editor controls which children go in which section via `filteredComponentIds`. Examples: Navbar, HeroSliderItem, CardProductName, ProductDetailAddToCart.\n\n**Tier 3 \u2014 Sub-Components (35):** Internal helpers in `src/sub-components/`. NOT registered in config. Shared across multiple sections and child components. Examples: Button, Input, Modal, ProductCard, Pagination, Toast.\n\n### Section Inventory (20 sections)\n\n| Section | Purpose | Child Component Count |\n|---------|---------|----------------------|\n| Header | Global navigation | 3 (Navbar, Announcements, CookieBar) |\n| HeroSlider | Homepage banner carousel | 1 (HeroSliderItem) |\n| CategoryImages | Category image grid | 1 (CategoryImageItem) |\n| Footer | Global footer | 1 (SocialMediaIcon) |\n| ProductSlider | Product carousel | 3 (CardProductName, CardProductVariants, CardProductPrice) |\n| CategoryList | Product listing/filtering | 3 (same card components) |\n| ProductDetail | Product page | 12 child components in 2 slots |\n| Features | Feature icon grid | 1 (FeatureItem) |\n| AccountInfo | Customer dashboard | 5 (AccountInfoContent, AccountOrders, AccountAddresses, AccountFavorites, AccountOrderDetail) |\n| CartPage | Shopping cart | 0 (uses sub-components directly) |\n| Login, Register, ForgotPassword, RecoverPassword | Auth pages | 0 each |\n| BlogHome, BlogPost | Blog pages | 0 each |\n| RichText | Generic content block | 0 |\n| ProductDetailReviews | Product reviews | 0 |\n| CustomerEmailVerification | Email verification | 0 |\n| NotFound | 404 page | 0 |\n\n### Child Component Reuse via privateVarMap\n\nSeveral sections share the same card child components (CardProductName, CardProductVariants, CardProductPrice) using `privateVarMap` to expose a `product` variable of type `IkasProduct`. This pattern is used by ProductSlider, CategoryList, AccountFavorites, and Navbar.\n\n### Custom Hooks (8)\n\n- `useBundleProducts` \u2014 bundle product logic\n- `useColumnPreference` \u2014 grid column preference state\n- `useInfiniteScroll` \u2014 scroll-based pagination\n- `usePageTracking` \u2014 analytics page tracking\n- `usePayWithIkas` \u2014 ikas payment integration\n- `useRedirectIfLoggedIn` \u2014 auth redirect guard\n- `useScrollLock` \u2014 body scroll locking for modals\n- `useToast` \u2014 toast notification system\n\n### Utility Modules (9)\n\n`bundle.ts`, `cx.ts` (classname helper), `fullName.ts`, `media.ts`, `optionPrice.ts`, `optionSet.ts`, `orderStatus.ts`, `pagination.ts`, `toast.ts`\n\n### Prop Design Patterns\n\n1. **Content props**: `TEXT` for all user-visible text (headings, buttons, labels)\n2. **Style props**: `COLOR` for backgrounds and text colors. Every section has `backgroundColor`\n3. **Component slots**: `COMPONENT_LIST` for child component areas\n4. **Navigation props**: `LIST_OF_LINK` for editable nav menus\n5. **Data props**: `PRODUCT_LIST`, `BLOG_LIST` for dynamic data\n6. **Media props**: `IMAGE` for logos, banners, icons\n7. **Toggle props**: `BOOLEAN` for show/hide features\n\n### Responsive Design\n\nProduction themes target: Desktop 1200px+, Tablet 768-1199px, Mobile <768px.\n\n```css\n.section { width: 100%; padding: 64px 24px; }\n.section-inner { max-width: 1200px; margin: 0 auto; }\n@media (max-width: 768px) { .section { padding: 32px 16px; } }\n```",
|
|
396
396
|
"tags": [
|
|
397
397
|
"architecture",
|
|
398
398
|
"theme",
|
|
@@ -408,9 +408,9 @@
|
|
|
408
408
|
]
|
|
409
409
|
},
|
|
410
410
|
"prop-groups": {
|
|
411
|
-
"title": "Prop Groups
|
|
411
|
+
"title": "Prop Groups \u2014 Organizing Component Properties",
|
|
412
412
|
"description": "How to organize props into collapsible groups in the editor sidebar",
|
|
413
|
-
"content": "## Prop Groups\n\nProp groups organize a component's props into collapsible containers in the editor sidebar, making complex components easier to configure.\n\n### When to Use\n- Components with 5+ props\n- All section templates with multiple categories (content vs style vs data)\n- Any component where related props should be visually grouped\n\n### Config Format\n\nDefine `propGroups` on the component and assign props via `groupId`:\n\n```json\n{\n \"name\": \"HeroBanner\",\n \"type\": \"section\",\n \"props\": [\n { \"name\": \"heading\", \"type\": \"TEXT\", \"groupId\": \"content\", ... },\n { \"name\": \"subtitle\", \"type\": \"TEXT\", \"groupId\": \"content\", ... },\n { \"name\": \"bgColor\", \"type\": \"COLOR\", \"groupId\": \"style.colors\", ... },\n { \"name\": \"textColor\", \"type\": \"COLOR\", \"groupId\": \"style.colors\", ... },\n { \"name\": \"showBadge\", \"type\": \"BOOLEAN\", ... }\n ],\n \"propGroups\": [\n { \"id\": \"content\", \"name\": \"Content\", \"description\": \"Text and media\" },\n {\n \"id\": \"style\", \"name\": \"Style\",\n \"children\": [\n { \"id\": \"style.colors\", \"name\": \"Colors\" }\n ]\n }\n ]\n}\n```\n\n### Rules\n- `propGroups` and `groupId` are optional (backward-compatible)\n- Props without `groupId` appear ungrouped at root level\n- Groups can nest 1 level deep via `children`\n- Group IDs must be unique within a component (use kebab-case)\n\n### CLI Commands\n```bash\n# Create a group\nnpx ikas-component config add-prop-group --component \"Name\" --id content --name \"Content\"\n\n# Create a nested group\nnpx ikas-component config add-prop-group --component \"Name\" --id colors --name \"Colors\" --parent style\n\n# Add a prop to a group\nnpx ikas-component config add-prop --component \"Name\" --name title --displayName \"Title\" --type TEXT --group content\n\n# Move a prop to a different group\nnpx ikas-component config update-prop --component \"Name\" --prop title --group colors\n\n# Remove a group (props become ungrouped)\nnpx ikas-component config remove-prop-group --component \"Name\" --id content\n```\n\n### Common Patterns\n- Simple section: \"Content\" + \"Appearance\" groups\n- Complex section: \"Content\" + \"Style\"
|
|
413
|
+
"content": "## Prop Groups\n\nProp groups organize a component's props into collapsible containers in the editor sidebar, making complex components easier to configure.\n\n### When to Use\n- Components with 5+ props\n- All section templates with multiple categories (content vs style vs data)\n- Any component where related props should be visually grouped\n\n### Config Format\n\nDefine `propGroups` on the component and assign props via `groupId`:\n\n```json\n{\n \"name\": \"HeroBanner\",\n \"type\": \"section\",\n \"props\": [\n { \"name\": \"heading\", \"type\": \"TEXT\", \"groupId\": \"content\", ... },\n { \"name\": \"subtitle\", \"type\": \"TEXT\", \"groupId\": \"content\", ... },\n { \"name\": \"bgColor\", \"type\": \"COLOR\", \"groupId\": \"style.colors\", ... },\n { \"name\": \"textColor\", \"type\": \"COLOR\", \"groupId\": \"style.colors\", ... },\n { \"name\": \"showBadge\", \"type\": \"BOOLEAN\", ... }\n ],\n \"propGroups\": [\n { \"id\": \"content\", \"name\": \"Content\", \"description\": \"Text and media\" },\n {\n \"id\": \"style\", \"name\": \"Style\",\n \"children\": [\n { \"id\": \"style.colors\", \"name\": \"Colors\" }\n ]\n }\n ]\n}\n```\n\n### Rules\n- `propGroups` and `groupId` are optional (backward-compatible)\n- Props without `groupId` appear ungrouped at root level\n- Groups can nest 1 level deep via `children`\n- Group IDs must be unique within a component (use kebab-case)\n\n### CLI Commands\n```bash\n# Create a group\nnpx ikas-component config add-prop-group --component \"Name\" --id content --name \"Content\"\n\n# Create a nested group\nnpx ikas-component config add-prop-group --component \"Name\" --id colors --name \"Colors\" --parent style\n\n# Add a prop to a group\nnpx ikas-component config add-prop --component \"Name\" --name title --displayName \"Title\" --type TEXT --group content\n\n# Move a prop to a different group\nnpx ikas-component config update-prop --component \"Name\" --prop title --group colors\n\n# Remove a group (props become ungrouped)\nnpx ikas-component config remove-prop-group --component \"Name\" --id content\n```\n\n### Common Patterns\n- Simple section: \"Content\" + \"Appearance\" groups\n- Complex section: \"Content\" + \"Style\" \u2192 (\"Colors\", \"Typography\") nested groups\n- Data-heavy section: \"Content\" + \"Data\" + \"Appearance\" groups",
|
|
414
414
|
"tags": [
|
|
415
415
|
"props",
|
|
416
416
|
"groups",
|
|
@@ -422,9 +422,9 @@
|
|
|
422
422
|
]
|
|
423
423
|
},
|
|
424
424
|
"component-renderer-patterns": {
|
|
425
|
-
"title": "IkasComponentRenderer
|
|
425
|
+
"title": "IkasComponentRenderer \u2014 Component Slot Architecture",
|
|
426
426
|
"description": "How sections declare COMPONENT_LIST slots, filteredComponentIds to restrict children, parentProps for data passing, and multiple slot patterns from the serel theme",
|
|
427
|
-
"content": "## IkasComponentRenderer
|
|
427
|
+
"content": "## IkasComponentRenderer \u2014 Component Slot Architecture\n\n`IkasComponentRenderer` is the core mechanism for sections to render child components. It enables store owners to add, remove, and reorder child components within a section from the editor UI.\n\n### Basic Usage\n\nA section declares a `COMPONENT_LIST` prop, then renders it with `IkasComponentRenderer`:\n\n```tsx\nimport { IkasComponentRenderer } from \"@ikas/bp-storefront\";\nimport { Props } from \"./types\";\n\nexport function MySection({ components, backgroundColor, ...props }: Props) {\n return (\n <section style={backgroundColor ? { backgroundColor } : undefined}>\n <IkasComponentRenderer id=\"section-components\" components={components} parentProps={props} />\n </section>\n );\n}\nexport default MySection;\n```\n\n**Config:**\n```json\n{\n \"name\": \"components\",\n \"displayName\": \"Components\",\n \"type\": \"COMPONENT_LIST\"\n}\n```\n\n### filteredComponentIds \u2014 Restricting Allowed Children\n\nBy default, any child component can be placed in a COMPONENT_LIST slot. Use `filteredComponentIds` to restrict which components are allowed:\n\n```json\n{\n \"name\": \"components\",\n \"type\": \"COMPONENT_LIST\",\n \"filteredComponentIds\": [\n \"nb34u3yu-navbar\",\n \"nb34u3yu-announcements\",\n \"nb34u3yu-cookie-bar\"\n ]\n}\n```\n\nThis ensures only Navbar, Announcements, and CookieBar can be placed in the Header's component slot.\n\n### parentProps \u2014 Passing Data to Children\n\nAlways spread remaining props into `parentProps` so child components can access section-level data:\n\n```tsx\nexport function ProductSlider({ components, product, ...props }: Props) {\n return (\n <div>\n <IkasComponentRenderer id=\"product-slider-components\" components={components} parentProps={props} />\n </div>\n );\n}\n```\n\nChild components automatically receive data from `parentProps`. Combined with `privateVarMap`, this is how sections expose data (like a product object) to their children.\n\n### map \u2014 Runtime Data for privateVarMap\n\nWhen a section declares a `privateVarMap` in config, you must pass the corresponding values at runtime via the `map` prop. Keys in `map` must exactly match keys declared in `privateVarMap`:\n\n```tsx\n// Section config declares: privateVarMap.product \u2192 IkasProduct\nexport function ProductSlider({ products, components, ...props }: Props) {\n return (\n <IkasThemeSlider>\n {products.map((product) => (\n <IkasComponentRenderer\n id={`product-slider-product-${product.id}`}\n components={components}\n parentProps={props}\n map={{ product }}\n />\n ))}\n </IkasThemeSlider>\n );\n}\n```\n\nWithout `map`, children bound to a `privateVarMap` key receive `undefined`. See `get_framework_guide(\"private-var-map\")` for full config details.\n\n### Multiple COMPONENT_LIST Slots\n\nSections can have multiple slots for different layout regions. The serel ProductDetail section uses two:\n\n```json\n{\n \"props\": [\n {\n \"name\": \"components\",\n \"type\": \"COMPONENT_LIST\",\n \"filteredComponentIds\": [\"...12 product detail children...\"]\n },\n {\n \"name\": \"bottomComponents\",\n \"type\": \"COMPONENT_LIST\",\n \"filteredComponentIds\": [\"nb34u3yu-product-detail-description\"]\n }\n ]\n}\n```\n\n```tsx\nexport function ProductDetail({ product, components, bottomComponents, ...props }: Props) {\n return (\n <section>\n <div className=\"detail-side-panel\">\n <IkasComponentRenderer id=\"detail-components\" components={components} parentProps={props} />\n </div>\n <div className=\"detail-bottom\">\n <IkasComponentRenderer id=\"detail-bottom-components\" components={bottomComponents} parentProps={props} />\n </div>\n </section>\n );\n}\n```\n\n### Serel Examples of Section-to-Child Relationships\n\n| Section | COMPONENT_LIST children | Notes |\n|---------|------------------------|-------|\n| Header | Navbar, Announcements, CookieBar | `isHeader: true` |\n| HeroSlider | HeroSliderItem | Inside IkasThemeSlider |\n| CategoryImages | CategoryImageItem | Grid layout |\n| Footer | SocialMediaIcon | `isFooter: true` |\n| ProductSlider | CardProductName, CardProductVariants, CardProductPrice | Uses `privateVarMap` for product |\n| CategoryList | CardProductName, CardProductVariants, CardProductPrice | Uses `privateVarMap` for product |\n| ProductDetail | 12 children in 2 slots | Most complex section |\n| Features | FeatureItem | Simple repeating items |\n| AccountInfo | 5 account tab children | Dashboard tabs |\n\n### Key Rules\n\n1. Always pass `parentProps={props}` (spread remaining props after destructuring COMPONENT_LIST)\n2. If the section declares `privateVarMap`, pass the matching values via `map={{ key: value }}` \u2014 keys must match the `privateVarMap` keys\n3. Cast if needed: `components={components as any[]}`\n4. `filteredComponentIds` uses the full component ID (project prefix + kebab-case name)\n5. Multiple COMPONENT_LIST slots enable multi-region layouts\n6. Sections without COMPONENT_LIST (CartPage, Login, etc.) compose sub-components directly",
|
|
428
428
|
"tags": [
|
|
429
429
|
"IkasComponentRenderer",
|
|
430
430
|
"COMPONENT_LIST",
|
|
@@ -437,9 +437,9 @@
|
|
|
437
437
|
]
|
|
438
438
|
},
|
|
439
439
|
"private-var-map": {
|
|
440
|
-
"title": "privateVarMap
|
|
440
|
+
"title": "privateVarMap \u2014 Passing Data from Sections to Children",
|
|
441
441
|
"description": "How sections expose typed data to child components via privateVarMap config, enabling component reuse across multiple sections with different data sources",
|
|
442
|
-
"content": "## privateVarMap
|
|
442
|
+
"content": "## privateVarMap \u2014 Passing Data from Sections to Children\n\n`privateVarMap` is a config mechanism that lets a section expose a typed variable to its child components. This enables the same child component to be reused across multiple sections that provide the same data type.\n\n### The Problem It Solves\n\nIn the serel theme, `CardProductName`, `CardProductVariants`, and `CardProductPrice` are used in 4 different sections (ProductSlider, CategoryList, AccountFavorites, Navbar). Each section has a different product data source, but the card components need a `product` object to render. `privateVarMap` bridges this gap.\n\n### Config Syntax\n\nAdd `privateVarMap` to a `COMPONENT_LIST` prop definition:\n\n```json\n{\n \"name\": \"components\",\n \"type\": \"COMPONENT_LIST\",\n \"filteredComponentIds\": [\n \"nb34u3yu-card-product-name\",\n \"nb34u3yu-card-product-variants\",\n \"nb34u3yu-card-product-price\"\n ],\n \"privateVarMap\": {\n \"product\": {\n \"id\": \"nb34u3yu-product-slider-product\",\n \"typeId\": \"@ikas/bp-storefront-models-IkasProduct\"\n }\n }\n}\n```\n\n### How It Works\n\n1. The section declares a `privateVarMap` on its COMPONENT_LIST prop\n2. Each key in the map (e.g., `\"product\"`) is the variable name exposed to children\n3. `id` is a unique identifier for this variable binding (project prefix + section name + variable name)\n4. `typeId` references the model type using the format `@ikas/bp-storefront-models-TypeName`\n5. At runtime, the section passes the actual data to children via the `map` prop on `IkasComponentRenderer` (see Runtime Usage below)\n\n### Runtime Usage\n\nDeclaring `privateVarMap` in config is not enough \u2014 you must also pass the actual data at runtime via the `map` prop on `IkasComponentRenderer`. The keys in `map` must exactly match the keys declared in `privateVarMap`:\n\n```tsx\n// Config declares: privateVarMap.product \u2192 IkasProduct\n// Runtime passes the actual product object to each render:\n{products.map((product) => (\n <IkasComponentRenderer\n id={`product-slider-product-${product.id}`}\n components={components}\n parentProps={props}\n map={{ product }}\n />\n))}\n```\n\nWithout `map`, children bound to a `privateVarMap` key receive `undefined`.\n\n### Type Reference Format\n\nThe `typeId` field uses the pattern `@ikas/bp-storefront-models-TypeName`. Common types:\n\n| typeId | TypeScript Type |\n|--------|----------------|\n| `@ikas/bp-storefront-models-IkasProduct` | `IkasProduct` |\n| `@ikas/bp-storefront-models-IkasCategory` | `IkasCategory` |\n| `@ikas/bp-storefront-models-IkasBlog` | `IkasBlog` |\n| `@ikas/bp-storefront-models-IkasOrder` | `IkasOrder` |\n| `@ikas/bp-storefront-models-IkasCustomer` | `IkasCustomer` |\n\n### Real Serel Examples\n\n**ProductSlider** exposes `product` to card children:\n```json\n{\n \"name\": \"components\",\n \"type\": \"COMPONENT_LIST\",\n \"filteredComponentIds\": [\"nb34u3yu-card-product-name\", \"nb34u3yu-card-product-variants\", \"nb34u3yu-card-product-price\"],\n \"privateVarMap\": {\n \"product\": {\n \"id\": \"nb34u3yu-product-slider-product\",\n \"typeId\": \"@ikas/bp-storefront-models-IkasProduct\"\n }\n }\n}\n```\n\n**CategoryList** exposes the same `product` variable to the same card children:\n```json\n{\n \"name\": \"components\",\n \"type\": \"COMPONENT_LIST\",\n \"filteredComponentIds\": [\"nb34u3yu-card-product-name\", \"nb34u3yu-card-product-variants\", \"nb34u3yu-card-product-price\"],\n \"privateVarMap\": {\n \"product\": {\n \"id\": \"nb34u3yu-category-list-product\",\n \"typeId\": \"@ikas/bp-storefront-models-IkasProduct\"\n }\n }\n}\n```\n\nNotice: `id` is different per section, but `typeId` is the same. The child components (CardProductName, etc.) work identically in both contexts.\n\n### Sections Using privateVarMap in Serel\n\n| Section | Variable | Type | Child Components |\n|---------|----------|------|------------------|\n| ProductSlider | `product` | `IkasProduct` | CardProductName, CardProductVariants, CardProductPrice |\n| CategoryList | `product` | `IkasProduct` | CardProductName, CardProductVariants, CardProductPrice |\n| AccountFavorites | `product` | `IkasProduct` | CardProductName, CardProductVariants, CardProductPrice |\n| Navbar | `product` | `IkasProduct` | CardProductName, CardProductVariants, CardProductPrice |\n\n### Key Rules\n\n1. `privateVarMap` goes on the COMPONENT_LIST prop, not the section itself\n2. `id` must be unique per section-variable combination (use project prefix + section name + var name)\n3. `typeId` uses the `@ikas/bp-storefront-models-` prefix\n4. At runtime, pass the actual value via `map={{ <key>: value }}` on `IkasComponentRenderer` \u2014 keys must match `privateVarMap` keys. Without `map`, children receive `undefined`\n5. Each child component MUST declare a matching prop in its own `ikas.config.json` (e.g., `{ \"name\": \"product\", \"type\": \"PRODUCT\" }`) so the variable is properly typed in its `types.ts`\n6. The same child component works across different sections that expose the same typeId",
|
|
443
443
|
"tags": [
|
|
444
444
|
"privateVarMap",
|
|
445
445
|
"data-passing",
|
|
@@ -453,9 +453,9 @@
|
|
|
453
453
|
]
|
|
454
454
|
},
|
|
455
455
|
"page-composition": {
|
|
456
|
-
"title": "Page Composition
|
|
456
|
+
"title": "Page Composition \u2014 Which Sections Compose Each Page",
|
|
457
457
|
"description": "How the serel theme composes pages from sections: home, product detail, category, cart, account, blog, auth, and error pages",
|
|
458
|
-
"content": "## Page Composition
|
|
458
|
+
"content": "## Page Composition \u2014 Which Sections Compose Each Page\n\nEvery page in the serel theme follows the pattern: **Header \u2192 Content Section(s) \u2192 Footer**. The Header (`isHeader: true`) and Footer (`isFooter: true`) appear on all pages automatically.\n\n### Home Page (INDEX)\n\n```\nHeader (global)\nHeroSlider \u2014 full-width carousel with HeroSliderItem children\nCategoryImages \u2014 category image grid with CategoryImageItem children\nProductSlider \u2014 product carousel with card children (privateVarMap: product)\nFeatures \u2014 feature icon grid with FeatureItem children\nFooter (global)\n```\n\n### Product Detail Page (PRODUCT_DETAIL)\n\n```\nHeader (global)\nProductDetail \u2014 12 child components in 2 COMPONENT_LIST slots\n Gallery, name, SKU, prices, variants, add-to-cart,\n features, description, bundles, option sets, offers\nProductDetailReviews \u2014 customer review display and submission form\nFooter (global)\n```\n\n### Category / Product List Page (CATEGORY)\n\n```\nHeader (global)\nCategoryList \u2014 product grid with filtering, sorting, pagination\n Card children via privateVarMap: product\nFooter (global)\n```\n\n### Cart Page (CART)\n\n```\nHeader (global)\nCartPage \u2014 cart items, order summary, coupon codes\n Uses CartItem sub-component directly (no IkasComponentRenderer)\nFooter (global)\n```\n\n### Account Dashboard (ACCOUNT)\n\n```\nHeader (global)\nAccountInfo \u2014 5 child components for tab-based dashboard:\n AccountInfoContent, AccountOrders, AccountAddresses,\n AccountFavorites, AccountOrderDetail\nFooter (global)\n```\n\n### Blog Home (BLOG listing)\n\n```\nHeader (global)\nBlogHome \u2014 blog post list with pagination\nFooter (global)\n```\n\n### Blog Post Detail (BLOG post)\n\n```\nHeader (global)\nBlogPost \u2014 single blog post content\nFooter (global)\n```\n\n### Auth Pages\n\n```\nHeader (global)\nLogin / Register / ForgotPassword / RecoverPassword \u2014 single auth form section\nFooter (global)\n```\n\nEach auth page uses one of: Login, Register, ForgotPassword, or RecoverPassword sections.\n\n### Email Verification (CUSTOMER_EMAIL_VERIFICATION)\n\n```\nHeader (global)\nCustomerEmailVerification \u2014 verification status display\nFooter (global)\n```\n\n### 404 Not Found (NOT_FOUND)\n\n```\nHeader (global)\nNotFound \u2014 404 message and navigation back\nFooter (global)\n```\n\n### Section Count Summary\n\nThe 20 sections cover all essential page types:\n- **Global (2):** Header, Footer\n- **Home (4):** HeroSlider, CategoryImages, ProductSlider, Features\n- **Product (2):** ProductDetail, ProductDetailReviews\n- **Category (1):** CategoryList\n- **Cart (1):** CartPage\n- **Account (1):** AccountInfo (with 5 child tab components)\n- **Blog (2):** BlogHome, BlogPost\n- **Auth (4):** Login, Register, ForgotPassword, RecoverPassword\n- **Utility (3):** RichText, CustomerEmailVerification, NotFound\n\n### Design Principles\n\n1. **Header/Footer on every page** via `isHeader`/`isFooter` flags\n2. **One section per concern** \u2014 product detail, reviews, cart are separate sections\n3. **Complex pages use COMPONENT_LIST** \u2014 ProductDetail has 12 children, AccountInfo has 5\n4. **Simple pages use direct composition** \u2014 Login, CartPage compose sub-components without IkasComponentRenderer\n5. **Reusable content sections** \u2014 RichText can appear on any custom page",
|
|
459
459
|
"tags": [
|
|
460
460
|
"pages",
|
|
461
461
|
"composition",
|
|
@@ -472,9 +472,9 @@
|
|
|
472
472
|
]
|
|
473
473
|
},
|
|
474
474
|
"sub-component-catalog": {
|
|
475
|
-
"title": "Sub-Component Catalog
|
|
475
|
+
"title": "Sub-Component Catalog \u2014 35+ Reusable Internal Components",
|
|
476
476
|
"description": "Complete catalog of shared sub-components in the serel theme: UI primitives, layout, product, reviews, feedback, auth, media, and icons",
|
|
477
|
-
"content": "## Sub-Component Catalog
|
|
477
|
+
"content": "## Sub-Component Catalog \u2014 35+ Reusable Internal Components\n\nThe serel theme has 35+ sub-components in `src/sub-components/`. These are internal helpers NOT registered in `ikas.config.json`. They are imported by sections and child components via relative paths.\n\n### UI Primitives\n\n**Button** \u2014 Multi-variant button with sizes and states:\n- Variants: `primary`, `secondary`, `dangerous`\n- Sizes: `xs`, `s`, `m`\n- States: loading (with SpinnerIcon), disabled\n- Usage: `<Button variant=\"primary\" size=\"m\" loading={isSubmitting}>Submit</Button>`\n\n**Input** \u2014 Text input with label, error display, and optional icon. Handles Preact `onInput` events.\n\n**Select** \u2014 Dropdown select with label and error display.\n\n**Checkbox** \u2014 Styled checkbox with label text.\n\n**Toggle** \u2014 On/off toggle switch.\n\n**Textarea** \u2014 Multi-line text input with label and error.\n\n**Tag** \u2014 Small label/tag component for categories, filters, etc.\n\n**Badge** \u2014 Status badge for product labels (sale, new, etc.).\n\n**ColorInput** \u2014 Color swatch input for variant color selection.\n\n### Layout Components\n\n**Breadcrumb** \u2014 Category path navigation using `getProductCategoryPath()` / `getCategoryPath()`. Used in ProductDetail and CategoryList.\n\n**Pagination** \u2014 Page navigation with prev/next and page numbers. Used with the `pagination.ts` utility.\n\n**CollapsibleGroup** \u2014 Accordion-style expandable content sections. Used for product description, FAQ, filters.\n\n**PageLoader** \u2014 Full-page loading spinner for route transitions.\n\n**SkeletonField** \u2014 Placeholder loading skeleton for content that has not yet loaded.\n\n**SliderArrow** \u2014 Left/right navigation arrows for slider components. Used in HeroSlider and ProductSlider.\n\n### Product Components\n\n**ProductCard** \u2014 Product card with image, name, price, and variant info. Core building block for product grids.\n\n**CartItem** \u2014 Single cart line item with image, name, quantity controls, price, and remove button.\n\n**QuantitySelector** \u2014 Increment/decrement quantity input. Used in CartItem and ProductDetailAddToCart.\n\n**VariantBadge** \u2014 Visual badge for variant options (color swatches, size labels).\n\n**BundleMedia** \u2014 Media display for bundle products.\n\n**BundleQuantityBox** \u2014 Quantity selector for bundle product items.\n\n### Review Components\n\n**ReviewCard** \u2014 Single review display with star rating, title, comment, author, and date.\n\n**ReviewForm** \u2014 Review submission form with star rating input, title, and comment fields.\n\n**ReviewSummary** \u2014 Aggregate review statistics (average rating, rating distribution).\n\n**StarRating** \u2014 Star rating display (1-5 stars, filled/empty). Used in both ReviewCard and ReviewForm.\n\n### Feedback Components\n\n**Toast** \u2014 Portal-based notification system with stacking. Renders via `createPortal` to document.body. Supports multiple concurrent toasts with auto-dismiss. Used with the `useToast` hook and `toast.ts` utility.\n\n**ConfirmModal** \u2014 Confirmation dialog with cancel/confirm actions. Used for destructive operations (delete address, remove item).\n\n**SpinnerIcon** \u2014 Loading spinner SVG animation. Used inside Button loading state.\n\n### Auth Components\n\n**SocialLoginButton** \u2014 Social authentication buttons (Google, Facebook, etc.) with provider icons.\n\n### Media Components\n\n**ImagePreviewModal** \u2014 Full-screen image zoom/preview overlay. Used in product gallery for detailed image viewing.\n\n### Form Components\n\n**FormItem** \u2014 Form field wrapper with label, input slot, and error message display. Standardizes form layout across all forms.\n\n### Modal Component\n\n**Modal** \u2014 Generic modal overlay with backdrop, close button, scroll lock (uses `useScrollLock`), and portal rendering.\n\n### Icons Directory\n\nThe `src/sub-components/icons/` directory contains 35+ SVG icon components exported as Preact functional components. Examples: ChevronLeft, ChevronRight, Close, Search, Cart, Heart, User, Plus, Minus, Trash, etc.",
|
|
478
478
|
"tags": [
|
|
479
479
|
"sub-components",
|
|
480
480
|
"catalog",
|
|
@@ -495,7 +495,7 @@
|
|
|
495
495
|
"config-advanced-features": {
|
|
496
496
|
"title": "Advanced ikas.config.json Features",
|
|
497
497
|
"description": "filteredComponentIds, privateVarMap, isHeader/isFooter, propGroups, custom enum types, typeId/enumTypeId for advanced config patterns",
|
|
498
|
-
"content": "## Advanced ikas.config.json Features\n\nBeyond basic component and prop definitions, `ikas.config.json` supports advanced features for complex theme architectures.\n\n### filteredComponentIds
|
|
498
|
+
"content": "## Advanced ikas.config.json Features\n\nBeyond basic component and prop definitions, `ikas.config.json` supports advanced features for complex theme architectures.\n\n### filteredComponentIds \u2014 Restricting Component Slots\n\n`filteredComponentIds` on a `COMPONENT_LIST` prop limits which child components can be placed in that slot. Without it, any component can be added.\n\n```json\n{\n \"name\": \"components\",\n \"type\": \"COMPONENT_LIST\",\n \"filteredComponentIds\": [\n \"nb34u3yu-navbar\",\n \"nb34u3yu-announcements\",\n \"nb34u3yu-cookie-bar\"\n ]\n}\n```\n\nThe IDs use the full component ID format: `{projectId}-{kebab-case-name}`. Check your component IDs with `npx ikas-component config list`.\n\n### privateVarMap \u2014 Section-to-Child Data Passing\n\nExposes a typed variable from a section to its child components. At runtime, the section must pass the actual data via the `map` prop on `IkasComponentRenderer`.\n\n```json\n{\n \"name\": \"components\",\n \"type\": \"COMPONENT_LIST\",\n \"filteredComponentIds\": [\"nb34u3yu-card-product-price\"],\n \"privateVarMap\": {\n \"product\": {\n \"id\": \"nb34u3yu-product-slider-product\",\n \"typeId\": \"@ikas/bp-storefront-models-IkasProduct\"\n }\n }\n}\n```\n\n- `product` = variable name children receive\n- `id` = unique identifier (project-section-variable format)\n- `typeId` = model type reference (`@ikas/bp-storefront-models-TypeName`)\n\nSee `get_framework_guide(\"private-var-map\")` for full details.\n\n### isHeader / isFooter \u2014 Global Sections\n\nMark a section to appear on every page:\n\n```json\n{\n \"id\": \"header\",\n \"name\": \"Header\",\n \"type\": \"section\",\n \"isHeader\": true,\n \"props\": [...]\n}\n```\n\nRules:\n- Only one component can have `isHeader: true`\n- Only one component can have `isFooter: true`\n- Only applies to `type: \"section\"` components\n- The editor automatically adds these to all pages\n\nCLI:\n```bash\nnpx ikas-component config add-component --name \"Header\" --type section --isHeader --props '[...]'\nnpx ikas-component config add-component --name \"Footer\" --type section --isFooter --props '[...]'\n```\n\n### propGroups \u2014 Organizing Props\n\nGroup props into collapsible sections in the editor sidebar:\n\n```json\n{\n \"propGroups\": [\n { \"id\": \"content\", \"name\": \"Content\" },\n {\n \"id\": \"style\", \"name\": \"Style\",\n \"children\": [\n { \"id\": \"style.colors\", \"name\": \"Colors\" }\n ]\n }\n ],\n \"props\": [\n { \"name\": \"title\", \"type\": \"TEXT\", \"groupId\": \"content\" },\n { \"name\": \"bgColor\", \"type\": \"COLOR\", \"groupId\": \"style.colors\" }\n ]\n}\n```\n\nCLI:\n```bash\nnpx ikas-component config add-prop-group --component \"MySection\" --id content --name \"Content\"\nnpx ikas-component config add-prop-group --component \"MySection\" --id colors --name \"Colors\" --parent style\nnpx ikas-component config update-prop --component \"MySection\" --prop title --group content\n```\n\nSee `get_framework_guide(\"prop-groups\")` for full details.\n\n### TYPE Props \u2014 Structured Style Types\n\nThe `TYPE` prop type uses `typeId` to reference structured style types (padding, margin, border-radius, etc.):\n\n```json\n{\n \"name\": \"spacing\",\n \"type\": \"TYPE\",\n \"typeId\": \"@ikas/bp-storefront-models-PaddingStyleType\"\n}\n```\n\nAppend `_array` for array types: `\"@ikas/bp-storefront-models-MarginStyleType_array\"`\n\nDiscover available types:\n```bash\nnpx ikas-component config list-types # all types\nnpx ikas-component config list-types --component-type section # section-allowed types only\n```\n\n### ENUM Props \u2014 Enum Style Types\n\nThe `ENUM` prop type uses `enumTypeId` for dropdown selectors:\n\n```json\n{\n \"name\": \"direction\",\n \"type\": \"ENUM\",\n \"enumTypeId\": \"@ikas/bp-storefront-models-FlexDirectionStyleType\"\n}\n```\n\nCommon enum types: FlexDirectionStyleType, JustifyContentStyleType, AlignItemsStyleType, TextAlignStyleType, ObjectFitStyleType.\n\nCLI:\n```bash\nnpx ikas-component config add-prop --component MyComp --name direction --displayName Direction --type ENUM --enumTypeId \"@ikas/bp-storefront-models-FlexDirectionStyleType\"\n```\n\n### CLI Config Commands Reference\n\n| Command | Purpose |\n|---------|--------|\n| `config add-component --name X --type section --props '[...]'` | Create component with props |\n| `config add-prop --component X --name y --type TYPE --typeId Z` | Add structured type prop |\n| `config add-prop --component X --name y --type ENUM --enumTypeId Z` | Add enum prop |\n| `config add-prop-group --component X --id g --name \"Group\"` | Create prop group |\n| `config update-prop --component X --prop y --group g` | Assign prop to group |\n| `config list-types` | List available TYPE and ENUM types |\n| `config list` | List all components and props |",
|
|
499
499
|
"tags": [
|
|
500
500
|
"config",
|
|
501
501
|
"filteredComponentIds",
|
|
@@ -514,7 +514,7 @@
|
|
|
514
514
|
"custom-enums": {
|
|
515
515
|
"title": "Custom Enum Types",
|
|
516
516
|
"description": "Create and manage custom enum types for ENUM props without needing an editor connection",
|
|
517
|
-
"content": "## Custom Enum Types\n\nCustom enums let you define project-specific dropdown options for ENUM props. Unlike built-in enums (FlexDirectionStyleType, etc.), custom enums are stored in `ikas.config.json` and work offline
|
|
517
|
+
"content": "## Custom Enum Types\n\nCustom enums let you define project-specific dropdown options for ENUM props. Unlike built-in enums (FlexDirectionStyleType, etc.), custom enums are stored in `ikas.config.json` and work offline \u2014 no editor connection required.\n\n### Creating a Custom Enum\n\n```bash\nnpx ikas-component config add-enum --name \"Size\" --options '{\"Small\":\"sm\",\"Medium\":\"md\",\"Large\":\"lg\"}'\n```\n\nOutput:\n```json\n{\"success\":true,\"enumId\":\"aBcDeFgHiJ\",\"enumName\":\"Size\",\"enumOptions\":{\"Small\":\"sm\",\"Medium\":\"md\",\"Large\":\"lg\"}}\n```\n\nThe `enumId` is a generated 10-character ID. Use it when adding ENUM props.\n\n### Using a Custom Enum with a Prop\n\n```bash\nnpx ikas-component config add-prop --component MyComp --name size --displayName \"Size\" --type ENUM --enumTypeId aBcDeFgHiJ\n```\n\nThis generates in `types.ts`:\n```typescript\nexport type Size = \"sm\" | \"md\" | \"lg\";\nexport interface MyCompProps { size?: Size; }\n```\n\n### Listing Custom Enums\n\n```bash\nnpx ikas-component config list-enums\n```\n\n### Updating a Custom Enum\n\n```bash\nnpx ikas-component config update-enum --id aBcDeFgHiJ --name \"ProductSize\" --options '{\"XS\":\"xs\",\"S\":\"s\",\"M\":\"m\",\"L\":\"l\",\"XL\":\"xl\"}'\n```\n\nBoth `--name` and `--options` are optional \u2014 provide only what you want to change.\n\n### Removing a Custom Enum\n\n```bash\nnpx ikas-component config remove-enum --id aBcDeFgHiJ\n```\n\nThis fails if any component prop references the enum. Remove or update the prop first.\n\n### Workflow: Complete Example\n\n```bash\n# 1. Create the enum\nnpx ikas-component config add-enum --name \"AspectRatio\" --options '{\"Square\":\"1/1\",\"Landscape\":\"16/9\",\"Portrait\":\"3/4\"}'\n# Returns: {\"enumId\":\"xYz123AbCd\"}\n\n# 2. Use it in a prop\nnpx ikas-component config add-prop --component ImageGrid --name aspectRatio --displayName \"Aspect Ratio\" --type ENUM --enumTypeId xYz123AbCd\n\n# 3. The generated types.ts will have:\n# export type AspectRatio = \"1/1\" | \"16/9\" | \"3/4\";\n# export interface ImageGridProps { aspectRatio?: AspectRatio; }\n```\n\n### Storage\n\nCustom enums are stored in `ikas.config.json` under `customTypes`:\n```json\n{\n \"customTypes\": [\n {\n \"id\": \"aBcDeFgHiJ\",\n \"name\": \"Size\",\n \"type\": \"enum\",\n \"enumOptions\": {\"Small\":\"sm\",\"Medium\":\"md\",\"Large\":\"lg\"},\n \"isDynamic\": false\n }\n ]\n}\n```\n\n### Built-in vs Custom Enums\n\n- **Built-in enums** (e.g. `@ikas/bp-storefront-models-FlexDirectionStyleType`) come from the editor and require `list-types` with a connected editor.\n- **Custom enums** are defined in `ikas.config.json` and work offline. Use `config add-enum` to create them.\n\nFor automated/AI-driven component generation, custom enums are the recommended approach since they don't require an editor connection.",
|
|
518
518
|
"tags": [
|
|
519
519
|
"enum",
|
|
520
520
|
"custom-enum",
|
|
@@ -530,4 +530,4 @@
|
|
|
530
530
|
]
|
|
531
531
|
}
|
|
532
532
|
}
|
|
533
|
-
}
|
|
533
|
+
}
|