@ikas/code-components-mcp 1.4.0-beta.1 → 1.4.0-beta.3

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.
Files changed (37) hide show
  1. package/data/framework.json +2 -2
  2. package/data/migration-examples/complex-header-migration/_meta.json +4 -0
  3. package/data/migration-examples/complex-header-migration/after-config-snippet.json +55 -0
  4. package/data/migration-examples/complex-header-migration/after-section.tsx +64 -0
  5. package/data/migration-examples/complex-header-migration/before-props-summary.json +42 -0
  6. package/data/migration-examples/custom-dynamic-list-to-component-list/_meta.json +4 -0
  7. package/data/migration-examples/custom-dynamic-list-to-component-list/after-child-styles.css +38 -0
  8. package/data/migration-examples/custom-dynamic-list-to-component-list/after-child.tsx +22 -0
  9. package/data/migration-examples/custom-dynamic-list-to-component-list/after-config-snippet.json +31 -0
  10. package/data/migration-examples/custom-dynamic-list-to-component-list/after-section-styles.css +25 -0
  11. package/data/migration-examples/custom-dynamic-list-to-component-list/after-section.tsx +17 -0
  12. package/data/migration-examples/custom-dynamic-list-to-component-list/before-component.tsx +32 -0
  13. package/data/migration-examples/custom-dynamic-list-to-component-list/before-theme-snippet.json +53 -0
  14. package/data/migration-examples/full-component-with-tailwind/_meta.json +4 -0
  15. package/data/migration-examples/full-component-with-tailwind/after-component.tsx +43 -0
  16. package/data/migration-examples/full-component-with-tailwind/after-config-snippet.json +25 -0
  17. package/data/migration-examples/full-component-with-tailwind/after-styles.css +99 -0
  18. package/data/migration-examples/full-component-with-tailwind/before-component.tsx +60 -0
  19. package/data/migration-examples/object-custom-to-inline-props/_meta.json +4 -0
  20. package/data/migration-examples/object-custom-to-inline-props/after-component.tsx +34 -0
  21. package/data/migration-examples/object-custom-to-inline-props/after-config-snippet.json +23 -0
  22. package/data/migration-examples/object-custom-to-inline-props/after-styles.css +38 -0
  23. package/data/migration-examples/object-custom-to-inline-props/before-component.tsx +30 -0
  24. package/data/migration-examples/object-custom-to-inline-props/before-theme-snippet.json +26 -0
  25. package/data/migration-examples/slider-library-replacement/_meta.json +4 -0
  26. package/data/migration-examples/slider-library-replacement/after-child.tsx +13 -0
  27. package/data/migration-examples/slider-library-replacement/after-config-snippet.json +29 -0
  28. package/data/migration-examples/slider-library-replacement/after-section.tsx +38 -0
  29. package/data/migration-examples/slider-library-replacement/after-styles.css +43 -0
  30. package/data/migration-examples/slider-library-replacement/before-component.tsx +39 -0
  31. package/data/migration-examples/slider-library-replacement/before-types-snippet.ts +14 -0
  32. package/data/migration.json +95 -0
  33. package/data/storefront-api.json +1 -1
  34. package/data/storefront-types.json +1 -1
  35. package/dist/index.js +1332 -26
  36. package/dist/index.js.map +1 -1
  37. package/package.json +1 -1
@@ -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### 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>`. |\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.",
39
39
  "tags": [
40
40
  "props",
41
41
  "types",
@@ -530,4 +530,4 @@
530
530
  ]
531
531
  }
532
532
  }
