@ikas/code-components-mcp 1.4.0-beta.143 → 1.4.0-beta.145
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
CHANGED
|
@@ -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. Authored `defaultValue` must be a finite number — an empty string, a non-numeric string (e.g. `12px`), NaN or Infinity are rejected by the CLI. |\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| `SVG` | SVG upload | `string` | Raw inline SVG markup (the `<svg>…</svg>` source). Normalize with `normalizeSvg(value, { idPrefix })` before rendering via `dangerouslySetInnerHTML`. NOT served through the image CDN — this is the way to ship logos/icons as vectors. |\n| `SVG_LIST` | SVG list upload | `string[]` | List of raw inline SVG markup strings. Normalize each with `normalizeSvg(svg, { idPrefix })` (unique per item) before rendering with `dangerouslySetInnerHTML`. |\n| `DATE` | Date picker | `Date | string` | Date value |\n| `LINK` | Link editor | `IkasNavigationLink | null` | Navigation link. Read at runtime as `.href`/`.label`/`.subLinks`; authored `defaultValue` is a typed object (`linkType`+`externalLink`/`pageType`+`subLinks`). See the LINK section below. |\n| `LIST_OF_LINK` | Link list editor | `IkasNavigationLinkList` | List of navigation links. Authored `defaultValue` is `{ \"links\": [ <link>, … ] }`. See the LINK section below. |\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 | `IkasProductAttributeDetail | null` | Wrapper `{ product, attributePropValue }` — NOT the value itself; read it with `getAttributeDetailValues(prop)` |\n| `PRODUCT_ATTRIBUTE_LIST` | Product attribute list picker | `IkasProductAttributeList` | Wrapper `{ product, attributeListPropValue }` — a single object, NOT an array; read it with `getAttributeListValues(prop)` |\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### Dynamic props have NO default value\n\nMerchant-data reference types resolve from the store's live data at render time, so they cannot carry a static `defaultValue`: IMAGE, IMAGE_LIST, VIDEO, PRODUCT, PRODUCT_LIST, PRODUCT_ATTRIBUTE, PRODUCT_ATTRIBUTE_LIST, BRAND, BRAND_LIST, CATEGORY, CATEGORY_LIST, BLOG, BLOG_LIST, BLOG_CATEGORY, BLOG_CATEGORY_LIST, RAFFLE, RAFFLE_LIST. The CLI (`add-prop`/`update-prop`) rejects a `defaultValue` on these with an error, and the editor strips any that slips through a hand-edited config. Omit `defaultValue` entirely for these props. (Scalar, LINK, COMPONENT, TYPE and SVG props DO support defaults.)\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### SVG and SVG_LIST props (inline vector graphics)\n\nUse `SVG` / `SVG_LIST` when you need a **vector** asset (logo, icon, decorative shape) that must scale crisply and be styleable with CSS — things the raster image CDN cannot do. The editor uploads the file and stores the **raw `<svg>…</svg>` markup as a string**; it is NOT uploaded to the image CDN, so there is no URL and no `IkasImage` wrapper. Uploads are sanitized by the editor at upload time — XSS vectors (`<script>`, `on*` handlers, `javascript:` hrefs, `<foreignObject>`) are stripped — in addition to the existing 64 KB cap and rejection of SVGs containing embedded base64 raster images.\n\n- `SVG` → `string` (one SVG's markup)\n- `SVG_LIST` → `string[]` (multiple SVGs; the editor allows selecting several files at once)\n\n**Rendering:** inject the markup directly. Because it is the same `<svg>` source the user uploaded, you can target it with CSS (e.g. `.icon svg { width: 24px; fill: currentColor; }`).\n\n**Normalize before injecting:** call `normalizeSvg(value, { idPrefix })` from `@ikas/bp-storefront` before setting `dangerouslySetInnerHTML`, so internal ids/`clipPath`/`url(#…)` references get scoped per instance and don't collide when the same SVG (or multiple `SVG_LIST` items) render more than once on a page. For `SVG_LIST`, give each item a deterministic per-item `idPrefix`, e.g. `` `${propKey}-${i}` ``. By default `normalizeSvg` also strips fixed `width`/`height` and ensures a `viewBox` (so CSS controls size); pass `{ color: \"currentColor\" }` to recolor single-color icons via CSS `color` (omit it for multi-color SVGs). This is opt-in for code-components — the theme's built-in SVG element normalizes automatically, but your own `dangerouslySetInnerHTML` injections must call it explicitly.\n```tsx\nimport { normalizeSvg } from \"@ikas/bp-storefront\";\nimport { Props } from \"./types\";\n\nexport function Logo({ logo, partnerLogos }: Props) {\n return (\n <div className=\"logos\">\n {logo && (\n <span className=\"icon\" dangerouslySetInnerHTML={{ __html: normalizeSvg(logo, { idPrefix: \"logo\" }) }} />\n )}\n {(partnerLogos ?? []).map((svg, i) => (\n <span key={i} className=\"icon\" dangerouslySetInnerHTML={{ __html: normalizeSvg(svg, { idPrefix: `partner-logo-${i}` }) }} />\n ))}\n </div>\n );\n}\nexport default Logo;\n```\n\n**Config example (SVG):**\n```json\n{\n \"name\": \"logo\",\n \"displayName\": \"Logo\",\n \"type\": \"SVG\"\n}\n```\n\n**Config example (SVG_LIST):**\n```json\n{\n \"name\": \"partnerLogos\",\n \"displayName\": \"Partner Logos\",\n \"type\": \"SVG_LIST\"\n}\n```\n\n**CLI command:**\n```bash\nnpx ikas-component config add-prop --component MyComp --name logo --displayName Logo --type SVG\n```\n\n**When NOT to use:** for photos/raster artwork or anything that should be optimized/resized by the CDN, use `IMAGE` / `IMAGE_LIST` instead. SVG is only for hand-authored vector markup.\n\n**Providing a default value:** A default value is a raw <svg>\u2026</svg> string (SVG) or an array of them (SVG_LIST). It must be a single well-formed <svg> root, \u2264 64 KB, with no embedded base64 raster images; it is sanitized on load. Invalid defaults are rejected by the CLI (add-prop / add-component hard-fail with the reason) and silently dropped by the editor when the component is placed, so verify your default is valid. It must also RENDER VISIBLY: an open path with no enclosed area (e.g. the single horizontal line M4 12h16) is invisible under the default fill:black; stroke:none. Give the shape real area and set a color - prefer fill=currentColor (or an explicit stroke with stroke-width) so the icon inherits the theme text color. Example: \"defaultValue\": \"<svg viewBox=\\\"0 0 24 24\\\"><path fill=\\\"currentColor\\\" d=\\\"M12 2 2 22h20z\\\"/></svg>\".\n\n### SVG normalization & sanitization -- the theme-developer standard\n\n**Responsibility split (read this first):**\n- **Security/sanitization is the editor's job, not yours.** Every SVG is sanitized (DOMPurify) on upload, on programmatic writes, AND at render -- you never sanitize in component code.\n- **Normalization (size / color / id-scoping) is `normalizeSvg`'s job.** The theme's built-in SVG element applies it automatically; in your own code component you MUST call `normalizeSvg(value, { idPrefix })` before `dangerouslySetInnerHTML`. There is ONE shared util -- do not reinvent per theme: `import { normalizeSvg } from \"@ikas/bp-storefront\"`.\n\n`normalizeSvg(svg, options)` options: `{ idPrefix: string; scopeIds?: boolean = true; color?: \"currentColor\"; size?: boolean = true }`.\n\n**1. Size.** `size: true` (default) synthesizes a `viewBox` from numeric (unitless/px) `width`/`height` when missing, then strips `width`/`height` so CSS controls size. If there is no viewBox AND size cannot be synthesized (e.g. `%`/`em` dims), width/height are kept (so the icon is not left with zero intrinsic size). Merchants are NOT required to upload viewBox'd or size-less SVGs -- normalize handles it. Size icons with CSS: `.icon svg { width: 24px; height: 24px; }`.\n\n**2. Color.** Opt-in `{ color: \"currentColor\" }` rewrites `fill`/`stroke` (attributes AND `style:` declarations) to `currentColor`, but PRESERVES `none` and `url(...)` (gradients/patterns). So outline icons (`fill=\"none\" stroke=\"...\"`) keep `fill:none` and get `stroke:currentColor` -- no special-casing needed. For MULTI-COLOR SVGs, OMIT `color` to keep the original colors. Rule of thumb: single-color icons -> pass `currentColor`; multi-color -> omit.\n\n**3. Collision (id / clipPath / url(#...)).** Solved in code, not by the editor. `scopeIds: true` (default) prefixes every declared `id` and every `url(#...)` / `href=\"#...\"` reference with `idPrefix`, so the same SVG rendered multiple times (or multiple SVG_LIST items) cannot collide. Give each render an instance-unique prefix -- the built-in element uses its element id; for SVG_LIST use a per-item prefix like `` `${propKey}-${i}` ``.\n\n**4. Sanitization -- what the editor strips.** Beyond the 64 KB cap and base64-raster rejection, the editor strips `<script>`, `on*` event handlers (onload/onclick/onerror/...), `javascript:` hrefs, and `<foreignObject>`. This runs on file upload, on programmatic `update_section_prop` writes, and again at render -- so stored and rendered markup is always safe. `normalizeSvg` is NOT a sanitizer (pure string transforms); security lives in the separate sanitize/validate layer and is automatic.\n\n**5. One shared pattern.** Yes -- `normalizeSvg` from `@ikas/bp-storefront` is the single, isomorphic (SSR/browser/node) util. Do not hand-roll size/color/id-scoping per theme.\n\n**Naming.** The editor shows each SVG's `class` attribute as its display name in the list; SVG / SVG_LIST items with no class render as `svg-1`, `svg-2`, .... Set `class=\"my-icon\"` in the markup to name it.\n\n### NUMBER_RANGE prop (bounded numeric slider)\n\nA `NUMBER_RANGE` prop renders a **slider + number input** and gives the store owner one bounded number with an optional unit. Configure the slider with a **required** `numberRangeData` object on the prop (`min` and `max` are mandatory; `interval` and `unit` are optional). A NUMBER_RANGE prop authored without it is rejected:\n\n- `min` (integer) — lower bound\n- `max` (integer) — upper bound, must be greater than `min`\n- `interval` (positive integer, optional) — the step increment; typed values snap to `min + k·interval`, and a `0`/negative interval is dropped (step falls back to 1)\n- `unit` (string, optional) — suffix shown next to the number input (e.g. `\"px\"`, `\"%\"`)\n\n**Runtime read shape** is `IkasNumberRange`: read `props.myRange.value` (a number) and `props.myRange.unit` (string or null) — e.g. `props.gap.value + (props.gap.unit ?? \"px\")`.\n\n**Authored `defaultValue`** is a typed object `{ \"value\": <number>, \"unit\": \"px\" | null }` — NOT a bare number and NOT a JSON string. When the prop defines `numberRangeData`, the CLI validates that `value` is within `[min, max]` and lands on the interval grid (`min + k·interval`); off-range or off-step defaults are rejected.\n\n**Config example:**\n```json\n{ \"name\": \"gap\", \"displayName\": \"Gap\", \"type\": \"NUMBER_RANGE\", \"numberRangeData\": { \"min\": 0, \"max\": 100, \"interval\": 5, \"unit\": \"px\" }, \"defaultValue\": { \"value\": 20, \"unit\": \"px\" } }\n```\n\n**CLI command:**\n```bash\nnpx ikas-component config add-prop --component MyComp --name gap --displayName Gap --type NUMBER_RANGE --numberRangeData '{\"min\":0,\"max\":100,\"interval\":5,\"unit\":\"px\"}' --defaultValue '{\"value\":20,\"unit\":\"px\"}'\n```\n\n**Update the config or default later** (send a new `--numberRangeData` to change it; `--numberRangeData none` is rejected while the prop stays NUMBER_RANGE, since min/max are required):\n```bash\nnpx ikas-component config update-prop --component MyComp --prop gap --numberRangeData '{\"min\":0,\"max\":200,\"interval\":10,\"unit\":\"px\"}'\n```\n\n### PRODUCT_ATTRIBUTE and PRODUCT_ATTRIBUTE_LIST props (product custom fields)\n\n**CRITICAL — these props do NOT hand you attribute values.** What your component receives is a *wrapper* pairing the picker selection with the product it resolves against. Reading `prop.value` (or mapping over the prop) references fields that do not exist at runtime: `check` and `build` stay clean, every field is `undefined`, and the component silently renders nothing.\n\nThree distinct shapes are involved — do not confuse them:\n\n| Where | Shape |\n|-------|-------|\n| Value stored by the editor (visible via `get_section_values`) | `{ \"attributeId\": \"…\", \"usePageData\": true }` — the list variant uses `attributeIds: [...]` |\n| Runtime prop your component receives | `IkasProductAttributeDetail` = `{ product, attributePropValue }` — the list variant is `IkasProductAttributeList` = `{ product, attributeListPropValue }` |\n| Resolved attribute values | Reachable ONLY through the helpers below: `getAttributeDetailValues()` → `IkasProductAttributeValue[]`, `getAttributeListValues()` → `IkasProductAttributeMap[]` (each entry is `{ attribute, values }`) |\n\n**Read them with the helpers from `@ikas/bp-storefront`:**\n```tsx\nimport { getAttributeDetailValues, getAttributeListValues } from \"@ikas/bp-storefront\";\n\n// PRODUCT_ATTRIBUTE → attribute: IkasProductAttributeDetail | null\nif (!attribute?.attributePropValue?.attributeId) return null;\nconst html = getAttributeDetailValues(attribute)\n .map((v) => v.value)\n .filter(Boolean)\n .join(\"\");\n\n// PRODUCT_ATTRIBUTE_LIST → attributeList: IkasProductAttributeList\n// getAttributeListValues returns IkasProductAttributeMap[] — grouped per attribute, NOT a flat value list\nconst groups = getAttributeListValues(attributeList);\ngroups.forEach((g) => console.log(g.attribute.name, g.values));\n```\n\n`PRODUCT_ATTRIBUTE_LIST` is a **single object, not an array** — `attributeList.map(...)` throws at runtime; the selected ids live at `attributeList.attributeListPropValue.attributeIds`. Both prop types are merchant-data types, so neither can carry a `defaultValue`. Details: `get_function_doc(\"getAttributeDetailValues\")`, `get_type_definition(\"IkasProductAttributeDetail\")`, `get_type_definition(\"IkasProductAttributeList\")`.\n\n### LINK and LIST_OF_LINK props (navigation links)\n\n**CRITICAL — the authored `defaultValue` shape is NOT the runtime read shape.** In component code you READ a link as `props.myLink.href` / `.label` / `.subLinks`. But the `defaultValue` you author in `ikas.config.json` MUST be the typed object shown below. Do NOT pass `{ \"href\": \"…\", \"label\": \"…\" }` (legacy shape) and do NOT pass the value as a JSON string — the CLI rejects both.\n\n**LINK** `defaultValue` is a single link object:\n```json\n{ \"linkType\": \"EXTERNAL\", \"label\": \"Shop now\", \"externalLink\": \"https://example.com\", \"subLinks\": [] }\n```\nor, to link to a store page:\n```json\n{ \"linkType\": \"PAGE\", \"label\": \"Home\", \"pageType\": \"INDEX\", \"subLinks\": [] }\n```\n\n**LIST_OF_LINK** `defaultValue` wraps an array of link objects in `{ \"links\": [...] }`:\n```json\n{ \"links\": [\n { \"linkType\": \"PAGE\", \"label\": \"Home\", \"pageType\": \"INDEX\", \"subLinks\": [] },\n { \"linkType\": \"EXTERNAL\", \"label\": \"Blog\", \"externalLink\": \"https://example.com/blog\", \"subLinks\": [] }\n] }\n```\n\n**Rules for every link object:**\n- `linkType` is REQUIRED and must be one of `PAGE` or `EXTERNAL`. `FILE` links are NOT authorable in a code-component default (they reference a store-specific uploaded file) — the CLI rejects them.\n- Each link MUST carry a destination for its `linkType` — the CLI rejects a link that points nowhere. `EXTERNAL` → a non-empty `externalLink` URL. `PAGE` → a `pageType` (e.g. `INDEX`); do NOT invent a `pageId` — it is per-store and filled automatically when the theme is installed, so author only `pageType`.\n- `subLinks` must be an array (use `[]` when empty); each entry is itself a link object with the same shape (for dropdown/nested menus).\n- NEVER use a JSON string, and NEVER use the legacy `{ label, href }` shape.\n\n**Config example (LINK):**\n```json\n{ \"name\": \"ctaLink\", \"displayName\": \"CTA Link\", \"type\": \"LINK\", \"defaultValue\": { \"linkType\": \"EXTERNAL\", \"label\": \"Shop now\", \"externalLink\": \"https://example.com\", \"subLinks\": [] } }\n```\n\n**Config example (LIST_OF_LINK):**\n```json\n{ \"name\": \"menuLinks\", \"displayName\": \"Menu Links\", \"type\": \"LIST_OF_LINK\", \"defaultValue\": { \"links\": [ { \"linkType\": \"PAGE\", \"label\": \"Home\", \"pageType\": \"INDEX\", \"subLinks\": [] } ] } }\n```\n\n**CLI command (LIST_OF_LINK):**\n```bash\nnpx ikas-component config add-prop --component MyComp --name menuLinks --displayName \"Menu Links\" --type LIST_OF_LINK --defaultValue '{\"links\":[{\"linkType\":\"PAGE\",\"label\":\"Home\",\"pageType\":\"INDEX\",\"subLinks\":[]}]}'\n```\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**Providing a default value:** Style TYPE props accept a `defaultValue` object — size/style types (Padding, Margin, Width, Height, Gap, BorderRadius, Size, …) take `{ \"value\": <number>, \"unit\": \"px\" | \"rem\" | \"%\" | \"vh\" | \"vw\" }` (or `{ \"css\": \"<raw css>\" }`). Add it to the prop object, e.g. `\"defaultValue\": { \"value\": 16, \"unit\": \"px\" }`. For an `_array` typeId the default is an array of these objects; the `--defaultValue` flag also accepts this JSON as a string. Only `css`, `value` and `unit` keys are allowed on a size/style default object — any other field is rejected.\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.\n3. **Font Style enum** (sentinel `__patternElementEnum__`): lets store owners pick one of the theme's typography tokens. Built-in — no `add-enum` and no editor connection needed. See \"Font Style enum (typography picker)\" below.\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.\n\n### Font Style enum (typography picker)\nUse the built-in sentinel `enumTypeId: \"__patternElementEnum__\"` to let store owners pick one of the theme's typography tokens (a \"Font Style\" prop). It works exactly like a built-in enum: no `config add-enum` call and no editor connection required.\n\n**Example config:**\n```json\n{\n \"name\": \"fontStyle\",\n \"displayName\": \"Font Style\",\n \"type\": \"ENUM\",\n \"enumTypeId\": \"__patternElementEnum__\",\n \"required\": false\n}\n```\nThis generates `fontStyle?: string` in types.ts.\n\n**CLI command:**\n```bash\nnpx ikas-component config add-prop --component MyComp --name fontStyle --displayName \"Font Style\" --type ENUM --enumTypeId __patternElementEnum__\n```\n\n**Runtime:** the prop value is the chosen typography's **id** (a plain string), not the typography object itself. Resolve it with `getThemeTypography()` (see `get_framework_guide(\"theme-globals\")`) and apply its `className`:\n```tsx\nimport { getThemeTypography } from \"@ikas/bp-storefront\";\nimport type { Props } from \"./types\"; // fontStyle?: string\n\nexport function MyCard({ fontStyle }: Props) {\n const t = getThemeTypography().find((t) => t.id === fontStyle);\n return <p className={t?.className}>...</p>; // _<id> class applies the theme's typography CSS\n}\n```",
|
|
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. Authored `defaultValue` must be a finite number — an empty string, a non-numeric string (e.g. `12px`), NaN or Infinity are rejected by the CLI. |\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| `SVG` | SVG upload | `string` | Raw inline SVG markup (the `<svg>…</svg>` source). Normalize with `normalizeSvg(value, { idPrefix })` before rendering via `dangerouslySetInnerHTML`. NOT served through the image CDN — this is the way to ship logos/icons as vectors. |\n| `SVG_LIST` | SVG list upload | `string[]` | List of raw inline SVG markup strings. Normalize each with `normalizeSvg(svg, { idPrefix })` (unique per item) before rendering with `dangerouslySetInnerHTML`. |\n| `DATE` | Date picker | `Date | string` | Date value |\n| `LINK` | Link editor | `IkasNavigationLink | null` | Navigation link. Read at runtime as `.href`/`.label`/`.subLinks`; authored `defaultValue` is a typed object (`linkType`+`externalLink`/`pageType`+`subLinks`). See the LINK section below. |\n| `LIST_OF_LINK` | Link list editor | `IkasNavigationLinkList` | List of navigation links. Authored `defaultValue` is `{ \"links\": [ <link>, … ] }`. See the LINK section below. |\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 | `IkasProductAttributeDetail | null` | Wrapper `{ product, attributePropValue }` — NOT the value itself; read it with `getAttributeDetailValues(prop)` |\n| `PRODUCT_ATTRIBUTE_LIST` | Product attribute list picker | `IkasProductAttributeList` | Wrapper `{ product, attributeListPropValue }` — a single object, NOT an array; read it with `getAttributeListValues(prop)` |\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### Dynamic props have NO default value\n\nMerchant-data reference types resolve from the store's live data at render time, so they cannot carry a static `defaultValue`: IMAGE, IMAGE_LIST, VIDEO, PRODUCT, PRODUCT_LIST, PRODUCT_ATTRIBUTE, PRODUCT_ATTRIBUTE_LIST, BRAND, BRAND_LIST, CATEGORY, CATEGORY_LIST, BLOG, BLOG_LIST, BLOG_CATEGORY, BLOG_CATEGORY_LIST, RAFFLE, RAFFLE_LIST. The CLI (`add-prop`/`update-prop`) rejects a `defaultValue` on these with an error, and the editor strips any that slips through a hand-edited config. Omit `defaultValue` entirely for these props. (Scalar, LINK, COMPONENT, TYPE and SVG props DO support defaults.)\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### SVG and SVG_LIST props (inline vector graphics)\n\nUse `SVG` / `SVG_LIST` when you need a **vector** asset (logo, icon, decorative shape) that must scale crisply and be styleable with CSS — things the raster image CDN cannot do. The editor uploads the file and stores the **raw `<svg>…</svg>` markup as a string**; it is NOT uploaded to the image CDN, so there is no URL and no `IkasImage` wrapper. Uploads are sanitized by the editor at upload time — XSS vectors (`<script>`, `on*` handlers, `javascript:` hrefs, `<foreignObject>`) are stripped — in addition to the existing 64 KB cap and rejection of SVGs containing embedded base64 raster images.\n\n- `SVG` → `string` (one SVG's markup)\n- `SVG_LIST` → `string[]` (multiple SVGs; the editor allows selecting several files at once)\n\n**Rendering:** inject the markup directly. Because it is the same `<svg>` source the user uploaded, you can target it with CSS (e.g. `.icon svg { width: 24px; fill: currentColor; }`).\n\n**Normalize before injecting:** call `normalizeSvg(value, { idPrefix })` from `@ikas/bp-storefront` before setting `dangerouslySetInnerHTML`, so internal ids/`clipPath`/`url(#…)` references get scoped per instance and don't collide when the same SVG (or multiple `SVG_LIST` items) render more than once on a page. For `SVG_LIST`, give each item a deterministic per-item `idPrefix`, e.g. `` `${propKey}-${i}` ``. By default `normalizeSvg` also strips fixed `width`/`height` and ensures a `viewBox` (so CSS controls size); pass `{ color: \"currentColor\" }` to recolor single-color icons via CSS `color` (omit it for multi-color SVGs). This is opt-in for code-components — the theme's built-in SVG element normalizes automatically, but your own `dangerouslySetInnerHTML` injections must call it explicitly.\n```tsx\nimport { normalizeSvg } from \"@ikas/bp-storefront\";\nimport { Props } from \"./types\";\n\nexport function Logo({ logo, partnerLogos }: Props) {\n return (\n <div className=\"logos\">\n {logo && (\n <span className=\"icon\" dangerouslySetInnerHTML={{ __html: normalizeSvg(logo, { idPrefix: \"logo\" }) }} />\n )}\n {(partnerLogos ?? []).map((svg, i) => (\n <span key={i} className=\"icon\" dangerouslySetInnerHTML={{ __html: normalizeSvg(svg, { idPrefix: `partner-logo-${i}` }) }} />\n ))}\n </div>\n );\n}\nexport default Logo;\n```\n\n**Config example (SVG):**\n```json\n{\n \"name\": \"logo\",\n \"displayName\": \"Logo\",\n \"type\": \"SVG\"\n}\n```\n\n**Config example (SVG_LIST):**\n```json\n{\n \"name\": \"partnerLogos\",\n \"displayName\": \"Partner Logos\",\n \"type\": \"SVG_LIST\"\n}\n```\n\n**CLI command:**\n```bash\nnpx ikas-component config add-prop --component MyComp --name logo --displayName Logo --type SVG\n```\n\n**When NOT to use:** for photos/raster artwork or anything that should be optimized/resized by the CDN, use `IMAGE` / `IMAGE_LIST` instead. SVG is only for hand-authored vector markup.\n\n**Providing a default value:** A default value is a raw <svg>\u2026</svg> string (SVG) or an array of them (SVG_LIST). It must be a single well-formed <svg> root, \u2264 64 KB, with no embedded base64 raster images; it is sanitized on load. Invalid defaults are rejected by the CLI (add-prop / add-component hard-fail with the reason) and silently dropped by the editor when the component is placed, so verify your default is valid. It must also RENDER VISIBLY: an open path with no enclosed area (e.g. the single horizontal line M4 12h16) is invisible under the default fill:black; stroke:none. Give the shape real area and set a color - prefer fill=currentColor (or an explicit stroke with stroke-width) so the icon inherits the theme text color. Example: \"defaultValue\": \"<svg viewBox=\\\"0 0 24 24\\\"><path fill=\\\"currentColor\\\" d=\\\"M12 2 2 22h20z\\\"/></svg>\".\n\n### SVG normalization & sanitization -- the theme-developer standard\n\n**Responsibility split (read this first):**\n- **Security/sanitization is the editor's job, not yours.** Every SVG is sanitized (DOMPurify) on upload, on programmatic writes, AND at render -- you never sanitize in component code.\n- **Normalization (size / color / id-scoping) is `normalizeSvg`'s job.** The theme's built-in SVG element applies it automatically; in your own code component you MUST call `normalizeSvg(value, { idPrefix })` before `dangerouslySetInnerHTML`. There is ONE shared util -- do not reinvent per theme: `import { normalizeSvg } from \"@ikas/bp-storefront\"`.\n\n`normalizeSvg(svg, options)` options: `{ idPrefix: string; scopeIds?: boolean = true; color?: \"currentColor\"; size?: boolean = true }`.\n\n**1. Size.** `size: true` (default) synthesizes a `viewBox` from numeric (unitless/px) `width`/`height` when missing, then strips `width`/`height` so CSS controls size. If there is no viewBox AND size cannot be synthesized (e.g. `%`/`em` dims), width/height are kept (so the icon is not left with zero intrinsic size). Merchants are NOT required to upload viewBox'd or size-less SVGs -- normalize handles it. Size icons with CSS: `.icon svg { width: 24px; height: 24px; }`.\n\n**2. Color.** Opt-in `{ color: \"currentColor\" }` rewrites `fill`/`stroke` (attributes AND `style:` declarations) to `currentColor`, but PRESERVES `none` and `url(...)` (gradients/patterns). So outline icons (`fill=\"none\" stroke=\"...\"`) keep `fill:none` and get `stroke:currentColor` -- no special-casing needed. For MULTI-COLOR SVGs, OMIT `color` to keep the original colors. Rule of thumb: single-color icons -> pass `currentColor`; multi-color -> omit.\n\n**3. Collision (id / clipPath / url(#...)).** Solved in code, not by the editor. `scopeIds: true` (default) prefixes every declared `id` and every `url(#...)` / `href=\"#...\"` reference with `idPrefix`, so the same SVG rendered multiple times (or multiple SVG_LIST items) cannot collide. Give each render an instance-unique prefix -- the built-in element uses its element id; for SVG_LIST use a per-item prefix like `` `${propKey}-${i}` ``.\n\n**4. Sanitization -- what the editor strips.** Beyond the 64 KB cap and base64-raster rejection, the editor strips `<script>`, `on*` event handlers (onload/onclick/onerror/...), `javascript:` hrefs, and `<foreignObject>`. This runs on file upload, on programmatic `update_section_prop` writes, and again at render -- so stored and rendered markup is always safe. `normalizeSvg` is NOT a sanitizer (pure string transforms); security lives in the separate sanitize/validate layer and is automatic.\n\n**5. One shared pattern.** Yes -- `normalizeSvg` from `@ikas/bp-storefront` is the single, isomorphic (SSR/browser/node) util. Do not hand-roll size/color/id-scoping per theme.\n\n**Naming.** The editor shows each SVG's `class` attribute as its display name in the list; SVG / SVG_LIST items with no class render as `svg-1`, `svg-2`, .... Set `class=\"my-icon\"` in the markup to name it.\n\n### NUMBER_RANGE prop (bounded numeric slider)\n\nA `NUMBER_RANGE` prop renders a **slider + number input** and gives the store owner one bounded number with an optional unit. Configure the slider with a **required** `numberRangeData` object on the prop (`min` and `max` are mandatory; `interval` and `unit` are optional). A NUMBER_RANGE prop authored without it is rejected:\n\n- `min` (integer) — lower bound\n- `max` (integer) — upper bound, must be greater than `min`\n- `interval` (positive integer, optional) — the step increment; typed values snap to `min + k·interval`, and a `0`/negative interval is dropped (step falls back to 1)\n- `unit` (string, optional) — suffix shown next to the number input (e.g. `\"px\"`, `\"%\"`)\n\n**Runtime read shape** is `IkasNumberRange`: read `props.myRange.value` (a number) and `props.myRange.unit` (string or null) — e.g. `props.gap.value + (props.gap.unit ?? \"px\")`.\n\n**Authored `defaultValue`** is a typed object `{ \"value\": <number>, \"unit\": \"px\" | null }` — NOT a bare number and NOT a JSON string. When the prop defines `numberRangeData`, the CLI validates that `value` is within `[min, max]` and lands on the interval grid (`min + k·interval`); off-range or off-step defaults are rejected.\n\n**Config example:**\n```json\n{ \"name\": \"gap\", \"displayName\": \"Gap\", \"type\": \"NUMBER_RANGE\", \"numberRangeData\": { \"min\": 0, \"max\": 100, \"interval\": 5, \"unit\": \"px\" }, \"defaultValue\": { \"value\": 20, \"unit\": \"px\" } }\n```\n\n**CLI command:**\n```bash\nnpx ikas-component config add-prop --component MyComp --name gap --displayName Gap --type NUMBER_RANGE --numberRangeData '{\"min\":0,\"max\":100,\"interval\":5,\"unit\":\"px\"}' --defaultValue '{\"value\":20,\"unit\":\"px\"}'\n```\n\n**Update the config or default later** (send a new `--numberRangeData` to change it; `--numberRangeData none` is rejected while the prop stays NUMBER_RANGE, since min/max are required):\n```bash\nnpx ikas-component config update-prop --component MyComp --prop gap --numberRangeData '{\"min\":0,\"max\":200,\"interval\":10,\"unit\":\"px\"}'\n```\n\n### PRODUCT_ATTRIBUTE and PRODUCT_ATTRIBUTE_LIST props (product custom fields)\n\n**CRITICAL — these props do NOT hand you attribute values.** What your component receives is a *wrapper* pairing the picker selection with the product it resolves against. Reading `prop.value` (or mapping over the prop) references fields that do not exist at runtime: `check` and `build` stay clean, every field is `undefined`, and the component silently renders nothing.\n\nThree distinct shapes are involved — do not confuse them:\n\n| Where | Shape |\n|-------|-------|\n| Value stored by the editor (visible via `get_section_values`) | `{ \"attributeId\": \"…\", \"usePageData\": true }` — the list variant uses `attributeIds: [...]` |\n| Runtime prop your component receives | `IkasProductAttributeDetail` = `{ product, attributePropValue }` — the list variant is `IkasProductAttributeList` = `{ product, attributeListPropValue }` |\n| Resolved attribute values | Reachable ONLY through the helpers below: `getAttributeDetailValues()` → `IkasProductAttributeValue[]`, `getAttributeListValues()` → `IkasProductAttributeMap[]` (each entry is `{ attribute, values }`) |\n\n**Read them with the helpers from `@ikas/bp-storefront`:**\n```tsx\nimport { getAttributeDetailValues, getAttributeListValues } from \"@ikas/bp-storefront\";\n\n// PRODUCT_ATTRIBUTE → attribute: IkasProductAttributeDetail | null\nif (!attribute?.attributePropValue?.attributeId) return null;\nconst html = getAttributeDetailValues(attribute)\n .map((v) => v.value)\n .filter(Boolean)\n .join(\"\");\n\n// PRODUCT_ATTRIBUTE_LIST → attributeList: IkasProductAttributeList\n// getAttributeListValues returns IkasProductAttributeMap[] — grouped per attribute, NOT a flat value list\nconst groups = getAttributeListValues(attributeList);\ngroups.forEach((g) => console.log(g.attribute.name, g.values));\n```\n\n**Second trap — a resolved value's `value` is often `null`.** `getAttributeDetailValues()` returning items does NOT mean you have text to render. For the choice-style attribute types (`CHOICE`, `MULTIPLE_CHOICE`, `COLOR_THUMBNAIL_MULTIPLE_CHOICE`) the selection lives in `productAttributeOptionId` / `productAttributeOption` and `value` is `null` by design, so `.map((v) => v.value)` yields empty strings and the component looks broken again. Read the label defensively:\n\n```tsx\nconst text = getAttributeDetailValues(attribute)\n .map((v) => v.value ?? v.productAttributeOption?.name ?? \"\")\n .filter(Boolean)\n .join(\", \");\n```\n\nWhich field carries the content depends on `v.productAttribute?.type`: `TEXT`/`NUMERIC`/`HTML`/`DATE`/`DATETIME`/`BOOLEAN` → `value`; the choice types → `productAttributeOption.name`; `IMAGE` → `images` / `imageIds`; `TABLE` → parse with `getIkasProductAttributeTableValue(v)`; `PRODUCT` → `getIkasProductAttrributeProductIds(v)` (that export name really is misspelled — see `get_framework_guide(\"imports\")`). Check the type with `get_type_definition(\"IkasProductAttributeValue\")` before assuming a field is populated.\n\n`PRODUCT_ATTRIBUTE_LIST` is a **single object, not an array** — `attributeList.map(...)` throws at runtime; the selected ids live at `attributeList.attributeListPropValue.attributeIds`. Both prop types are merchant-data types, so neither can carry a `defaultValue`. Details: `get_function_doc(\"getAttributeDetailValues\")`, `get_type_definition(\"IkasProductAttributeDetail\")`, `get_type_definition(\"IkasProductAttributeList\")`.\n\n### LINK and LIST_OF_LINK props (navigation links)\n\n**CRITICAL — the authored `defaultValue` shape is NOT the runtime read shape.** In component code you READ a link as `props.myLink.href` / `.label` / `.subLinks`. But the `defaultValue` you author in `ikas.config.json` MUST be the typed object shown below. Do NOT pass `{ \"href\": \"…\", \"label\": \"…\" }` (legacy shape) and do NOT pass the value as a JSON string — the CLI rejects both.\n\n**LINK** `defaultValue` is a single link object:\n```json\n{ \"linkType\": \"EXTERNAL\", \"label\": \"Shop now\", \"externalLink\": \"https://example.com\", \"subLinks\": [] }\n```\nor, to link to a store page:\n```json\n{ \"linkType\": \"PAGE\", \"label\": \"Home\", \"pageType\": \"INDEX\", \"subLinks\": [] }\n```\n\n**LIST_OF_LINK** `defaultValue` wraps an array of link objects in `{ \"links\": [...] }`:\n```json\n{ \"links\": [\n { \"linkType\": \"PAGE\", \"label\": \"Home\", \"pageType\": \"INDEX\", \"subLinks\": [] },\n { \"linkType\": \"EXTERNAL\", \"label\": \"Blog\", \"externalLink\": \"https://example.com/blog\", \"subLinks\": [] }\n] }\n```\n\n**Rules for every link object:**\n- `linkType` is REQUIRED and must be one of `PAGE` or `EXTERNAL`. `FILE` links are NOT authorable in a code-component default (they reference a store-specific uploaded file) — the CLI rejects them.\n- Each link MUST carry a destination for its `linkType` — the CLI rejects a link that points nowhere. `EXTERNAL` → a non-empty `externalLink` URL. `PAGE` → a `pageType` (e.g. `INDEX`); do NOT invent a `pageId` — it is per-store and filled automatically when the theme is installed, so author only `pageType`.\n- `subLinks` must be an array (use `[]` when empty); each entry is itself a link object with the same shape (for dropdown/nested menus).\n- NEVER use a JSON string, and NEVER use the legacy `{ label, href }` shape.\n\n**Config example (LINK):**\n```json\n{ \"name\": \"ctaLink\", \"displayName\": \"CTA Link\", \"type\": \"LINK\", \"defaultValue\": { \"linkType\": \"EXTERNAL\", \"label\": \"Shop now\", \"externalLink\": \"https://example.com\", \"subLinks\": [] } }\n```\n\n**Config example (LIST_OF_LINK):**\n```json\n{ \"name\": \"menuLinks\", \"displayName\": \"Menu Links\", \"type\": \"LIST_OF_LINK\", \"defaultValue\": { \"links\": [ { \"linkType\": \"PAGE\", \"label\": \"Home\", \"pageType\": \"INDEX\", \"subLinks\": [] } ] } }\n```\n\n**CLI command (LIST_OF_LINK):**\n```bash\nnpx ikas-component config add-prop --component MyComp --name menuLinks --displayName \"Menu Links\" --type LIST_OF_LINK --defaultValue '{\"links\":[{\"linkType\":\"PAGE\",\"label\":\"Home\",\"pageType\":\"INDEX\",\"subLinks\":[]}]}'\n```\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**Providing a default value:** Style TYPE props accept a `defaultValue` object — size/style types (Padding, Margin, Width, Height, Gap, BorderRadius, Size, …) take `{ \"value\": <number>, \"unit\": \"px\" | \"rem\" | \"%\" | \"vh\" | \"vw\" }` (or `{ \"css\": \"<raw css>\" }`). Add it to the prop object, e.g. `\"defaultValue\": { \"value\": 16, \"unit\": \"px\" }`. For an `_array` typeId the default is an array of these objects; the `--defaultValue` flag also accepts this JSON as a string. Only `css`, `value` and `unit` keys are allowed on a size/style default object — any other field is rejected.\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.\n3. **Font Style enum** (sentinel `__patternElementEnum__`): lets store owners pick one of the theme's typography tokens. Built-in — no `add-enum` and no editor connection needed. See \"Font Style enum (typography picker)\" below.\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.\n\n### Font Style enum (typography picker)\nUse the built-in sentinel `enumTypeId: \"__patternElementEnum__\"` to let store owners pick one of the theme's typography tokens (a \"Font Style\" prop). It works exactly like a built-in enum: no `config add-enum` call and no editor connection required.\n\n**Example config:**\n```json\n{\n \"name\": \"fontStyle\",\n \"displayName\": \"Font Style\",\n \"type\": \"ENUM\",\n \"enumTypeId\": \"__patternElementEnum__\",\n \"required\": false\n}\n```\nThis generates `fontStyle?: string` in types.ts.\n\n**CLI command:**\n```bash\nnpx ikas-component config add-prop --component MyComp --name fontStyle --displayName \"Font Style\" --type ENUM --enumTypeId __patternElementEnum__\n```\n\n**Runtime:** the prop value is the chosen typography's **id** (a plain string), not the typography object itself. Resolve it with `getThemeTypography()` (see `get_framework_guide(\"theme-globals\")`) and apply its `className`:\n```tsx\nimport { getThemeTypography } from \"@ikas/bp-storefront\";\nimport type { Props } from \"./types\"; // fontStyle?: string\n\nexport function MyCard({ fontStyle }: Props) {\n const t = getThemeTypography().find((t) => t.id === fontStyle);\n return <p className={t?.className}>...</p>; // _<id> class applies the theme's typography CSS\n}\n```",
|
|
39
39
|
"tags": [
|
|
40
40
|
"props",
|
|
41
41
|
"types",
|
|
@@ -151,7 +151,7 @@
|
|
|
151
151
|
"imports": {
|
|
152
152
|
"title": "Import Patterns",
|
|
153
153
|
"description": "How to import storefront functions and types",
|
|
154
|
-
"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 — 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`).\n\n### ⛔ Never deep-import from the SDK\n\nImport ONLY from the bare package root `@ikas/bp-storefront` (and likewise `@ikas/bp-storefront-models`/`-config`). **NEVER** import a subpath such as `@ikas/bp-storefront/dist/...` or `@ikas/bp-storefront/src/...`. Those private paths bundle without error but have NO window global in the published storefront, so the component throws at runtime — and `ikas-component build`/`dev` now hard-rejects them. If a helper looks missing from the root, it almost always exists there under a public name. Examples:\n\n- Coupon code → `getCouponCodeForm(customerStore)`, `initCouponCodeForm`, `setCouponCodeFormCouponCode`, `submitCouponCodeForm`, `removeCouponCodeForm`\n- Cart note / generic cart save → `saveCart`\n- Cart data → `cartStore`, `getCart`",
|
|
154
|
+
"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 — 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`).\n\n### ⛔ Never deep-import from the SDK\n\nImport ONLY from the bare package root `@ikas/bp-storefront` (and likewise `@ikas/bp-storefront-models`/`-config`). **NEVER** import a subpath such as `@ikas/bp-storefront/dist/...` or `@ikas/bp-storefront/src/...`. Those private paths bundle without error but have NO window global in the published storefront, so the component throws at runtime — and `ikas-component build`/`dev` now hard-rejects them. If a helper looks missing from the root, it almost always exists there under a public name. Examples:\n\n- Coupon code → `getCouponCodeForm(customerStore)`, `initCouponCodeForm`, `setCouponCodeFormCouponCode`, `submitCouponCodeForm`, `removeCouponCodeForm`\n- Cart note / generic cart save → `saveCart`\n- Cart data → `cartStore`, `getCart`\n\n### Known misspelled export — import it verbatim\n\n`getIkasProductAttrributeProductIds` is spelled with a double **r** (\"Attrribute\"). That IS the real export name: it ships in `@ikas/bp-storefront` and is kept as-is because renaming a published export would break every theme importing it. Use it exactly as written — the correctly spelled `getIkasProductAttributeProductIds` does not exist and will not compile. Do NOT report it as a typo or \"fix\" the spelling. (In the blueprint editor its display alias is `getProductIds`, which hides the misspelling.)",
|
|
155
155
|
"tags": [
|
|
156
156
|
"import",
|
|
157
157
|
"storefront",
|
package/data/storefront-api.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"generatedAt": "2026-07-
|
|
2
|
+
"generatedAt": "2026-07-27T12:03:17.458Z",
|
|
3
3
|
"functions": [
|
|
4
4
|
{
|
|
5
5
|
"name": "apiListProductBrand",
|
|
@@ -4628,7 +4628,7 @@
|
|
|
4628
4628
|
"name": "getIkasProductAttrributeProductIds",
|
|
4629
4629
|
"displayName": "getProductIds",
|
|
4630
4630
|
"signature": "function getIkasProductAttrributeProductIds(value: IkasProductAttributeValue): string[]",
|
|
4631
|
-
"description": "Extracts and returns the product IDs from a PRODUCT-type attribute value's JSON string.",
|
|
4631
|
+
"description": "Extracts and returns the product IDs from a PRODUCT-type attribute value's JSON string.\nNOTE: the export name really is misspelled — \"Attrribute\" with a double r. It is kept as-is because\nrenaming a published export would break every theme that imports it, so import it VERBATIM; the\ncorrectly spelled `getIkasProductAttributeProductIds` does not exist and will not compile. This is\nnot a documentation typo to report or \"fix\". In the blueprint editor the display alias is\n`getProductIds`, which hides the misspelling; only direct TypeScript imports see it.",
|
|
4632
4632
|
"params": [
|
|
4633
4633
|
{
|
|
4634
4634
|
"name": "value",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"generatedAt": "2026-07-
|
|
2
|
+
"generatedAt": "2026-07-27T12:03:17.483Z",
|
|
3
3
|
"types": [
|
|
4
4
|
{
|
|
5
5
|
"name": "TextElementTagTypes",
|
|
@@ -8443,9 +8443,11 @@
|
|
|
8443
8443
|
"unionValues": [
|
|
8444
8444
|
"CHOICE",
|
|
8445
8445
|
"MULTIPLE_CHOICE",
|
|
8446
|
+
"COLOR_THUMBNAIL_MULTIPLE_CHOICE",
|
|
8446
8447
|
"TEXT",
|
|
8447
8448
|
"BOOLEAN",
|
|
8448
8449
|
"NUMERIC",
|
|
8450
|
+
"DATE",
|
|
8449
8451
|
"DATETIME",
|
|
8450
8452
|
"HTML",
|
|
8451
8453
|
"TABLE",
|