533
- }
533
+ }
@@ -0,0 +1,4 @@
1
+ {
2
+ "title": "Complex Header Migration (Navbar → HeaderSection)",
3
+ "description": "Converts a complex Navbar component with CUSTOM props (menu links, cookie, contact), SLIDER props, Swiper usage, and Headless UI into a HeaderSection with COMPONENT_LIST children."
4
+ }
@@ -0,0 +1,55 @@
1
+ {
2
+ "components": [
3
+ {
4
+ "id": "my-theme-header",
5
+ "name": "HeaderSection",
6
+ "type": "section",
7
+ "isHeader": true,
8
+ "entry": "./src/components/HeaderSection/index.tsx",
9
+ "styles": "./src/components/HeaderSection/styles.css",
10
+ "props": [
11
+ { "name": "logo", "displayName": "Logo", "type": "IMAGE", "required": true },
12
+ { "name": "logoWidth", "displayName": "Logo Width (px)", "type": "NUMBER", "required": false, "defaultValue": 120 },
13
+ { "name": "mobileLogoWidth", "displayName": "Mobile Logo Width (px)", "type": "NUMBER", "required": false, "defaultValue": 80 },
14
+ { "name": "menuLinks", "displayName": "Menu Links", "type": "COMPONENT_LIST", "required": false, "filteredComponentIds": ["my-theme-menu-link"] },
15
+ { "name": "rightIcons", "displayName": "Right Icons", "type": "COMPONENT_LIST", "required": false, "filteredComponentIds": ["my-theme-icon-link"] },
16
+ { "name": "announcement", "displayName": "Announcement", "type": "TEXT", "required": false },
17
+ { "name": "announcementLink", "displayName": "Announcement Link", "type": "LINK", "required": false },
18
+ { "name": "announcementBg", "displayName": "Announcement Background", "type": "COLOR", "required": false },
19
+ { "name": "searchPlaceholder", "displayName": "Search Placeholder", "type": "TEXT", "required": false, "defaultValue": "Search..." },
20
+ { "name": "navBackground", "displayName": "Nav Background", "type": "COLOR", "required": false },
21
+ { "name": "stickyNav", "displayName": "Sticky Navigation", "type": "BOOLEAN", "required": false, "defaultValue": true },
22
+ { "name": "searchProducts", "displayName": "Search Popular Products", "type": "PRODUCT_LIST", "required": false }
23
+ ],
24
+ "propGroups": [
25
+ { "id": "appearance", "name": "Appearance" },
26
+ { "id": "search", "name": "Search" }
27
+ ]
28
+ },
29
+ {
30
+ "id": "my-theme-menu-link",
31
+ "name": "MenuLink",
32
+ "type": "component",
33
+ "entry": "./src/components/MenuLink/index.tsx",
34
+ "styles": "./src/components/MenuLink/styles.css",
35
+ "props": [
36
+ { "name": "link", "displayName": "Link", "type": "LINK", "required": true },
37
+ { "name": "icon", "displayName": "Icon", "type": "IMAGE", "required": false },
38
+ { "name": "color", "displayName": "Text Color", "type": "COLOR", "required": false },
39
+ { "name": "subLinks", "displayName": "Sub Links", "type": "LIST_OF_LINK", "required": false }
40
+ ]
41
+ },
42
+ {
43
+ "id": "my-theme-icon-link",
44
+ "name": "IconLink",
45
+ "type": "component",
46
+ "entry": "./src/components/IconLink/index.tsx",
47
+ "styles": "./src/components/IconLink/styles.css",
48
+ "props": [
49
+ { "name": "image", "displayName": "Icon Image", "type": "IMAGE", "required": true },
50
+ { "name": "link", "displayName": "Link", "type": "LINK", "required": true },
51
+ { "name": "aspectRatio", "displayName": "Aspect Ratio", "type": "TEXT", "required": false, "defaultValue": "1/1" }
52
+ ]
53
+ }
54
+ ]
55
+ }
@@ -0,0 +1,64 @@
1
+ import { useState } from "preact/hooks";
2
+ import { getDefaultSrc, IkasComponentRenderer } from "@ikas/bp-storefront";
3
+ import { Props } from "./types";
4
+
5
+ export default function HeaderSection({
6
+ logo,
7
+ logoWidth,
8
+ mobileLogoWidth,
9
+ menuLinks,
10
+ rightIcons,
11
+ announcement,
12
+ announcementLink,
13
+ announcementBg,
14
+ searchPlaceholder,
15
+ navBackground,
16
+ stickyNav,
17
+ ...props
18
+ }: Props) {
19
+ const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
20
+
21
+ return (
22
+ <header className={`header ${stickyNav ? "header--sticky" : ""}`}>
23
+ {announcement && (
24
+ <div className="announcement-bar" style={{ backgroundColor: announcementBg || "#000" }}>
25
+ {announcementLink ? (
26
+ <a href={announcementLink.href} className="announcement-link">{announcement}</a>
27
+ ) : (
28
+ <span>{announcement}</span>
29
+ )}
30
+ </div>
31
+ )}
32
+
33
+ <nav className="nav-bar" style={{ backgroundColor: navBackground || "#fff" }}>
34
+ <div className="nav-inner">
35
+ <button className="mobile-menu-btn" onClick={() => setMobileMenuOpen(!mobileMenuOpen)}>
36
+ <span className="hamburger-icon" />
37
+ </button>
38
+
39
+ {logo && (
40
+ <a href="/" className="logo-link" style={{ "--logo-w": `${logoWidth || 120}px`, "--logo-mw": `${mobileLogoWidth || 80}px` }}>
41
+ <img src={getDefaultSrc(logo)} alt="Logo" className="logo-image" />
42
+ </a>
43
+ )}
44
+
45
+ <div className="menu-links">
46
+ <IkasComponentRenderer id="menu-links" components={menuLinks as any[]} parentProps={props} />
47
+ </div>
48
+
49
+ <div className="right-icons">
50
+ <IkasComponentRenderer id="right-icons" components={rightIcons as any[]} parentProps={props} />
51
+ </div>
52
+ </div>
53
+ </nav>
54
+
55
+ {mobileMenuOpen && (
56
+ <div className="mobile-menu-overlay" onClick={() => setMobileMenuOpen(false)}>
57
+ <div className="mobile-menu" onClick={(e) => e.stopPropagation()}>
58
+ <IkasComponentRenderer id="mobile-menu-links" components={menuLinks as any[]} parentProps={props} />
59
+ </div>
60
+ </div>
61
+ )}
62
+ </header>
63
+ );
64
+ }
@@ -0,0 +1,42 @@
1
+ {
2
+ "component": "Navbar",
3
+ "displayName": "Navbar",
4
+ "isHeader": true,
5
+ "props": [
6
+ { "name": "logo", "type": "IMAGE" },
7
+ { "name": "logoWidth", "type": "SLIDER", "sliderData": { "min": 80, "max": 300 } },
8
+ { "name": "mobLogoWidth", "type": "SLIDER", "sliderData": { "min": 0, "max": 200 } },
9
+ { "name": "menuLinks", "type": "CUSTOM", "customDataRef": "MenLink (DYNAMIC_LIST)" },
10
+ { "name": "extraRightLinks", "type": "CUSTOM", "customDataRef": "GrselveLinkObj (DYNAMIC_LIST)" },
11
+ { "name": "announcement", "type": "TEXT" },
12
+ { "name": "announcementLink", "type": "LINK" },
13
+ { "name": "announcementBg", "type": "COLOR" },
14
+ { "name": "searchPlaceholder", "type": "TEXT" },
15
+ { "name": "showGold", "type": "BOOLEAN" },
16
+ { "name": "navBg", "type": "COLOR" },
17
+ { "name": "mobileMenuBg", "type": "COLOR" },
18
+ { "name": "searchPopularProducts", "type": "PRODUCT_LIST", "groupId": "search-group" },
19
+ { "name": "closeInMobile", "type": "BOOLEAN" },
20
+ { "name": "stickyNav", "type": "BOOLEAN" }
21
+ ],
22
+ "customDataSummary": {
23
+ "MenLink": {
24
+ "type": "DYNAMIC_LIST/OBJECT",
25
+ "fields": [
26
+ { "key": "mainlink", "type": "LINK" },
27
+ { "key": "icon", "type": "IMAGE" },
28
+ { "key": "color", "type": "COLOR" },
29
+ { "key": "images", "type": "DYNAMIC_LIST", "nestedType": "GrselObjesi (image + link + viewType)" },
30
+ { "key": "sublinks", "type": "DYNAMIC_LIST", "nestedType": "AltLink (links + cols)" }
31
+ ]
32
+ },
33
+ "GrselveLinkObj": {
34
+ "type": "DYNAMIC_LIST/OBJECT",
35
+ "fields": [
36
+ { "key": "image", "type": "IMAGE" },
37
+ { "key": "aspect", "type": "TEXT" },
38
+ { "key": "link", "type": "LINK" }
39
+ ]
40
+ }
41
+ }
42
+ }
@@ -0,0 +1,4 @@
1
+ {
2
+ "title": "CUSTOM (DYNAMIC_LIST) → COMPONENT_LIST + Child Component",
3
+ "description": "Converts an old component with a CUSTOM prop referencing a DYNAMIC_LIST customData into a section with COMPONENT_LIST prop and a separate child component. This is the most common migration pattern."
4
+ }
@@ -0,0 +1,38 @@
1
+ .store-card {
2
+ display: block;
3
+ overflow: hidden;
4
+ border-radius: 0.5rem;
5
+ border: 1px solid #e5e5e5;
6
+ text-decoration: none;
7
+ color: inherit;
8
+ }
9
+
10
+ .store-image-wrapper {
11
+ aspect-ratio: 16/9;
12
+ overflow: hidden;
13
+ }
14
+
15
+ .store-image {
16
+ width: 100%;
17
+ height: 100%;
18
+ object-fit: cover;
19
+ }
20
+
21
+ .store-content {
22
+ padding: 1rem;
23
+ }
24
+
25
+ .store-title {
26
+ font-size: 1.125rem;
27
+ font-weight: 500;
28
+ }
29
+
30
+ .store-location {
31
+ font-size: 0.875rem;
32
+ color: #6b7280;
33
+ }
34
+
35
+ .store-phone {
36
+ margin-top: 0.25rem;
37
+ font-size: 0.875rem;
38
+ }
@@ -0,0 +1,22 @@
1
+ import { getDefaultSrc } from "@ikas/bp-storefront";
2
+ import { Props } from "./types";
3
+
4
+ export default function StoreCard({ title, location, district, phone, image, link }: Props) {
5
+ const Tag = link ? "a" : "div";
6
+ const linkProps = link ? { href: link.href } : {};
7
+
8
+ return (
9
+ <Tag {...linkProps} className="store-card">
10
+ {image && (
11
+ <div className="store-image-wrapper">
12
+ <img src={getDefaultSrc(image)} alt={title || ""} className="store-image" />
13
+ </div>
14
+ )}
15
+ <div className="store-content">
16
+ <h3 className="store-title">{title}</h3>
17
+ <p className="store-location">{location}{district ? `, ${district}` : ""}</p>
18
+ {phone && <p className="store-phone">{phone}</p>}
19
+ </div>
20
+ </Tag>
21
+ );
22
+ }
@@ -0,0 +1,31 @@
1
+ {
2
+ "components": [
3
+ {
4
+ "id": "my-theme-stores-section",
5
+ "name": "StoresSection",
6
+ "type": "section",
7
+ "entry": "./src/components/StoresSection/index.tsx",
8
+ "styles": "./src/components/StoresSection/styles.css",
9
+ "props": [
10
+ { "name": "heading", "displayName": "Heading", "type": "TEXT", "required": false },
11
+ { "name": "columns", "displayName": "Columns", "type": "NUMBER", "required": false, "defaultValue": 3 },
12
+ { "name": "stores", "displayName": "Stores", "type": "COMPONENT_LIST", "required": false, "filteredComponentIds": ["my-theme-store-card"] }
13
+ ]
14
+ },
15
+ {
16
+ "id": "my-theme-store-card",
17
+ "name": "StoreCard",
18
+ "type": "component",
19
+ "entry": "./src/components/StoreCard/index.tsx",
20
+ "styles": "./src/components/StoreCard/styles.css",
21
+ "props": [
22
+ { "name": "title", "displayName": "Title", "type": "TEXT", "required": false },
23
+ { "name": "location", "displayName": "Location", "type": "TEXT", "required": false },
24
+ { "name": "district", "displayName": "District", "type": "TEXT", "required": false },
25
+ { "name": "phone", "displayName": "Phone", "type": "TEXT", "required": false },
26
+ { "name": "image", "displayName": "Image", "type": "IMAGE", "required": false },
27
+ { "name": "link", "displayName": "Link", "type": "LINK", "required": false }
28
+ ]
29
+ }
30
+ ]
31
+ }
@@ -0,0 +1,25 @@
1
+ .stores-section {
2
+ max-width: 1700px;
3
+ margin: 2.5rem auto 0;
4
+ padding: 0 20px;
5
+ }
6
+
7
+ @media (min-width: 1024px) {
8
+ .stores-section {
9
+ margin-top: 5rem;
10
+ padding: 0 60px;
11
+ }
12
+ }
13
+
14
+ .stores-heading {
15
+ margin-bottom: 1.5rem;
16
+ text-align: center;
17
+ font-size: 1.5rem;
18
+ font-weight: 600;
19
+ }
20
+
21
+ .stores-grid {
22
+ display: grid;
23
+ grid-template-columns: repeat(var(--cols, 3), 1fr);
24
+ gap: 1.5rem;
25
+ }
@@ -0,0 +1,17 @@
1
+ import { IkasComponentRenderer } from "@ikas/bp-storefront";
2
+ import { Props } from "./types";
3
+
4
+ export default function StoresSection({ heading, columns, stores, ...props }: Props) {
5
+ return (
6
+ <section className="stores-section" style={{ "--cols": columns || 3 }}>
7
+ {heading && <h2 className="stores-heading">{heading}</h2>}
8
+ <div className="stores-grid">
9
+ <IkasComponentRenderer
10
+ id="stores"
11
+ components={stores as any[]}
12
+ parentProps={props}
13
+ />
14
+ </div>
15
+ </section>
16
+ );
17
+ }
@@ -0,0 +1,32 @@
1
+ import { Image } from '@ikas/storefront';
2
+ import { observer } from 'mobx-react-lite';
3
+ import { StoresPageProps } from '../__generated__/types';
4
+
5
+ const StoresPage = ({ heading, stores, columns }: StoresPageProps) => {
6
+ return (
7
+ <div
8
+ style={{ '--cols': columns?.value || 3 } as React.CSSProperties}
9
+ className="wrapper mt-10 lg:mt-20"
10
+ >
11
+ {heading && <h2 className="mb-6 text-center text-2xl font-semibold">{heading}</h2>}
12
+ <div className="grid gap-6" style={{ gridTemplateColumns: `repeat(var(--cols), 1fr)` }}>
13
+ {stores.map((store, i) => (
14
+ <a key={i} href={store.link?.href} className="group overflow-hidden rounded-lg border">
15
+ {store.image && (
16
+ <div className="relative aspect-video">
17
+ <Image image={store.image} layout="fill" objectFit="cover" />
18
+ </div>
19
+ )}
20
+ <div className="p-4">
21
+ <h3 className="text-lg font-medium">{store.title}</h3>
22
+ <p className="text-sm text-gray-500">{store.location}, {store.district}</p>
23
+ <p className="mt-1 text-sm">{store.phone}</p>
24
+ </div>
25
+ </a>
26
+ ))}
27
+ </div>
28
+ </div>
29
+ );
30
+ };
31
+
32
+ export default observer(StoresPage);
@@ -0,0 +1,53 @@
1
+ {
2
+ "components": [
3
+ {
4
+ "id": "stores-page",
5
+ "name": "StoresPage",
6
+ "displayName": "Stores Page",
7
+ "props": [
8
+ {
9
+ "key": "heading",
10
+ "type": "TEXT",
11
+ "displayName": "Heading"
12
+ },
13
+ {
14
+ "key": "stores",
15
+ "type": "CUSTOM",
16
+ "displayName": "Stores",
17
+ "customDataId": "cd_stores_list"
18
+ },
19
+ {
20
+ "key": "columns",
21
+ "type": "SLIDER",
22
+ "displayName": "Columns",
23
+ "sliderData": {
24
+ "min": 1,
25
+ "max": 4,
26
+ "defaultValue": 3
27
+ }
28
+ }
29
+ ]
30
+ }
31
+ ],
32
+ "customData": [
33
+ {
34
+ "id": "cd_stores_list",
35
+ "name": "Mağazalarımız",
36
+ "type": "DYNAMIC_LIST",
37
+ "objectTypeId": "cd_store_object"
38
+ },
39
+ {
40
+ "id": "cd_store_object",
41
+ "name": "Maaza",
42
+ "type": "OBJECT",
43
+ "fields": [
44
+ { "key": "title", "type": "TEXT", "displayName": "Title" },
45
+ { "key": "location", "type": "TEXT", "displayName": "Location" },
46
+ { "key": "district", "type": "TEXT", "displayName": "District" },
47
+ { "key": "phone", "type": "TEXT", "displayName": "Phone" },
48
+ { "key": "image", "type": "IMAGE", "displayName": "Image" },
49
+ { "key": "link", "type": "LINK", "displayName": "Link" }
50
+ ]
51
+ }
52
+ ]
53
+ }
@@ -0,0 +1,4 @@
1
+ {
2
+ "title": "Tailwind CSS → Plain Scoped CSS",
3
+ "description": "Converts a component using Tailwind utility classes, prose, and classnames into a component with plain scoped CSS."
4
+ }
@@ -0,0 +1,43 @@
1
+ import { getDefaultSrc } from "@ikas/bp-storefront";
2
+ import { Props } from "./types";
3
+
4
+ export default function CTASection({
5
+ image,
6
+ aspectRatio,
7
+ title,
8
+ content,
9
+ marginTop,
10
+ mobileMarginTop,
11
+ reverseLayout,
12
+ backgroundColor,
13
+ miniImage,
14
+ miniImageAspect,
15
+ imageMaxWidth,
16
+ textMaxWidth,
17
+ }: Props) {
18
+ return (
19
+ <div className="cta-wrapper" style={{ backgroundColor: backgroundColor || "transparent" }}>
20
+ <div
21
+ className={`cta-grid ${reverseLayout ? "cta-grid--reversed" : ""}`}
22
+ style={{
23
+ "--mt": `${marginTop || 0}px`,
24
+ "--mt-mobile": `${mobileMarginTop || 0}px`,
25
+ }}
26
+ >
27
+ <div className="cta-image-col" style={{ aspectRatio: aspectRatio || "1/1", maxWidth: imageMaxWidth || "316px" }}>
28
+ {image && <img src={getDefaultSrc(image)} alt="" className="cta-image" />}
29
+ </div>
30
+
31
+ <div className="cta-text-col" style={{ maxWidth: textMaxWidth || "600px" }}>
32
+ {miniImage && (
33
+ <div className="cta-mini-image" style={{ aspectRatio: miniImageAspect }}>
34
+ <img src={getDefaultSrc(miniImage)} alt="" className="cta-mini-img" />
35
+ </div>
36
+ )}
37
+ {title && <div className="cta-title" dangerouslySetInnerHTML={{ __html: title }} />}
38
+ {content && <div className="cta-content" dangerouslySetInnerHTML={{ __html: content }} />}
39
+ </div>
40
+ </div>
41
+ </div>
42
+ );
43
+ }
@@ -0,0 +1,25 @@
1
+ {
2
+ "components": [
3
+ {
4
+ "id": "my-theme-cta-section",
5
+ "name": "CTASection",
6
+ "type": "section",
7
+ "entry": "./src/components/CTASection/index.tsx",
8
+ "styles": "./src/components/CTASection/styles.css",
9
+ "props": [
10
+ { "name": "image", "displayName": "Image", "type": "IMAGE", "required": true },
11
+ { "name": "aspectRatio", "displayName": "Image Aspect Ratio", "type": "TEXT", "required": false, "defaultValue": "1/1" },
12
+ { "name": "title", "displayName": "Title (HTML)", "type": "RICH_TEXT", "required": false },
13
+ { "name": "content", "displayName": "Content (HTML)", "type": "RICH_TEXT", "required": true },
14
+ { "name": "marginTop", "displayName": "Margin Top (px)", "type": "NUMBER", "required": false, "defaultValue": 0 },
15
+ { "name": "mobileMarginTop", "displayName": "Mobile Margin Top (px)", "type": "NUMBER", "required": false, "defaultValue": 0 },
16
+ { "name": "reverseLayout", "displayName": "Reverse Image/Text", "type": "BOOLEAN", "required": false, "defaultValue": false },
17
+ { "name": "backgroundColor", "displayName": "Background Color", "type": "COLOR", "required": false },
18
+ { "name": "miniImage", "displayName": "Mini Image", "type": "IMAGE", "required": false },
19
+ { "name": "miniImageAspect", "displayName": "Mini Image Aspect", "type": "TEXT", "required": false },
20
+ { "name": "imageMaxWidth", "displayName": "Image Max Width", "type": "TEXT", "required": false, "defaultValue": "316px" },
21
+ { "name": "textMaxWidth", "displayName": "Text Max Width", "type": "TEXT", "required": false, "defaultValue": "600px" }
22
+ ]
23
+ }
24
+ ]
25
+ }
@@ -0,0 +1,99 @@
1
+ .cta-wrapper {
2
+ width: 100%;
3
+ }
4
+
5
+ .cta-grid {
6
+ max-width: 1700px;
7
+ margin: var(--mt-mobile, 0) auto 0;
8
+ padding: 60px 20px 0;
9
+ display: grid;
10
+ grid-template-columns: 1fr;
11
+ align-items: center;
12
+ }
13
+
14
+ @media (min-width: 1024px) {
15
+ .cta-grid {
16
+ margin-top: var(--mt, 0);
17
+ padding: 0 60px;
18
+ grid-template-columns: 1fr 1fr;
19
+ }
20
+ }
21
+
22
+ .cta-grid--reversed .cta-image-col {
23
+ order: 2;
24
+ }
25
+
26
+ .cta-image-col {
27
+ position: relative;
28
+ width: 100%;
29
+ margin: 0 auto;
30
+ overflow: hidden;
31
+ }
32
+
33
+ @media (min-width: 1024px) {
34
+ .cta-image-col {
35
+ margin-top: 60px;
36
+ }
37
+ }
38
+
39
+ .cta-image {
40
+ width: 100%;
41
+ height: 100%;
42
+ object-fit: cover;
43
+ }
44
+
45
+ .cta-text-col {
46
+ margin: 0 auto;
47
+ display: flex;
48
+ flex-direction: column;
49
+ align-items: center;
50
+ justify-content: center;
51
+ height: 100%;
52
+ padding: 0 1.5rem 1.5rem;
53
+ }
54
+
55
+ @media (min-width: 1280px) {
56
+ .cta-text-col {
57
+ padding: 2.5rem;
58
+ }
59
+ }
60
+
61
+ .cta-mini-image {
62
+ width: 188px;
63
+ margin-bottom: 30px;
64
+ }
65
+
66
+ @media (min-width: 1024px) {
67
+ .cta-mini-image {
68
+ margin-bottom: 40px;
69
+ }
70
+ }
71
+
72
+ .cta-mini-img {
73
+ width: 100%;
74
+ height: 100%;
75
+ object-fit: contain;
76
+ }
77
+
78
+ .cta-title {
79
+ margin-bottom: 1rem;
80
+ text-align: center;
81
+ font-size: 20px;
82
+ font-weight: 300;
83
+ line-height: 1.2;
84
+ color: white;
85
+ }
86
+
87
+ @media (min-width: 1024px) {
88
+ .cta-title {
89
+ font-size: 30px;
90
+ }
91
+ }
92
+
93
+ .cta-content {
94
+ text-align: center;
95
+ font-size: 0.875rem;
96
+ font-weight: 300;
97
+ line-height: 1.2;
98
+ color: white;
99
+ }