@ikas/code-components-mcp 0.116.0 → 0.117.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (221) hide show
  1. package/data/section-templates/account-info-section/children/AccountAddresses/components/AddressModal/index.tsx +6 -9
  2. package/data/section-templates/account-info-section/children/AccountAddresses/components/AddressModal/styles.css +1 -0
  3. package/data/section-templates/account-info-section/children/AccountAddresses/ikas-config-snippet.json +30 -30
  4. package/data/section-templates/account-info-section/children/AccountAddresses/index.tsx +12 -12
  5. package/data/section-templates/account-info-section/children/AccountAddresses/types.ts +1 -0
  6. package/data/section-templates/account-info-section/children/AccountFavorites/ikas-config-snippet.json +9 -9
  7. package/data/section-templates/account-info-section/children/AccountFavorites/index.tsx +3 -3
  8. package/data/section-templates/account-info-section/children/AccountFavorites/types.ts +1 -0
  9. package/data/section-templates/account-info-section/children/AccountInfoContent/ikas-config-snippet.json +16 -16
  10. package/data/section-templates/account-info-section/children/AccountInfoContent/index.tsx +5 -5
  11. package/data/section-templates/account-info-section/children/AccountInfoContent/types.ts +1 -0
  12. package/data/section-templates/account-info-section/children/AccountOrderDetail/ikas-config-snippet.json +74 -74
  13. package/data/section-templates/account-info-section/children/AccountOrderDetail/index.tsx +29 -29
  14. package/data/section-templates/account-info-section/children/AccountOrderDetail/types.ts +1 -0
  15. package/data/section-templates/account-info-section/children/AccountOrders/ikas-config-snippet.json +20 -20
  16. package/data/section-templates/account-info-section/children/AccountOrders/index.tsx +8 -8
  17. package/data/section-templates/account-info-section/children/AccountOrders/types.ts +1 -0
  18. package/data/section-templates/account-info-section/global-types.ts +0 -2
  19. package/data/section-templates/account-info-section/hooks/useScrollLock.ts +0 -4
  20. package/data/section-templates/account-info-section/ikas-config-snippet.json +13 -13
  21. package/data/section-templates/account-info-section/index.tsx +5 -5
  22. package/data/section-templates/account-info-section/sub-components/Modal/index.tsx +4 -2
  23. package/data/section-templates/account-info-section/sub-components/ProductCard/index.tsx +4 -4
  24. package/data/section-templates/account-info-section/types.ts +1 -0
  25. package/data/section-templates/add-to-cart/ikas-config-snippet.json +30 -30
  26. package/data/section-templates/add-to-cart/index.tsx +7 -7
  27. package/data/section-templates/add-to-cart/types.ts +1 -0
  28. package/data/section-templates/blog-home-section/global-types.ts +0 -2
  29. package/data/section-templates/blog-home-section/ikas-config-snippet.json +18 -18
  30. package/data/section-templates/blog-home-section/index.tsx +3 -3
  31. package/data/section-templates/blog-home-section/types.ts +1 -0
  32. package/data/section-templates/blog-post-section/global-types.ts +0 -2
  33. package/data/section-templates/blog-post-section/ikas-config-snippet.json +15 -15
  34. package/data/section-templates/blog-post-section/index.tsx +3 -3
  35. package/data/section-templates/blog-post-section/types.ts +1 -0
  36. package/data/section-templates/bundle-products/global-types.ts +0 -2
  37. package/data/section-templates/bundle-products/ikas-config-snippet.json +25 -25
  38. package/data/section-templates/bundle-products/index.tsx +3 -3
  39. package/data/section-templates/bundle-products/types.ts +1 -0
  40. package/data/section-templates/cart-section/global-types.ts +0 -2
  41. package/data/section-templates/cart-section/ikas-config-snippet.json +32 -32
  42. package/data/section-templates/cart-section/index.tsx +13 -13
  43. package/data/section-templates/cart-section/sub-components/CartItem/components/BundleProducts/index.tsx +1 -1
  44. package/data/section-templates/cart-section/types.ts +1 -0
  45. package/data/section-templates/category-images-section/children/CategoryImageItem/ikas-config-snippet.json +12 -12
  46. package/data/section-templates/category-images-section/children/CategoryImageItem/types.ts +1 -0
  47. package/data/section-templates/category-images-section/global-types.ts +0 -2
  48. package/data/section-templates/category-images-section/ikas-config-snippet.json +11 -11
  49. package/data/section-templates/category-images-section/types.ts +1 -0
  50. package/data/section-templates/category-list-section/children/CardProductName/ikas-config-snippet.json +2 -2
  51. package/data/section-templates/category-list-section/children/CardProductName/types.ts +1 -0
  52. package/data/section-templates/category-list-section/children/CardProductPrice/ikas-config-snippet.json +1 -1
  53. package/data/section-templates/category-list-section/children/CardProductPrice/types.ts +1 -0
  54. package/data/section-templates/category-list-section/children/CardProductVariants/ikas-config-snippet.json +1 -1
  55. package/data/section-templates/category-list-section/children/CardProductVariants/types.ts +1 -0
  56. package/data/section-templates/category-list-section/components/CategoryListControls/index.tsx +1 -1
  57. package/data/section-templates/category-list-section/global-types.ts +0 -2
  58. package/data/section-templates/category-list-section/hooks/useScrollLock.ts +0 -4
  59. package/data/section-templates/category-list-section/ikas-config-snippet.json +50 -50
  60. package/data/section-templates/category-list-section/index.tsx +13 -13
  61. package/data/section-templates/category-list-section/sub-components/ProductCard/index.tsx +4 -4
  62. package/data/section-templates/category-list-section/types.ts +1 -0
  63. package/data/section-templates/component-renderer/additional/Features/types.ts +1 -0
  64. package/data/section-templates/component-renderer/additional/ProductDetail/index.tsx +1 -1
  65. package/data/section-templates/component-renderer/additional/ProductDetail/types.ts +1 -0
  66. package/data/section-templates/component-renderer/global-types.ts +0 -2
  67. package/data/section-templates/component-renderer/ikas-config-snippet.json +5 -5
  68. package/data/section-templates/component-renderer/types.ts +1 -0
  69. package/data/section-templates/email-verification-section/ikas-config-snippet.json +36 -36
  70. package/data/section-templates/email-verification-section/index.tsx +14 -14
  71. package/data/section-templates/email-verification-section/types.ts +1 -0
  72. package/data/section-templates/favorites/ikas-config-snippet.json +12 -12
  73. package/data/section-templates/favorites/types.ts +1 -0
  74. package/data/section-templates/features-section/children/FeatureItem/ikas-config-snippet.json +2 -2
  75. package/data/section-templates/features-section/children/FeatureItem/types.ts +1 -0
  76. package/data/section-templates/features-section/ikas-config-snippet.json +2 -2
  77. package/data/section-templates/features-section/types.ts +1 -0
  78. package/data/section-templates/footer-section/children/SocialMediaIcon/ikas-config-snippet.json +1 -1
  79. package/data/section-templates/footer-section/children/SocialMediaIcon/types.ts +1 -0
  80. package/data/section-templates/footer-section/ikas-config-snippet.json +19 -19
  81. package/data/section-templates/footer-section/index.tsx +3 -3
  82. package/data/section-templates/footer-section/types.ts +1 -0
  83. package/data/section-templates/forgot-password-section/components/ForgotPasswordForm/index.tsx +10 -10
  84. package/data/section-templates/forgot-password-section/ikas-config-snippet.json +29 -29
  85. package/data/section-templates/forgot-password-section/types.ts +1 -0
  86. package/data/section-templates/header-section/children/Announcements/ikas-config-snippet.json +9 -9
  87. package/data/section-templates/header-section/children/Announcements/types.ts +1 -0
  88. package/data/section-templates/header-section/children/CookieBar/ikas-config-snippet.json +15 -15
  89. package/data/section-templates/header-section/children/CookieBar/types.ts +1 -0
  90. package/data/section-templates/header-section/children/Navbar/components/MobileMenu/index.tsx +1 -1
  91. package/data/section-templates/header-section/children/Navbar/ikas-config-snippet.json +68 -68
  92. package/data/section-templates/header-section/children/Navbar/index.tsx +19 -19
  93. package/data/section-templates/header-section/children/Navbar/types.ts +1 -0
  94. package/data/section-templates/header-section/global-types.ts +0 -2
  95. package/data/section-templates/header-section/hooks/useScrollLock.ts +0 -4
  96. package/data/section-templates/header-section/ikas-config-snippet.json +5 -5
  97. package/data/section-templates/header-section/sub-components/CartItem/components/BundleProducts/index.tsx +1 -1
  98. package/data/section-templates/header-section/sub-components/ProductCard/index.tsx +4 -4
  99. package/data/section-templates/header-section/types.ts +1 -0
  100. package/data/section-templates/hero-slider-section/children/HeroSliderItem/ikas-config-snippet.json +34 -34
  101. package/data/section-templates/hero-slider-section/children/HeroSliderItem/types.ts +1 -0
  102. package/data/section-templates/hero-slider-section/global-types.ts +0 -2
  103. package/data/section-templates/hero-slider-section/ikas-config-snippet.json +10 -10
  104. package/data/section-templates/hero-slider-section/types.ts +1 -0
  105. package/data/section-templates/image-handling/global-types.ts +0 -2
  106. package/data/section-templates/image-handling/ikas-config-snippet.json +16 -16
  107. package/data/section-templates/image-handling/index.tsx +1 -1
  108. package/data/section-templates/image-handling/types.ts +1 -0
  109. package/data/section-templates/login-section/components/LoginForm/index.tsx +13 -13
  110. package/data/section-templates/login-section/ikas-config-snippet.json +38 -38
  111. package/data/section-templates/login-section/types.ts +1 -0
  112. package/data/section-templates/navigation/components/MobileMenu/index.tsx +1 -1
  113. package/data/section-templates/navigation/global-types.ts +0 -2
  114. package/data/section-templates/navigation/hooks/useScrollLock.ts +0 -4
  115. package/data/section-templates/navigation/ikas-config-snippet.json +68 -68
  116. package/data/section-templates/navigation/index.tsx +19 -19
  117. package/data/section-templates/navigation/sub-components/CartItem/components/BundleProducts/index.tsx +1 -1
  118. package/data/section-templates/navigation/sub-components/ProductCard/index.tsx +4 -4
  119. package/data/section-templates/navigation/types.ts +1 -0
  120. package/data/section-templates/not-found-section/ikas-config-snippet.json +9 -9
  121. package/data/section-templates/not-found-section/types.ts +1 -0
  122. package/data/section-templates/product-detail-section/children/ProductDetailAddToCart/ikas-config-snippet.json +30 -30
  123. package/data/section-templates/product-detail-section/children/ProductDetailAddToCart/index.tsx +7 -7
  124. package/data/section-templates/product-detail-section/children/ProductDetailAddToCart/types.ts +1 -0
  125. package/data/section-templates/product-detail-section/children/ProductDetailBundleFurniture/ikas-config-snippet.json +33 -33
  126. package/data/section-templates/product-detail-section/children/ProductDetailBundleFurniture/index.tsx +8 -8
  127. package/data/section-templates/product-detail-section/children/ProductDetailBundleFurniture/types.ts +1 -0
  128. package/data/section-templates/product-detail-section/children/ProductDetailBundleProduct/ikas-config-snippet.json +25 -25
  129. package/data/section-templates/product-detail-section/children/ProductDetailBundleProduct/index.tsx +3 -3
  130. package/data/section-templates/product-detail-section/children/ProductDetailBundleProduct/types.ts +1 -0
  131. package/data/section-templates/product-detail-section/children/ProductDetailDescription/ikas-config-snippet.json +19 -19
  132. package/data/section-templates/product-detail-section/children/ProductDetailDescription/index.tsx +1 -1
  133. package/data/section-templates/product-detail-section/children/ProductDetailDescription/types.ts +1 -0
  134. package/data/section-templates/product-detail-section/children/ProductDetailFeatures/ikas-config-snippet.json +11 -11
  135. package/data/section-templates/product-detail-section/children/ProductDetailFeatures/types.ts +1 -0
  136. package/data/section-templates/product-detail-section/children/ProductDetailNameFavorite/ikas-config-snippet.json +12 -12
  137. package/data/section-templates/product-detail-section/children/ProductDetailNameFavorite/types.ts +1 -0
  138. package/data/section-templates/product-detail-section/children/ProductDetailOffer/components/OfferCard/index.tsx +2 -2
  139. package/data/section-templates/product-detail-section/children/ProductDetailOffer/ikas-config-snippet.json +39 -39
  140. package/data/section-templates/product-detail-section/children/ProductDetailOffer/index.tsx +9 -9
  141. package/data/section-templates/product-detail-section/children/ProductDetailOffer/types.ts +1 -0
  142. package/data/section-templates/product-detail-section/children/ProductDetailOptionSet/ikas-config-snippet.json +31 -31
  143. package/data/section-templates/product-detail-section/children/ProductDetailOptionSet/index.tsx +7 -7
  144. package/data/section-templates/product-detail-section/children/ProductDetailOptionSet/types.ts +1 -0
  145. package/data/section-templates/product-detail-section/children/ProductDetailPrices/ikas-config-snippet.json +11 -11
  146. package/data/section-templates/product-detail-section/children/ProductDetailPrices/types.ts +1 -0
  147. package/data/section-templates/product-detail-section/children/ProductDetailProductGroup/ikas-config-snippet.json +11 -11
  148. package/data/section-templates/product-detail-section/children/ProductDetailProductGroup/types.ts +1 -0
  149. package/data/section-templates/product-detail-section/children/ProductDetailSku/ikas-config-snippet.json +15 -15
  150. package/data/section-templates/product-detail-section/children/ProductDetailSku/index.tsx +1 -1
  151. package/data/section-templates/product-detail-section/children/ProductDetailSku/types.ts +1 -0
  152. package/data/section-templates/product-detail-section/children/ProductDetailVariant/ikas-config-snippet.json +13 -13
  153. package/data/section-templates/product-detail-section/children/ProductDetailVariant/types.ts +2 -1
  154. package/data/section-templates/product-detail-section/global-types.ts +0 -2
  155. package/data/section-templates/product-detail-section/hooks/useScrollLock.ts +0 -4
  156. package/data/section-templates/product-detail-section/ikas-config-snippet.json +16 -16
  157. package/data/section-templates/product-detail-section/index.tsx +1 -1
  158. package/data/section-templates/product-detail-section/types.ts +1 -0
  159. package/data/section-templates/product-pricing/ikas-config-snippet.json +11 -11
  160. package/data/section-templates/product-pricing/types.ts +1 -0
  161. package/data/section-templates/product-reviews-section/hooks/useScrollLock.ts +0 -4
  162. package/data/section-templates/product-reviews-section/ikas-config-snippet.json +6 -6
  163. package/data/section-templates/product-reviews-section/sub-components/Modal/index.tsx +4 -2
  164. package/data/section-templates/product-reviews-section/types.ts +1 -0
  165. package/data/section-templates/product-slider-section/children/CardProductName/ikas-config-snippet.json +2 -2
  166. package/data/section-templates/product-slider-section/children/CardProductName/types.ts +1 -0
  167. package/data/section-templates/product-slider-section/children/CardProductPrice/ikas-config-snippet.json +1 -1
  168. package/data/section-templates/product-slider-section/children/CardProductPrice/types.ts +1 -0
  169. package/data/section-templates/product-slider-section/children/CardProductVariants/ikas-config-snippet.json +1 -1
  170. package/data/section-templates/product-slider-section/children/CardProductVariants/types.ts +1 -0
  171. package/data/section-templates/product-slider-section/global-types.ts +0 -2
  172. package/data/section-templates/product-slider-section/ikas-config-snippet.json +22 -22
  173. package/data/section-templates/product-slider-section/index.tsx +4 -4
  174. package/data/section-templates/product-slider-section/sub-components/ProductCard/index.tsx +4 -4
  175. package/data/section-templates/product-slider-section/types.ts +1 -0
  176. package/data/section-templates/recover-password-section/components/RecoverPasswordForm/index.tsx +10 -10
  177. package/data/section-templates/recover-password-section/ikas-config-snippet.json +42 -42
  178. package/data/section-templates/recover-password-section/types.ts +1 -0
  179. package/data/section-templates/register-section/components/RegisterForm/index.tsx +15 -15
  180. package/data/section-templates/register-section/ikas-config-snippet.json +54 -54
  181. package/data/section-templates/register-section/types.ts +1 -0
  182. package/data/section-templates/rich-text-section/ikas-config-snippet.json +3 -3
  183. package/data/section-templates/rich-text-section/types.ts +1 -0
  184. package/data/section-templates/variant-selection/ikas-config-snippet.json +13 -13
  185. package/data/section-templates/variant-selection/types.ts +2 -1
  186. package/data/storefront-api.json +1 -1
  187. package/data/storefront-types.json +1 -1
  188. package/dist/index.js +2 -355
  189. package/dist/index.js.map +1 -1
  190. package/package.json +1 -1
  191. package/data/migration-examples/complex-header-migration/_meta.json +0 -4
  192. package/data/migration-examples/complex-header-migration/after-config-snippet.json +0 -55
  193. package/data/migration-examples/complex-header-migration/after-section.tsx +0 -64
  194. package/data/migration-examples/complex-header-migration/before-props-summary.json +0 -42
  195. package/data/migration-examples/custom-dynamic-list-to-component-list/_meta.json +0 -4
  196. package/data/migration-examples/custom-dynamic-list-to-component-list/after-child-styles.css +0 -38
  197. package/data/migration-examples/custom-dynamic-list-to-component-list/after-child.tsx +0 -22
  198. package/data/migration-examples/custom-dynamic-list-to-component-list/after-config-snippet.json +0 -31
  199. package/data/migration-examples/custom-dynamic-list-to-component-list/after-section-styles.css +0 -25
  200. package/data/migration-examples/custom-dynamic-list-to-component-list/after-section.tsx +0 -17
  201. package/data/migration-examples/custom-dynamic-list-to-component-list/before-component.tsx +0 -32
  202. package/data/migration-examples/custom-dynamic-list-to-component-list/before-theme-snippet.json +0 -53
  203. package/data/migration-examples/full-component-with-tailwind/_meta.json +0 -4
  204. package/data/migration-examples/full-component-with-tailwind/after-component.tsx +0 -43
  205. package/data/migration-examples/full-component-with-tailwind/after-config-snippet.json +0 -25
  206. package/data/migration-examples/full-component-with-tailwind/after-styles.css +0 -99
  207. package/data/migration-examples/full-component-with-tailwind/before-component.tsx +0 -60
  208. package/data/migration-examples/object-custom-to-inline-props/_meta.json +0 -4
  209. package/data/migration-examples/object-custom-to-inline-props/after-component.tsx +0 -34
  210. package/data/migration-examples/object-custom-to-inline-props/after-config-snippet.json +0 -23
  211. package/data/migration-examples/object-custom-to-inline-props/after-styles.css +0 -38
  212. package/data/migration-examples/object-custom-to-inline-props/before-component.tsx +0 -30
  213. package/data/migration-examples/object-custom-to-inline-props/before-theme-snippet.json +0 -26
  214. package/data/migration-examples/slider-library-replacement/_meta.json +0 -4
  215. package/data/migration-examples/slider-library-replacement/after-child.tsx +0 -13
  216. package/data/migration-examples/slider-library-replacement/after-config-snippet.json +0 -29
  217. package/data/migration-examples/slider-library-replacement/after-section.tsx +0 -38
  218. package/data/migration-examples/slider-library-replacement/after-styles.css +0 -43
  219. package/data/migration-examples/slider-library-replacement/before-component.tsx +0 -39
  220. package/data/migration-examples/slider-library-replacement/before-types-snippet.ts +0 -14
  221. package/data/migration.json +0 -65
@@ -1,34 +0,0 @@
1
- import { getDefaultSrc } from "@ikas/bp-storefront";
2
- import { Props } from "./types";
3
-
4
- export default function TextBannerSection({
5
- heading,
6
- content,
7
- image,
8
- buttonLabel,
9
- buttonLink,
10
- buttonBgColor,
11
- }: Props) {
12
- return (
13
- <section className="text-banner">
14
- {image && (
15
- <div className="text-banner-image-wrapper">
16
- <img src={getDefaultSrc(image)} alt={heading || ""} className="text-banner-image" />
17
- </div>
18
- )}
19
- <h2 className="text-banner-heading">{heading}</h2>
20
- {content && (
21
- <div className="text-banner-content" dangerouslySetInnerHTML={{ __html: content }} />
22
- )}
23
- {buttonLabel && (
24
- <a
25
- href={buttonLink?.href || "#"}
26
- className="text-banner-button"
27
- style={{ backgroundColor: buttonBgColor || "#000" }}
28
- >
29
- {buttonLabel}
30
- </a>
31
- )}
32
- </section>
33
- );
34
- }
@@ -1,23 +0,0 @@
1
- {
2
- "components": [
3
- {
4
- "id": "my-theme-text-banner",
5
- "name": "TextBannerSection",
6
- "type": "section",
7
- "entry": "./src/components/TextBannerSection/index.tsx",
8
- "styles": "./src/components/TextBannerSection/styles.css",
9
- "props": [
10
- { "name": "heading", "displayName": "Heading", "type": "TEXT", "required": true },
11
- { "name": "content", "displayName": "Content", "type": "RICH_TEXT", "required": false },
12
- { "name": "image", "displayName": "Image", "type": "IMAGE", "required": false },
13
- { "name": "buttonLabel", "displayName": "Button Label", "type": "TEXT", "required": false },
14
- { "name": "buttonLink", "displayName": "Button Link", "type": "LINK", "required": false },
15
- { "name": "buttonBgColor", "displayName": "Button Background", "type": "COLOR", "required": false, "defaultValue": "#000000" }
16
- ],
17
- "propGroups": [
18
- { "id": "button", "name": "Button" }
19
- ]
20
- }
21
- ],
22
- "note": "The old CUSTOM OBJECT with 3 fields (label, link, bgColor) is flattened into 3 direct props. A propGroup keeps them organized in the editor."
23
- }
@@ -1,38 +0,0 @@
1
- .text-banner {
2
- max-width: 1700px;
3
- margin: 2.5rem auto 0;
4
- padding: 0 20px;
5
- text-align: center;
6
- }
7
-
8
- .text-banner-image-wrapper {
9
- max-width: 600px;
10
- margin: 0 auto 1.5rem;
11
- aspect-ratio: 16/9;
12
- overflow: hidden;
13
- }
14
-
15
- .text-banner-image {
16
- width: 100%;
17
- height: 100%;
18
- object-fit: cover;
19
- }
20
-
21
- .text-banner-heading {
22
- font-size: 1.875rem;
23
- font-weight: 700;
24
- }
25
-
26
- .text-banner-content {
27
- max-width: 65ch;
28
- margin: 1rem auto 0;
29
- }
30
-
31
- .text-banner-button {
32
- display: inline-block;
33
- margin-top: 1.5rem;
34
- padding: 0.75rem 2rem;
35
- border-radius: 0.25rem;
36
- color: white;
37
- text-decoration: none;
38
- }
@@ -1,30 +0,0 @@
1
- import { Image } from '@ikas/storefront';
2
- import { observer } from 'mobx-react-lite';
3
- import { TextBannerProps } from '../__generated__/types';
4
-
5
- const TextBanner = ({ heading, content, image, buttonConfig }: TextBannerProps) => {
6
- return (
7
- <div className="wrapper mt-10 text-center">
8
- {image && (
9
- <div className="relative mx-auto mb-6 aspect-video max-w-[600px]">
10
- <Image image={image} layout="fill" objectFit="cover" />
11
- </div>
12
- )}
13
- <h2 className="text-3xl font-bold">{heading}</h2>
14
- {content && (
15
- <div className="prose mx-auto mt-4" dangerouslySetInnerHTML={{ __html: content }} />
16
- )}
17
- {buttonConfig?.label && (
18
- <a
19
- href={buttonConfig.link?.href || '#'}
20
- style={{ backgroundColor: buttonConfig.bgColor || '#000' }}
21
- className="mt-6 inline-block rounded px-8 py-3 text-white"
22
- >
23
- {buttonConfig.label}
24
- </a>
25
- )}
26
- </div>
27
- );
28
- };
29
-
30
- export default observer(TextBanner);
@@ -1,26 +0,0 @@
1
- {
2
- "component": {
3
- "dir": "TextBanner",
4
- "displayName": "Text Banner",
5
- "props": [
6
- { "name": "heading", "type": "TEXT", "isRequired": true },
7
- { "name": "content", "type": "RICH_TEXT", "isRequired": false },
8
- { "name": "image", "type": "IMAGE", "isRequired": false },
9
- {
10
- "name": "buttonConfig",
11
- "type": "CUSTOM",
12
- "customDataId": "button-config-id"
13
- }
14
- ]
15
- },
16
- "customData": {
17
- "id": "button-config-id",
18
- "name": "Button Config",
19
- "type": "OBJECT",
20
- "nestedData": [
21
- { "key": "label", "type": "TEXT", "name": "Button Label" },
22
- { "key": "link", "type": "LINK", "name": "Button Link" },
23
- { "key": "bgColor", "type": "COLOR", "name": "Button Background" }
24
- ]
25
- }
26
- }
@@ -1,4 +0,0 @@
1
- {
2
- "title": "Swiper Library → CSS Scroll-Snap + SLIDER → NUMBER",
3
- "description": "Converts a logo slider component that uses Swiper library and SLIDER props into a vanilla CSS scroll-snap carousel with NUMBER props."
4
- }
@@ -1,13 +0,0 @@
1
- import { getDefaultSrc } from "@ikas/bp-storefront";
2
- import { Props } from "./types";
3
-
4
- export default function LogoItem({ logo, width, mobileWidth, aspectRatio }: Props) {
5
- return (
6
- <div
7
- className="logo-item"
8
- style={{ "--w": width || "120px", "--mw": mobileWidth || "80px", aspectRatio: aspectRatio || "3/2" }}
9
- >
10
- {logo && <img src={getDefaultSrc(logo)} alt="" className="logo-image" />}
11
- </div>
12
- );
13
- }
@@ -1,29 +0,0 @@
1
- {
2
- "components": [
3
- {
4
- "id": "my-theme-logo-slider",
5
- "name": "LogoSliderSection",
6
- "type": "section",
7
- "entry": "./src/components/LogoSliderSection/index.tsx",
8
- "styles": "./src/components/LogoSliderSection/styles.css",
9
- "props": [
10
- { "name": "logos", "displayName": "Logos", "type": "COMPONENT_LIST", "required": false, "filteredComponentIds": ["my-theme-logo-item"] },
11
- { "name": "marginTop", "displayName": "Margin Top", "type": "NUMBER", "required": false, "defaultValue": 0 },
12
- { "name": "mobileMarginTop", "displayName": "Mobile Margin Top", "type": "NUMBER", "required": false, "defaultValue": 0 }
13
- ]
14
- },
15
- {
16
- "id": "my-theme-logo-item",
17
- "name": "LogoItem",
18
- "type": "component",
19
- "entry": "./src/components/LogoItem/index.tsx",
20
- "styles": "./src/components/LogoItem/styles.css",
21
- "props": [
22
- { "name": "logo", "displayName": "Logo", "type": "IMAGE", "required": true },
23
- { "name": "width", "displayName": "Width", "type": "TEXT", "required": false, "defaultValue": "120px" },
24
- { "name": "mobileWidth", "displayName": "Mobile Width", "type": "TEXT", "required": false, "defaultValue": "80px" },
25
- { "name": "aspectRatio", "displayName": "Aspect Ratio", "type": "TEXT", "required": false, "defaultValue": "3/2" }
26
- ]
27
- }
28
- ]
29
- }
@@ -1,38 +0,0 @@
1
- import { useRef, useEffect } from "preact/hooks";
2
- import { IkasComponentRenderer } from "@ikas/bp-storefront";
3
- import { Props } from "./types";
4
-
5
- export default function LogoSliderSection({ logos, marginTop, mobileMarginTop, ...props }: Props) {
6
- const trackRef = useRef<HTMLDivElement>(null);
7
-
8
- useEffect(() => {
9
- const track = trackRef.current;
10
- if (!track) return;
11
-
12
- let animationId: number;
13
- let scrollPos = 0;
14
- const speed = 0.5;
15
-
16
- const animate = () => {
17
- scrollPos += speed;
18
- if (scrollPos >= track.scrollWidth / 2) scrollPos = 0;
19
- track.scrollLeft = scrollPos;
20
- animationId = requestAnimationFrame(animate);
21
- };
22
-
23
- animationId = requestAnimationFrame(animate);
24
- return () => cancelAnimationFrame(animationId);
25
- }, []);
26
-
27
- return (
28
- <section
29
- className="logo-slider"
30
- style={{ "--mt": `${marginTop || 0}px`, "--mt-mobile": `${mobileMarginTop || 0}px` }}
31
- >
32
- <div ref={trackRef} className="logo-track">
33
- <IkasComponentRenderer id="logos" components={logos as any[]} parentProps={props} />
34
- <IkasComponentRenderer id="logos-dup" components={logos as any[]} parentProps={props} />
35
- </div>
36
- </section>
37
- );
38
- }
@@ -1,43 +0,0 @@
1
- .logo-slider {
2
- margin-top: var(--mt-mobile, 0);
3
- width: 100%;
4
- overflow: hidden;
5
- border-top: 1px solid #E3E3E3;
6
- border-bottom: 1px solid #E3E3E3;
7
- }
8
-
9
- @media (min-width: 1024px) {
10
- .logo-slider {
11
- margin-top: var(--mt, 0);
12
- }
13
- }
14
-
15
- .logo-track {
16
- display: flex;
17
- gap: 30px;
18
- overflow: hidden;
19
- }
20
-
21
- @media (min-width: 1024px) {
22
- .logo-track {
23
- gap: 134px;
24
- }
25
- }
26
-
27
- .logo-item {
28
- flex: 0 0 auto;
29
- width: var(--mw, 80px);
30
- user-select: none;
31
- }
32
-
33
- @media (min-width: 1024px) {
34
- .logo-item {
35
- width: var(--w, 120px);
36
- }
37
- }
38
-
39
- .logo-image {
40
- width: 100%;
41
- height: 100%;
42
- object-fit: contain;
43
- }
@@ -1,39 +0,0 @@
1
- import { Image } from '@ikas/storefront';
2
- import { observer } from 'mobx-react-lite';
3
- import { Autoplay } from 'swiper/modules';
4
- import { Swiper, SwiperSlide } from 'swiper/react';
5
- import { LogoSliderProps } from '../__generated__/types';
6
-
7
- const LogoSlider = ({ content, marginTop, mobileMarginTop }: LogoSliderProps) => {
8
- return (
9
- <div
10
- style={{
11
- '--mth': `${marginTop?.value || 0}px`,
12
- '--mthm': `${mobileMarginTop?.value || 0}px`,
13
- } as React.CSSProperties}
14
- className="mt-[var(--mthm)] w-full overflow-hidden border-y border-[#E3E3E3] lg:mt-[var(--mth)]"
15
- >
16
- <Swiper
17
- modules={[Autoplay]}
18
- centerInsufficientSlides
19
- loop={true}
20
- slidesPerView="auto"
21
- spaceBetween={30}
22
- autoplay={{ delay: 3000, disableOnInteraction: false }}
23
- breakpoints={{ 1024: { spaceBetween: 134 } }}
24
- >
25
- {content.map((item) => (
26
- <SwiperSlide
27
- style={{ aspectRatio: item.aspect, '--w': item.width, '--mw': item.mobWidth || item.width } as React.CSSProperties}
28
- className="relative my-auto !w-[--mw] select-none lg:!w-[--w]"
29
- key={item.logo.id}
30
- >
31
- <Image image={item.logo} layout="fill" objectFit="contain" sizes={item.width} />
32
- </SwiperSlide>
33
- ))}
34
- </Swiper>
35
- </div>
36
- );
37
- };
38
-
39
- export default observer(LogoSlider);
@@ -1,14 +0,0 @@
1
- import { IkasSlider, IkasImage } from '@ikas/storefront';
2
-
3
- export type LogoSlider = {
4
- logo: IkasImage;
5
- width: string;
6
- mobWidth: string;
7
- aspect: string;
8
- };
9
-
10
- export type LogoSliderProps = {
11
- content: LogoSlider[];
12
- marginTop: IkasSlider;
13
- mobileMarginTop: IkasSlider;
14
- };
@@ -1,65 +0,0 @@
1
- {
2
- "generatedAt": "2026-04-13T00:00:00.000Z",
3
- "topics": {
4
- "migration-overview": {
5
- "title": "Old Theme → Code Component Migration Overview",
6
- "description": "End-to-end workflow for converting an old ikas storefront theme into a new code-component project",
7
- "content": "This guide covers converting themes built with the old ikas storefront system (React, theme.json, @ikas/storefront, Tailwind CSS) into the new code-component system (Preact, ikas.config.json, @ikas/bp-storefront, scoped CSS).\n\n## CRITICAL: The Two Systems Are Completely Different\n\n**The old storefront system (`@ikas/storefront`) and the new code-component system (`@ikas/bp-storefront`) are entirely separate systems with different packages, different data types, different APIs, and different runtime behaviors.** Even when type names look the same (e.g., `IkasImage`, `IkasProduct`, `IkasNavigationLink`), they are **different types from different packages** with potentially different properties and methods.\n\n**NEVER assume** that because a type exists in the old system, it works the same way in the new system:\n- `IkasImage` from `@ikas/storefront` ≠ `IkasImage` from `@ikas/bp-storefront` — different properties, different helper functions\n- `IkasProduct` from `@ikas/storefront` ≠ `IkasProduct` from `@ikas/bp-storefront` — different data shape\n- `IkasNavigationLink` from `@ikas/storefront` ≠ `IkasNavigationLink` from `@ikas/bp-storefront` — different structure\n- `IkasSlider` from `@ikas/storefront` → **does not exist** in the new system (replaced by plain `number`)\n- The old `<Image>` component → **does not exist** in the new system (use `getDefaultSrc()` + native `<img>`)\n- The old `useStore()` hook → **does not exist** in the new system (import stores directly)\n- The old `observer()` from `mobx-react-lite` → **must not be used** on root components (use `observer` from `@ikas/component-utils` only on sub-components)\n\n**Similarly, the prop type systems are completely different:**\n- Old system prop types (in theme.json) are defined by `IkasThemeJsonComponentPropType` enum — a different set of strings with different semantics\n- New system prop types (in ikas.config.json) are defined by the code-component `PropType` enum\n- Prop types with the same name (e.g., `TEXT`, `IMAGE`, `BOOLEAN`) may seem identical but produce **different TypeScript types** and have **different editor UI behaviors**\n- The old system has types that don't exist in the new system (`SLIDER`, `CUSTOM`, `PRODUCT_DETAIL`)\n- The new system has types that don't exist in the old system (`NUMBER`, `NUMBER_RANGE`, `DATE`, `TYPE`, `FUNCTION`, `COMPONENT`)\n\n**Always use the MCP server's `get_prop_types`, `get_type_definition`, `get_function_doc`, and `get_model_guide` tools** to look up the correct new-system types, functions, and APIs. Never copy old-system type usage into new-system code.\n\n## Migration Workflow\n\n### Step 1: Analyze the Old Theme\nCall `analyze_old_theme` with the old theme.json content. This produces:\n- A list of all components with their prop types\n- A map of all customData definitions (DYNAMIC_LIST, OBJECT, STATIC_LIST, ENUM)\n- Migration recommendations per component (what becomes a section, what becomes a child component)\n- Statistics (total props, CUSTOM props, SLIDER props, estimated child components)\n\n### Step 2: Plan the New Project Structure\nBased on the analysis:\n1. Each old component typically maps to a **section** in the new system\n2. Components with CUSTOM (DYNAMIC_LIST) props need **child components** — each DYNAMIC_LIST becomes a COMPONENT_LIST prop on the parent section, with a child component representing one item\n3. Components marked as header/footer get `isHeader: true` / `isFooter: true`\n4. Simple CUSTOM (OBJECT) props with ≤3 fields can be flattened into direct props on the parent\n5. Create custom enums for any ENUM-like customData\n\n### Step 3: Generate the Complete ikas.config.json\nCreate the full config with:\n- All sections (from old components)\n- All child components (from CUSTOM DYNAMIC_LIST props)\n- All custom enums (from ENUM customData)\n- All prop groups (from old groups)\n- Correct prop type mappings (see `get_migration_guide(\"prop-type-mapping\")`)\n\n### Step 4: Generate global.css\nConvert old theme settings to CSS variables:\n```css\n:root {\n /* From settings.colors */\n --primary: #fed9d9;\n --button-bg-1: #000000;\n /* From settings.fontFamily */\n font-family: 'Quicksand', sans-serif;\n}\n```\n\n### Step 5: Generate Component Source Files\nFor each component, generate:\n- `index.tsx` — Preact component (see `get_migration_guide(\"react-to-preact\")`)\n- `styles.css` — Scoped CSS (convert Tailwind to plain CSS)\n\nKey conversion rules:\n- Replace `@ikas/storefront` imports with `@ikas/bp-storefront`\n- Replace `observer(Component)` exports with plain `export default function Component`\n- Replace Swiper/Headless UI/etc. with vanilla implementations (see `get_migration_guide(\"library-replacements\")`)\n- Replace Tailwind classes with scoped CSS\n- Replace `IkasSlider` (old SLIDER type) with `number`\n- Replace `IkasComponentRenderer` from old system with `IkasComponentRenderer` from `@ikas/bp-storefront`\n\n### Step 6: Generate Sub-Components\nShared UI pieces (buttons, modals, cards) go in `src/sub-components/`. These are NOT registered in ikas.config.json — they're internal helpers imported by registered components.\n\n### Step 7: Build and Verify\nRun `npx ikas-component build` to compile. Fix any TypeScript or build errors.\n\n## Key Differences Summary\n\n| Old System | New System |\n|-----------|------------|\n| React 18 | Preact 10 |\n| @ikas/storefront | @ikas/bp-storefront |\n| theme.json | ikas.config.json |\n| Tailwind CSS | Scoped plain CSS |\n| External libraries allowed | No external libraries |\n| CUSTOM + customData | COMPONENT_LIST + child components |\n| SLIDER prop (IkasSlider) | NUMBER prop (number) |\n| PRODUCT_DETAIL | PRODUCT |\n| observer() on all components | observer() only on sub-components |\n| CSS Modules / Tailwind | .cc_{id} scoped CSS |\n| Next.js pages | Sections composed in editor |",
8
- "tags": ["migration", "overview", "workflow", "convert", "old-theme", "theme-json"]
9
- },
10
- "prop-type-mapping": {
11
- "title": "Prop Type Mapping: Old → New",
12
- "description": "Complete mapping of old theme.json prop types to new ikas.config.json prop types",
13
- "content": "## IMPORTANT: Different Systems, Different Types\n\n**The old prop type system (theme.json) and the new prop type system (ikas.config.json) are completely separate.** Even when a prop type has the same name in both systems (e.g., `IMAGE`, `LINK`, `PRODUCT_LIST`), the underlying TypeScript types come from different packages (`@ikas/storefront` vs `@ikas/bp-storefront`) and may have different properties, methods, and nullability. Always use the MCP tools (`get_type_definition`, `get_model_guide`) to look up the exact new-system types — never assume old-system knowledge applies.\n\n## Direct Mappings (1:1)\n\nThese prop types have similar equivalents, but remember the TypeScript types are from different packages:\n\n| Old Type | New Type | TypeScript (Old) | TypeScript (New) | Notes |\n|----------|----------|-----------------|-----------------|-------|\n| `TEXT` | `TEXT` | `string` | `string` | |\n| `RICH_TEXT` | `RICH_TEXT` | `string` | `string` | |\n| `BOOLEAN` | `BOOLEAN` | `boolean` | `boolean` | |\n| `IMAGE` | `IMAGE` | `IkasImage` | `IkasImage \\| null` | New type is nullable |\n| `IMAGE_LIST` | `IMAGE_LIST` | `IkasImage[]` | `IkasImageList` | Slight type difference |\n| `LINK` | `LINK` | `IkasNavigationLink` | `IkasNavigationLink` | |\n| `LIST_OF_LINK` | `LIST_OF_LINK` | `IkasNavigationLink[]` | `IkasNavigationLinkList` | Slight type difference |\n| `COLOR` | `COLOR` | `string` | `string` | |\n| `VIDEO` | `VIDEO` | `IkasVideo` | `IkasVideo \\| null` | New type is nullable |\n| `PRODUCT_LIST` | `PRODUCT_LIST` | `IkasProductList` | `IkasProductList` | |\n| `CATEGORY` | `CATEGORY` | `IkasCategory` | `IkasCategory \\| null` | |\n| `CATEGORY_LIST` | `CATEGORY_LIST` | `IkasCategoryList` | `IkasCategoryList` | |\n| `BRAND` | `BRAND` | `IkasBrand` | `IkasBrand \\| null` | |\n| `BRAND_LIST` | `BRAND_LIST` | — | `IkasBrandList` | |\n| `BLOG` | `BLOG` | `IkasBlog` | `IkasBlog \\| null` | |\n| `BLOG_LIST` | `BLOG_LIST` | `IkasBlogList` | `IkasBlogList` | |\n| `BLOG_CATEGORY` | `BLOG_CATEGORY` | `IkasBlogCategory` | `IkasBlogCategory \\| null` | |\n| `BLOG_CATEGORY_LIST` | `BLOG_CATEGORY_LIST` | `IkasBlogCategoryList` | `IkasBlogCategoryList` | |\n| `PRODUCT_ATTRIBUTE` | `PRODUCT_ATTRIBUTE` | `IkasAttributeDetail` | `IkasProductAttributeValue \\| null` | Type renamed |\n| `PRODUCT_ATTRIBUTE_LIST` | `PRODUCT_ATTRIBUTE_LIST` | `IkasAttributeList` | `IkasProductAttributeValue[]` | Type renamed |\n| `COMPONENT_LIST` | `COMPONENT_LIST` | `IkasComponentRenderer` | `any` (rendered via `<IkasComponentRenderer>`) | |\n\n## Renamed Types\n\n| Old Type | New Type | Notes |\n|----------|----------|-------|\n| `PRODUCT_DETAIL` | `PRODUCT` | Same concept, just renamed. Old: `IkasProduct`, New: `IkasProduct \\| null` |\n\n## Types Requiring Conversion\n\n### SLIDER → NUMBER\nOld `SLIDER` type used `IkasSlider` with `{ value: number }`. The new system uses `NUMBER` which maps to plain `number`.\n\n**Old theme.json:**\n```json\n{\n \"name\": \"logoWidth\",\n \"type\": \"SLIDER\",\n \"sliderData\": { \"min\": 80, \"max\": 300, \"interval\": 1 }\n}\n```\n\n**New ikas.config.json:**\n```json\n{\n \"name\": \"logoWidth\",\n \"displayName\": \"Logo Width\",\n \"type\": \"NUMBER\",\n \"required\": false,\n \"defaultValue\": 120\n}\n```\n\n**Code change:** Replace `logoWidth?.value` or `logoWidth.value` with just `logoWidth` (it's already a number).\n\n### CUSTOM → Multiple approaches\nSee `get_migration_guide(\"custom-data-conversion\")` for the full guide. Summary:\n\n| Old customData type | New approach |\n|--------------------|--------------|\n| DYNAMIC_LIST (list of objects) | `COMPONENT_LIST` + child component |\n| OBJECT (≤3 simple fields) | Flatten into direct props on parent |\n| OBJECT (>3 fields or complex) | `COMPONENT_LIST` + child component |\n| STATIC_LIST (fixed count items) | `COMPONENT_LIST` (store owner manages count) |\n| ENUM (enumOptions) | `ENUM` with custom enum via `config add-enum` |\n\n### ENUM → ENUM\nOld ENUM type with `enumOptions` maps to new ENUM with `enumTypeId`. Create custom enums first:\n```bash\nnpx ikas-component config add-enum --name \"AspectRatio\" --options '{\"Square\":\"1:1\",\"Landscape\":\"16:9\",\"Portrait\":\"3:4\"}'\n# Use returned enumId in prop definition\n```\n\n## Types Not Available in New System\n\nThese old types do not exist in the new system and need workarounds:\n\n| Old Type | Workaround |\n|----------|------------|\n| `SLIDER` | Use `NUMBER`. Access value directly instead of `.value` |\n| `OBJECT` (customData) | Flatten into parent props or use COMPONENT_LIST |\n| `STATIC_LIST` (customData) | Use COMPONENT_LIST |\n\n## New Types Not in Old System\n\n| New Type | Description |\n|----------|-------------|\n| `NUMBER` | Numeric input (replaces SLIDER) |\n| `NUMBER_RANGE` | Two-number range (IkasNumberRange) |\n| `DATE` | Date picker |\n| `FUNCTION` | Function prop (callback) |\n| `TYPE` | Structured style type (padding, margin, etc.) |\n| `COMPONENT` | Single child component slot |\n| `RAFFLE` / `RAFFLE_LIST` | Raffle references |",
14
- "tags": ["migration", "props", "types", "mapping", "SLIDER", "CUSTOM", "PRODUCT_DETAIL", "conversion"]
15
- },
16
- "custom-data-conversion": {
17
- "title": "Converting CUSTOM Props and customData",
18
- "description": "How to convert old CUSTOM prop types with customData definitions to new COMPONENT_LIST + child components",
19
- "content": "The old system's `CUSTOM` prop type + `customData` definitions are the most complex migration. Each approach depends on the customData structure.\n\n## Decision Tree\n\n1. **Is it a DYNAMIC_LIST?** → Use `COMPONENT_LIST` + child component\n2. **Is it an OBJECT with ≤3 simple fields?** → Flatten into direct props on parent\n3. **Is it an OBJECT with >3 fields or complex nested types?** → Use `COMPONENT_LIST` + child component\n4. **Is it a STATIC_LIST?** → Use `COMPONENT_LIST` (store owner manages item count)\n5. **Is it an ENUM?** → Use `ENUM` prop with custom enum\n6. **Does it have nested DYNAMIC_LIST inside?** → Use nested COMPONENT_LIST or flatten\n\n## Example 1: DYNAMIC_LIST → COMPONENT_LIST + Child Component\n\nThis is the most common conversion. A list of custom items becomes a section with a COMPONENT_LIST prop and a child component.\n\n### Old System\n\n**customData definition (in theme.json):**\n```json\n{\n \"id\": \"b361dc09-...\",\n \"name\": \"Mağazalarımız\",\n \"type\": \"DYNAMIC_LIST\",\n \"nestedData\": [{\n \"name\": \"Mağaza\",\n \"typescriptName\": \"Maaza\",\n \"type\": \"OBJECT\",\n \"nestedData\": [\n { \"name\": \"Başlık\", \"type\": \"TEXT\", \"key\": \"title\" },\n { \"name\": \"Lokasyon\", \"type\": \"TEXT\", \"key\": \"location\" },\n { \"name\": \"Görsel\", \"type\": \"IMAGE\", \"key\": \"image\" }\n ]\n }]\n}\n```\n\n**Component prop (in theme.json):**\n```json\n{ \"name\": \"stores\", \"type\": \"CUSTOM\", \"customDataId\": \"b361dc09-...\" }\n```\n\n**Old generated type:** `stores: Maaza[]` where `Maaza = { title: string; location: string; image?: IkasImage }`\n\n**Old React code:**\n```tsx\n{stores.map((store, i) => (\n <div key={i}>\n <Image image={store.image} layout=\"fill\" />\n <h3>{store.title}</h3>\n <p>{store.location}</p>\n </div>\n))}\n```\n\n### New System\n\n**ikas.config.json — Parent section:**\n```json\n{\n \"id\": \"my-project-stores-section\",\n \"name\": \"StoresSection\",\n \"type\": \"section\",\n \"entry\": \"./src/components/StoresSection/index.tsx\",\n \"styles\": \"./src/components/StoresSection/styles.css\",\n \"props\": [\n {\n \"name\": \"stores\",\n \"displayName\": \"Stores\",\n \"type\": \"COMPONENT_LIST\",\n \"required\": false,\n \"filteredComponentIds\": [\"my-project-store-card\"]\n }\n ]\n}\n```\n\n**ikas.config.json — Child component:**\n```json\n{\n \"id\": \"my-project-store-card\",\n \"name\": \"StoreCard\",\n \"type\": \"component\",\n \"entry\": \"./src/components/StoreCard/index.tsx\",\n \"styles\": \"./src/components/StoreCard/styles.css\",\n \"props\": [\n { \"name\": \"title\", \"displayName\": \"Title\", \"type\": \"TEXT\", \"required\": true },\n { \"name\": \"location\", \"displayName\": \"Location\", \"type\": \"TEXT\", \"required\": true },\n { \"name\": \"image\", \"displayName\": \"Image\", \"type\": \"IMAGE\", \"required\": false }\n ]\n}\n```\n\n**New Preact section (StoresSection/index.tsx):**\n```tsx\nimport { IkasComponentRenderer } from \"@ikas/bp-storefront\";\nimport { Props } from \"./types\";\n\nexport default function StoresSection({ stores, ...props }: Props) {\n return (\n <section className=\"stores-section\">\n <IkasComponentRenderer\n id=\"stores\"\n components={stores as any[]}\n parentProps={props}\n />\n </section>\n );\n}\n```\n\n**New Preact child (StoreCard/index.tsx):**\n```tsx\nimport { getDefaultSrc } from \"@ikas/bp-storefront\";\nimport { Props } from \"./types\";\n\nexport default function StoreCard({ title, location, image }: Props) {\n return (\n <div className=\"store-card\">\n {image && <img src={getDefaultSrc(image)} alt={title} className=\"store-image\" />}\n <h3 className=\"store-title\">{title}</h3>\n <p className=\"store-location\">{location}</p>\n </div>\n );\n}\n```\n\n## Example 2: Simple OBJECT → Flatten into Parent Props\n\nWhen a CUSTOM prop references an OBJECT with just 2-3 simple fields, it's cleaner to flatten them into the parent component.\n\n### Old System\n```json\n// customData:\n{ \"name\": \"TextData\", \"type\": \"OBJECT\", \"nestedData\": [\n { \"key\": \"title\", \"type\": \"TEXT\" },\n { \"key\": \"color\", \"type\": \"COLOR\" }\n]}\n// Component prop:\n{ \"name\": \"textData\", \"type\": \"CUSTOM\", \"customDataId\": \"...\" }\n```\n\n### New System — Just use direct props:\n```json\n{\n \"props\": [\n { \"name\": \"title\", \"displayName\": \"Title\", \"type\": \"TEXT\" },\n { \"name\": \"titleColor\", \"displayName\": \"Title Color\", \"type\": \"COLOR\" }\n ]\n}\n```\n\n## Example 3: Nested DYNAMIC_LIST → Nested COMPONENT_LIST\n\nWhen customData has a DYNAMIC_LIST inside an OBJECT (e.g., menu links with sub-links), you need nested component relationships.\n\n### Old System\n```json\n// MenLink OBJECT has: mainlink (LINK), sublinks (DYNAMIC_LIST of AltLink)\n// AltLink OBJECT has: links (LIST_OF_LINK), cols (SLIDER)\n```\n\n### New System\nCreate three levels:\n1. **NavSection** (section) — has `menuLinks` as COMPONENT_LIST\n2. **MenuLink** (component) — has `mainLink` (LINK), `subLinks` (COMPONENT_LIST)\n3. **SubLink** (component) — has `links` (LIST_OF_LINK), `cols` (NUMBER)\n\nEach level is a registered component in ikas.config.json with its own props.\n\n## Example 4: ENUM customData → ENUM prop\n\n### Old System\n```json\n// customData:\n{ \"name\": \"Input Type\", \"type\": \"ENUM\", \"enumOptions\": [\n { \"displayName\": \"Email\", \"value\": \"email\" },\n { \"displayName\": \"Text\", \"value\": \"text\" },\n { \"displayName\": \"Textarea\", \"value\": \"textarea\" }\n]}\n```\n\n### New System\n```bash\n# First create the enum:\nnpx ikas-component config add-enum --name \"InputType\" --options '{\"Email\":\"email\",\"Text\":\"text\",\"Textarea\":\"textarea\"}'\n# Returns: { \"enumId\": \"aBcDeFgH\" }\n\n# Then use in prop:\n{ \"name\": \"inputType\", \"type\": \"ENUM\", \"enumTypeId\": \"aBcDeFgH\" }\n```\n\n## Naming Convention for Child Components\n\nWhen decomposing an old component into section + children:\n- Parent section: `{OldComponentName}Section` (e.g., `StoresSection`)\n- Child component: `{OldTypescriptName}` or `{OldComponentName}{ItemName}` (e.g., `StoreCard`)\n- Use `filteredComponentIds` on the COMPONENT_LIST prop to restrict which children can be placed there\n\n## Common Patterns\n\n### Slider/Carousel items\nOld: CUSTOM (DYNAMIC_LIST) with image + text per slide\nNew: Section with COMPONENT_LIST + SlideItem child component\n\n### Tab groups with content\nOld: CUSTOM (DYNAMIC_LIST) with tab name + content per tab\nNew: Section with COMPONENT_LIST + TabItem child component\n\n### Footer link columns\nOld: CUSTOM (DYNAMIC_LIST) with title + links per column\nNew: Section with COMPONENT_LIST + FooterColumn child component\n\n### Comments/Reviews\nOld: CUSTOM (DYNAMIC_LIST) with star + name + comment per review\nNew: Section with COMPONENT_LIST + ReviewItem child component",
20
- "tags": ["migration", "CUSTOM", "customData", "DYNAMIC_LIST", "OBJECT", "STATIC_LIST", "COMPONENT_LIST", "child-component", "conversion"]
21
- },
22
- "library-replacements": {
23
- "title": "Library Replacement Patterns",
24
- "description": "How to replace common third-party libraries with vanilla Preact + CSS implementations",
25
- "content": "The new code-component system does not support external libraries. Here's how to replace each common library found in old themes.\n\n## swiper → CSS Scroll-Snap Carousel\n\n**What it does:** Touch-enabled carousel/slider with autoplay, pagination, navigation.\n\n**Replacement pattern:**\n```tsx\nimport { useRef, useState, useEffect } from \"preact/hooks\";\n\nfunction Carousel({ children }: { children: any[] }) {\n const trackRef = useRef<HTMLDivElement>(null);\n const [current, setCurrent] = useState(0);\n\n useEffect(() => {\n const track = trackRef.current;\n if (!track) return;\n const handleScroll = () => {\n const index = Math.round(track.scrollLeft / track.clientWidth);\n setCurrent(index);\n };\n track.addEventListener(\"scroll\", handleScroll, { passive: true });\n return () => track.removeEventListener(\"scroll\", handleScroll);\n }, []);\n\n const goTo = (index: number) => {\n trackRef.current?.scrollTo({ left: index * trackRef.current.clientWidth, behavior: \"smooth\" });\n };\n\n return (\n <div className=\"carousel\">\n <div ref={trackRef} className=\"carousel-track\">\n {children}\n </div>\n <div className=\"carousel-dots\">\n {children.map((_, i) => (\n <button\n key={i}\n className={`carousel-dot ${i === current ? \"active\" : \"\"}`}\n onClick={() => goTo(i)}\n />\n ))}\n </div>\n </div>\n );\n}\n```\n\n**CSS:**\n```css\n.carousel-track {\n display: flex;\n overflow-x: auto;\n scroll-snap-type: x mandatory;\n scrollbar-width: none;\n -webkit-overflow-scrolling: touch;\n}\n.carousel-track::-webkit-scrollbar { display: none; }\n.carousel-track > * {\n flex: 0 0 100%;\n scroll-snap-align: start;\n}\n.carousel-dot {\n width: 8px; height: 8px; border-radius: 50%;\n background: #ccc; border: none; cursor: pointer;\n}\n.carousel-dot.active { background: #333; }\n```\n\n**For autoplay**, add a `useEffect` with `setInterval` that calls `goTo((current + 1) % count)`.\n\n**For `slidesPerView: \"auto\"`**, remove `flex: 0 0 100%` and let children size themselves.\n\n## @headlessui/react → Custom Preact Components\n\n**Disclosure (accordion):**\n```tsx\nimport { useState } from \"preact/hooks\";\n\nfunction Disclosure({ title, children }: { title: string; children: any }) {\n const [open, setOpen] = useState(false);\n return (\n <div>\n <button onClick={() => setOpen(!open)} className=\"disclosure-btn\">\n {title}\n <span className={`arrow ${open ? \"open\" : \"\"}`}>▸</span>\n </button>\n {open && <div className=\"disclosure-panel\">{children}</div>}\n </div>\n );\n}\n```\n\n**Dialog (modal):**\n```tsx\nimport { useEffect, useRef } from \"preact/hooks\";\n\nfunction Dialog({ open, onClose, children }: { open: boolean; onClose: () => void; children: any }) {\n const overlayRef = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n if (open) document.body.style.overflow = \"hidden\";\n else document.body.style.overflow = \"\";\n return () => { document.body.style.overflow = \"\"; };\n }, [open]);\n\n if (!open) return null;\n return (\n <div ref={overlayRef} className=\"dialog-overlay\" onClick={(e) => { if (e.target === overlayRef.current) onClose(); }}>\n <div className=\"dialog-content\">{children}</div>\n </div>\n );\n}\n```\n\n## recharts → SVG or CSS Charts\n\nFor simple bar/line charts, use inline SVG. For complex charts, consider CSS-based bar charts:\n```tsx\nfunction BarChart({ data }: { data: { label: string; value: number }[] }) {\n const max = Math.max(...data.map(d => d.value));\n return (\n <div className=\"bar-chart\">\n {data.map((d, i) => (\n <div key={i} className=\"bar-item\">\n <div className=\"bar\" style={{ height: `${(d.value / max) * 100}%` }} />\n <span className=\"bar-label\">{d.label}</span>\n </div>\n ))}\n </div>\n );\n}\n```\n\n## react-player → Native Video/Iframe\n\n```tsx\nfunction VideoPlayer({ url, poster }: { url: string; poster?: string }) {\n if (url.includes(\"youtube.com\") || url.includes(\"youtu.be\")) {\n const videoId = url.match(/(?:v=|youtu\\.be\\/)([^&]+)/)?.[1];\n return <iframe src={`https://www.youtube.com/embed/${videoId}`} allowFullScreen className=\"video-iframe\" />;\n }\n if (url.includes(\"vimeo.com\")) {\n const videoId = url.match(/vimeo\\.com\\/(\\d+)/)?.[1];\n return <iframe src={`https://player.vimeo.com/video/${videoId}`} allowFullScreen className=\"video-iframe\" />;\n }\n return <video src={url} poster={poster} controls className=\"video-native\" />;\n}\n```\n\n## react-simple-star-rating → CSS Star Rating\n\n```tsx\nfunction StarRating({ rating, max = 5 }: { rating: number; max?: number }) {\n return (\n <div className=\"star-rating\">\n {Array.from({ length: max }, (_, i) => (\n <span key={i} className={`star ${i < Math.round(rating) ? \"filled\" : \"\"}`}>★</span>\n ))}\n </div>\n );\n}\n```\n\n```css\n.star { color: #ddd; font-size: 1.2em; }\n.star.filled { color: #ffc107; }\n```\n\n## react-slider / react-compound-slider → Native Range Input\n\n```tsx\nfunction RangeSlider({ min, max, value, onChange }: { min: number; max: number; value: number; onChange: (v: number) => void }) {\n return (\n <input\n type=\"range\"\n min={min}\n max={max}\n value={value}\n onInput={(e) => onChange(Number((e.target as HTMLInputElement).value))}\n className=\"range-slider\"\n />\n );\n}\n```\n\n## react-hot-toast → Simple Toast\n\n```tsx\nimport { useState, useEffect } from \"preact/hooks\";\n\nfunction Toast({ message, duration = 3000, onClose }: { message: string; duration?: number; onClose: () => void }) {\n useEffect(() => {\n const timer = setTimeout(onClose, duration);\n return () => clearTimeout(timer);\n }, [duration, onClose]);\n\n return <div className=\"toast\">{message}</div>;\n}\n```\n\n```css\n.toast {\n position: fixed; bottom: 20px; right: 20px;\n background: #333; color: white; padding: 12px 24px;\n border-radius: 8px; z-index: 9999;\n animation: slideIn 0.3s ease;\n}\n@keyframes slideIn { from { transform: translateY(20px); opacity: 0; } }\n```\n\n## react-fast-marquee → CSS Marquee\n\n```css\n.marquee-container { overflow: hidden; }\n.marquee-track {\n display: flex; width: max-content;\n animation: marquee 20s linear infinite;\n}\n@keyframes marquee {\n 0% { transform: translateX(0); }\n 100% { transform: translateX(-50%); }\n}\n```\nDuplicate the content so it loops seamlessly.\n\n## react-indiana-drag-scroll → CSS Overflow Scroll\n\n```css\n.drag-scroll {\n overflow-x: auto;\n -webkit-overflow-scrolling: touch;\n scrollbar-width: none;\n cursor: grab;\n}\n.drag-scroll::-webkit-scrollbar { display: none; }\n```\n\n## react-simple-typewriter → setInterval Typewriter\n\n```tsx\nimport { useState, useEffect } from \"preact/hooks\";\n\nfunction Typewriter({ words, speed = 100 }: { words: string[]; speed?: number }) {\n const [text, setText] = useState(\"\");\n const [wordIndex, setWordIndex] = useState(0);\n const [charIndex, setCharIndex] = useState(0);\n const [deleting, setDeleting] = useState(false);\n\n useEffect(() => {\n const word = words[wordIndex];\n const timer = setTimeout(() => {\n if (!deleting) {\n setText(word.slice(0, charIndex + 1));\n if (charIndex + 1 === word.length) {\n setTimeout(() => setDeleting(true), 1500);\n } else {\n setCharIndex(c => c + 1);\n }\n } else {\n setText(word.slice(0, charIndex));\n if (charIndex === 0) {\n setDeleting(false);\n setWordIndex((i) => (i + 1) % words.length);\n } else {\n setCharIndex(c => c - 1);\n }\n }\n }, deleting ? speed / 2 : speed);\n return () => clearTimeout(timer);\n }, [charIndex, deleting, wordIndex, words, speed]);\n\n return <span>{text}<span className=\"cursor\">|</span></span>;\n}\n```\n\n## react-timer-hook → useEffect Countdown\n\n```tsx\nimport { useState, useEffect } from \"preact/hooks\";\n\nfunction useCountdown(targetDate: Date) {\n const [timeLeft, setTimeLeft] = useState(getTimeLeft(targetDate));\n\n useEffect(() => {\n const interval = setInterval(() => setTimeLeft(getTimeLeft(targetDate)), 1000);\n return () => clearInterval(interval);\n }, [targetDate]);\n\n return timeLeft;\n}\n\nfunction getTimeLeft(target: Date) {\n const diff = Math.max(0, target.getTime() - Date.now());\n return {\n days: Math.floor(diff / 86400000),\n hours: Math.floor((diff % 86400000) / 3600000),\n minutes: Math.floor((diff % 3600000) / 60000),\n seconds: Math.floor((diff % 60000) / 1000),\n };\n}\n```\n\n## date-fns → Native Intl / Inline Utils\n\n```tsx\nfunction formatDate(date: Date | string, locale = \"tr-TR\"): string {\n return new Intl.DateTimeFormat(locale, { day: \"numeric\", month: \"long\", year: \"numeric\" }).format(new Date(date));\n}\n\nfunction formatRelative(date: Date | string): string {\n const diff = Date.now() - new Date(date).getTime();\n const days = Math.floor(diff / 86400000);\n if (days === 0) return \"Today\";\n if (days === 1) return \"Yesterday\";\n if (days < 7) return `${days} days ago`;\n return formatDate(date);\n}\n```\n\n## slugify → Inline Slugify\n\n```tsx\nfunction slugify(text: string): string {\n return text\n .toLowerCase()\n .normalize(\"NFD\")\n .replace(/[\\u0300-\\u036f]/g, \"\")\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-|-$/g, \"\");\n}\n```\n\n## classnames → Template Literals\n\nOld: `classnames('base', { active: isActive, hidden: !visible })`\nNew: `` `base ${isActive ? 'active' : ''} ${!visible ? 'hidden' : ''}`.trim() ``\n\nOr a simple helper:\n```tsx\nfunction cn(...classes: (string | false | undefined | null)[]): string {\n return classes.filter(Boolean).join(\" \");\n}\n```\n\n## tailwindcss → Plain CSS\n\nConvert Tailwind utility classes to equivalent CSS. Common patterns:\n\n| Tailwind | CSS |\n|----------|-----|\n| `flex` | `display: flex` |\n| `grid grid-cols-2` | `display: grid; grid-template-columns: repeat(2, 1fr)` |\n| `gap-4` | `gap: 1rem` |\n| `p-6` | `padding: 1.5rem` |\n| `mt-4` | `margin-top: 1rem` |\n| `text-center` | `text-align: center` |\n| `text-[20px]` | `font-size: 20px` |\n| `font-light` | `font-weight: 300` |\n| `rounded-lg` | `border-radius: 0.5rem` |\n| `hidden lg:block` | `display: none; @media (min-width: 1024px) { display: block; }` |\n| `hover:opacity-80` | `.class:hover { opacity: 0.8; }` |\n| `transition-all` | `transition: all 0.3s ease` |\n| `absolute inset-0` | `position: absolute; top: 0; right: 0; bottom: 0; left: 0` |\n| `overflow-hidden` | `overflow: hidden` |\n| `prose` | Custom rich text styles (font sizes, line heights, margins for h1-h6, p, ul, ol) |\n\nTailwind breakpoints: `sm` = 640px, `md` = 768px, `lg` = 1024px, `xl` = 1280px, `2xl` = 1536px.\n\n## @react-pdf/renderer\n\nPDF generation cannot be replicated in code components. If the old theme generates PDFs, this functionality must be dropped or handled externally.\n\n## @heroicons/react → Inline SVG\n\nReplace icon components with inline SVG. Copy the SVG paths from the Heroicons website and create simple components:\n```tsx\nfunction ChevronIcon({ className }: { className?: string }) {\n return (\n <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" fill=\"currentColor\" className={className} width=\"20\" height=\"20\">\n <path fillRule=\"evenodd\" d=\"M5.23 7.21a.75.75 0 011.06.02L10 11.168l3.71-3.938a.75.75 0 111.08 1.04l-4.25 4.5a.75.75 0 01-1.08 0l-4.25-4.5a.75.75 0 01.02-1.06z\" clipRule=\"evenodd\" />\n </svg>\n );\n}\n```",
26
- "tags": ["migration", "libraries", "swiper", "headlessui", "recharts", "react-player", "tailwind", "classnames", "marquee", "typewriter", "toast", "slider", "star-rating", "replacement", "vanilla"]
27
- },
28
- "react-to-preact": {
29
- "title": "React to Preact Conversion Patterns",
30
- "description": "Code patterns for converting old React components to new Preact code components",
31
- "content": "## CRITICAL: Two Completely Different Systems\n\n**`@ikas/storefront` and `@ikas/bp-storefront` are entirely separate packages.** Types with the same name are NOT the same types — they come from different packages with different properties. Never copy old `@ikas/storefront` type usage patterns into new code. Always use the MCP tools (`get_type_definition`, `get_function_doc`, `get_model_guide`) to look up the correct API in the new system.\n\n## Import Changes\n\n### Remove React import\nOld: `import React from 'react';` or `import * as React from 'react';`\nNew: Remove entirely. Preact JSX is auto-imported.\n\n### Hook imports\nOld: `import { useState, useEffect, useRef, useMemo, useCallback } from 'react';`\nNew: `import { useState, useEffect, useRef, useMemo, useCallback } from 'preact/hooks';`\n\n### Storefront imports\nOld: `import { Image, ... } from '@ikas/storefront';`\nNew: `import { getDefaultSrc, ... } from '@ikas/bp-storefront';`\n\n**WARNING:** Do not assume that because `IkasImage` exists in both packages, it has the same shape. The old `IkasImage` from `@ikas/storefront` and the new `IkasImage` from `@ikas/bp-storefront` are different types. Always check the new type definition via `get_type_definition('IkasImage')` before using it.\n\nThe old `<Image>` component from `@ikas/storefront` does not exist in the new system. Use native `<img>` with `getDefaultSrc()`:\n```tsx\n// Old:\nimport { Image } from '@ikas/storefront';\n<Image image={myImage} layout=\"fill\" objectFit=\"cover\" sizes=\"300px\" />\n\n// New:\nimport { getDefaultSrc } from '@ikas/bp-storefront';\n<img src={getDefaultSrc(myImage)} alt=\"\" className=\"my-image\" />\n```\n\nFor responsive images, use `getSrc()` with width parameter or `createMediaSrcset()`.\n\n### Observer pattern\nOld: `import { observer } from 'mobx-react-lite'; export default observer(MyComponent);`\nNew: Remove for root components. Only use on sub-components:\n```tsx\n// Root component (in src/components/) — NO observer:\nexport default function MySection(props: Props) { ... }\n\n// Sub-component (in src/sub-components/) — observer OK:\nimport { observer } from '@ikas/component-utils';\nfunction CartCounter() { ... }\nexport default observer(CartCounter);\n```\n\n## Component Declaration\n\nOld:\n```tsx\nconst MyComponent = ({ title, image }: MyComponentProps) => { return (...); };\nexport default observer(MyComponent);\n```\n\nNew:\n```tsx\nimport { Props } from './types';\nexport default function MyComponent({ title, image }: Props) { return (...); }\n```\n\nKey changes:\n- Named function export instead of arrow function + observer\n- Props type imported from `./types` (auto-generated by CLI)\n- No `observer()` wrapper on root components\n\n## Type Changes\n\n| Old Type | New Type | Notes |\n|----------|----------|-------|\n| `React.CSSProperties` | `JSX.CSSProperties` | Or just use `Record<string, string>` |\n| `React.FC<Props>` | Remove, use named function | |\n| `React.MouseEvent` | `JSX.TargetedMouseEvent<HTMLElement>` | |\n| `React.ChangeEvent<HTMLInputElement>` | `JSX.TargetedEvent<HTMLInputElement>` | |\n| `React.FormEvent` | `JSX.TargetedEvent<HTMLFormElement>` | |\n| `React.ReactNode` | `ComponentChildren` from `preact` | |\n| `IkasSlider` | `number` | Was `{ value: number }`, now plain number |\n\n## CSS Patterns\n\n### Tailwind → Scoped CSS\nOld: `<div className=\"flex items-center gap-4 p-6 text-center\">`\nNew: `<div className=\"my-container\">` with CSS:\n```css\n.my-container {\n display: flex;\n align-items: center;\n gap: 1rem;\n padding: 1.5rem;\n text-align: center;\n}\n```\n\n### CSS Modules → Scoped CSS\nOld: `import styles from './styles.module.css'; <div className={styles.container}>`\nNew: `<div className=\"container\">` — CSS is auto-scoped via `.cc_{componentId}` prefix.\n\n### Inline styles with CSS custom properties\nOld: `style={{ '--mth': \\`\\${marginTop?.value}px\\` } as React.CSSProperties}`\nNew: `style={{ '--mth': \\`\\${marginTop}px\\` }}` — Note: `marginTop` is now a plain number, not `IkasSlider`.\n\n## IkasSlider → number\n\nThis is one of the most common code changes. Every `IkasSlider` value access needs updating:\n\n```tsx\n// Old (IkasSlider has .value property):\nconst width = logoWidth?.value || 120;\nconst gap = `${spacing?.value || 0}px`;\nstyle={{ '--w': `${item.width?.value}px` }}\n\n// New (plain number):\nconst width = logoWidth || 120;\nconst gap = `${spacing || 0}px`;\nstyle={{ '--w': `${item.width}px` }}\n```\n\n## Image Handling\n\n```tsx\n// Old:\nimport { Image } from '@ikas/storefront';\n<Image image={myImage} layout=\"fill\" objectFit=\"cover\" sizes=\"300px\" />\n<Image image={myImage} layout=\"responsive\" width={300} height={200} />\n\n// New:\nimport { getDefaultSrc, getSrc } from '@ikas/bp-storefront';\n\n// Simple image:\n{myImage && <img src={getDefaultSrc(myImage)} alt=\"\" />}\n\n// With specific size:\n{myImage && <img src={getSrc(myImage, { width: 300 })} alt=\"\" />}\n\n// Background image:\n<div style={{ backgroundImage: myImage ? `url(${getDefaultSrc(myImage)})` : 'none' }} />\n```\n\n## Video Handling\n\n```tsx\n// Old (IkasVideo was often used with react-player):\nimport ReactPlayer from 'react-player';\n<ReactPlayer url={video.url} />\n\n// New (native video or iframe):\n{video && (\n video.type === 'EMBED'\n ? <iframe src={video.url} allowFullScreen />\n : <video src={video.url} controls poster={video.thumbnailUrl} />\n)}\n```\n\n## Event Handling\n\n```tsx\n// Old:\nconst handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n setValue(e.target.value);\n};\n\n// New:\nconst handleChange = (e: JSX.TargetedEvent<HTMLInputElement>) => {\n setValue((e.target as HTMLInputElement).value);\n};\n\n// Or simply:\nconst handleInput = (e: Event) => {\n setValue((e.target as HTMLInputElement).value);\n};\n// Use onInput instead of onChange for text inputs in Preact\n```\n\n## dangerouslySetInnerHTML\n\nWorks the same in both:\n```tsx\n<div dangerouslySetInnerHTML={{ __html: richTextContent }} />\n```\n\n## useStore → Direct imports\n\nOld themes used `useStore()` from `@ikas/storefront` for accessing MobX stores:\n```tsx\n// Old:\nconst { cartStore, customerStore } = useStore();\n\n// New — import stores directly:\nimport { cartStore, customerStore } from '@ikas/bp-storefront';\n```\n\n## key prop\n\nSame in both systems. Always use `key` when mapping:\n```tsx\n{items.map((item, i) => <div key={item.id || i}>...</div>)}\n```",
32
- "tags": ["migration", "react", "preact", "observer", "imports", "hooks", "css", "types", "IkasSlider", "Image", "conversion"]
33
- },
34
- "storefront-import-mapping": {
35
- "title": "Import Mapping: @ikas/storefront → @ikas/bp-storefront",
36
- "description": "Maps old @ikas/storefront imports to new @ikas/bp-storefront equivalents",
37
- "content": "## CRITICAL: These Are Completely Different Packages\n\n**`@ikas/storefront` (old) and `@ikas/bp-storefront` (new) are entirely different packages with different implementations.** This is NOT a simple find-and-replace of the package name. Even when types share the same name (e.g., `IkasImage`, `IkasProduct`), they are **different types** with potentially different properties, methods, and behaviors. You must look up each type's actual definition in the new system using `get_type_definition` or `get_model_guide` before using it.\n\n## Package Change\n\nAll imports from `@ikas/storefront` must be changed to `@ikas/bp-storefront`, but you must also verify that the types you're using exist and have the expected shape in the new package.\n\n## Type Mappings (Name Correspondence Only — NOT Identical Types)\n\n| Old Import | New Import | Notes |\n|-----------|-----------|-------|\n| `IkasImage` | `IkasImage` | Same name |\n| `IkasVideo` | `IkasVideo` | Same name |\n| `IkasNavigationLink` | `IkasNavigationLink` | Same name |\n| `IkasSlider` | *(removed)* | Use plain `number` instead |\n| `IkasProduct` | `IkasProduct` | Same name |\n| `IkasProductList` | `IkasProductList` | Same name |\n| `IkasCategory` | `IkasCategory` | Same name |\n| `IkasCategoryList` | `IkasCategoryList` | Same name |\n| `IkasBrand` | `IkasBrand` | Same name |\n| `IkasBlog` | `IkasBlog` | Same name |\n| `IkasBlogList` | `IkasBlogList` | Same name |\n| `IkasBlogCategory` | `IkasBlogCategory` | Same name |\n| `IkasBlogCategoryList` | `IkasBlogCategoryList` | Same name |\n| `IkasAttributeDetail` | `IkasProductAttributeValue` | Renamed |\n| `IkasAttributeList` | `IkasProductAttributeValue[]` | Renamed, now array |\n| `IkasComponentRenderer` | `IkasComponentRenderer` | Same name, different usage pattern |\n\n## Function Mappings\n\n| Old Function | New Function | Notes |\n|-------------|-------------|-------|\n| `Image` component | `getDefaultSrc()`, `getSrc()` | Component replaced with utility functions |\n| `useStore()` | Direct imports (`cartStore`, `customerStore`, etc.) | No more hook wrapper |\n\n## IkasComponentRenderer Changes\n\nOld: Component used as wrapper with children.\nNew: Component used with `components`, `id`, and `parentProps` attributes:\n\n```tsx\n// Old:\nimport { IkasComponentRenderer } from '@ikas/storefront';\n<IkasComponentRenderer>{children}</IkasComponentRenderer>\n\n// New:\nimport { IkasComponentRenderer } from '@ikas/bp-storefront';\n<IkasComponentRenderer\n id=\"my-list\"\n components={myComponentList as any[]}\n parentProps={props}\n/>\n```\n\n## Store Access\n\nOld stores were accessed via `useStore()` hook. New stores are direct imports:\n```tsx\n// Old:\nimport { useStore } from '@ikas/storefront';\nconst { cartStore, customerStore, i18nStore } = useStore();\n\n// New:\nimport { cartStore, customerStore } from '@ikas/bp-storefront';\n// Use directly — they're already MobX observables\n```\n\nUse `search_docs` or `list_functions` on the MCP server to discover all available functions and stores in `@ikas/bp-storefront`.",
38
- "tags": ["migration", "imports", "storefront", "bp-storefront", "IkasSlider", "IkasImage", "useStore", "IkasComponentRenderer"]
39
- },
40
- "theme-json-anatomy": {
41
- "title": "Old theme.json Structure",
42
- "description": "Explains the structure of old theme.json files for interpreting analyze_old_theme output",
43
- "content": "## Top-Level Structure\n\n```json\n{\n \"id\": \"uuid\",\n \"name\": \"Theme Name\",\n \"components\": [...],\n \"pages\": [...],\n \"customData\": [...],\n \"groups\": [...],\n \"settings\": { \"colors\": [...], \"fontFamily\": {...}, \"favicon\": {...}, \"stockPreference\": \"...\" },\n \"languages\": [...]\n}\n```\n\n## components[]\n\nEach component is a reusable UI element:\n```json\n{\n \"id\": \"uuid-or-name\",\n \"dir\": \"ComponentDirName\",\n \"displayName\": \"Human Name\",\n \"description\": \"...\",\n \"type\": \"COMPONENT\",\n \"props\": [...],\n \"defaultPropValues\": { \"propName\": null },\n \"defaultPropValuesTranslations\": { \"locale\": { \"propName\": \"...\" } },\n \"commonPropValues\": {},\n \"translations\": {}\n}\n```\n\n`dir` is the component's directory under `src/components/`. It maps to the React component file at `src/components/{dir}/index.tsx`.\n\n## components[].props[]\n\nEach prop definition:\n```json\n{\n \"id\": \"uuid\",\n \"name\": \"propName\",\n \"displayName\": \"Prop Label\",\n \"description\": \"\",\n \"type\": \"TEXT|BOOLEAN|IMAGE|CUSTOM|SLIDER|...\",\n \"isRequired\": true,\n \"customDataId\": \"uuid-or-null\",\n \"sliderData\": { \"min\": 0, \"max\": 100, \"interval\": 1 },\n \"groupId\": \"uuid-or-empty\",\n \"translations\": {},\n \"excludedFields\": [],\n \"excludedFieldsCategory\": [],\n \"isFacetListNotSent\": false\n}\n```\n\nKey fields:\n- `type` — The prop type (TEXT, BOOLEAN, IMAGE, CUSTOM, SLIDER, etc.)\n- `customDataId` — Only for `type: \"CUSTOM\"`. References a customData entry by ID.\n- `sliderData` — Only for `type: \"SLIDER\"`. Contains min, max, interval.\n- `groupId` — References a group in the `groups[]` array.\n\n## customData[]\n\nReusable data type definitions. Each entry defines a complex data structure:\n\n```json\n{\n \"id\": \"uuid\",\n \"name\": \"Type Name\",\n \"typescriptName\": \"TypeName\",\n \"type\": \"DYNAMIC_LIST|OBJECT|STATIC_LIST|ENUM\",\n \"isRequired\": true,\n \"isRoot\": true,\n \"level\": 0,\n \"nestedData\": [...],\n \"enumOptions\": [...],\n \"sliderData\": {},\n \"translations\": {}\n}\n```\n\n### DYNAMIC_LIST\nA variable-length list. `nestedData` contains one OBJECT entry describing the item shape:\n```json\n{ \"type\": \"DYNAMIC_LIST\", \"nestedData\": [\n { \"type\": \"OBJECT\", \"typescriptName\": \"Item\", \"nestedData\": [\n { \"key\": \"title\", \"type\": \"TEXT\" },\n { \"key\": \"image\", \"type\": \"IMAGE\" }\n ]}\n]}\n```\nThe OBJECT's `nestedData` fields have `key` properties that become TypeScript property names.\n\n### OBJECT\nA structured object with named fields. Each field in `nestedData` has a `key`:\n```json\n{ \"type\": \"OBJECT\", \"nestedData\": [\n { \"key\": \"fieldName\", \"type\": \"TEXT\", \"name\": \"Field Label\" }\n]}\n```\n\n### Nesting\nCustomData can nest arbitrarily. A DYNAMIC_LIST can contain an OBJECT that has fields which are themselves DYNAMIC_LIST or reference other customData via `customDataId`.\n\n## groups[]\n\nProp grouping definitions:\n```json\n{ \"id\": \"uuid\", \"name\": \"Group Name\", \"translations\": {} }\n```\nComponents reference groups via `groupId` on their props.\n\n## settings\n\n```json\n{\n \"colors\": [\n { \"id\": \"uuid\", \"displayName\": \"Color Name\", \"color\": \"#hexvalue\", \"key\": \"--css-var-name\" }\n ],\n \"fontFamily\": { \"name\": \"FontName\", \"variants\": [\"300\", \"regular\", \"500\", \"600\", \"700\"] },\n \"favicon\": { \"id\": \"uuid-or-null\" },\n \"stockPreference\": \"SHOW_ALL|HIDE_OUT_OF_STOCK\"\n}\n```\n\nColors define CSS custom properties (e.g., `--primary`, `--button-bg-1`). These should be converted to CSS variables in `global.css`.\n\n## pages[]\n\nPage definitions with component instances and their prop values. Not needed for component migration but useful for understanding which components are used where.",
44
- "tags": ["migration", "theme-json", "structure", "anatomy", "components", "customData", "groups", "settings", "pages"]
45
- },
46
- "component-decomposition-strategy": {
47
- "title": "Component Decomposition Strategy",
48
- "description": "How to break old monolithic components into section + child components in the new system",
49
- "content": "## Overview\n\nOld themes typically have flat component lists where each component handles everything internally. The new system uses a section → child component hierarchy. Here's how to decompose.\n\n## Rule 1: Every Old Component Becomes a Section\n\nIn the new system, sections are page-level containers. Each old component maps to a `type: \"section\"` in ikas.config.json.\n\nException: If an old component was only used as an internal sub-component (e.g., a reusable ProductCard), it becomes a `type: \"component\"` (child) instead.\n\n## Rule 2: CUSTOM (DYNAMIC_LIST) Props → COMPONENT_LIST + Child\n\nFor each CUSTOM prop that references a DYNAMIC_LIST customData:\n1. Replace the CUSTOM prop with `COMPONENT_LIST` on the parent section\n2. Create a new child component for the list item\n3. Map the OBJECT's nested fields to props on the child component\n4. Use `filteredComponentIds` to restrict which children can be placed in the slot\n\n## Rule 3: Flatten Simple Objects\n\nIf a CUSTOM prop references an OBJECT with ≤3 simple fields (TEXT, BOOLEAN, COLOR, NUMBER), flatten them into direct props on the parent. This avoids unnecessary child components.\n\nExample:\n- Old: `settings: { backgroundColor: string, showTitle: boolean }` via CUSTOM OBJECT\n- New: `backgroundColor` (COLOR prop) + `showTitle` (BOOLEAN prop) directly on section\n\n## Rule 4: Header and Footer\n\nIdentify header/footer components (usually named \"Navbar\", \"Header\", \"Footer\"):\n- Mark the section with `isHeader: true` or `isFooter: true`\n- These sections appear on every page automatically\n\n## Rule 5: Prop Groups\n\nOld `groups[]` array maps to `propGroups` on each component:\n```json\n// Old (global groups array):\n\"groups\": [{ \"id\": \"group-uuid\", \"name\": \"Slider\" }]\n// Component prop: { \"name\": \"delay\", \"groupId\": \"group-uuid\" }\n\n// New (per-component propGroups):\n\"propGroups\": [{ \"id\": \"slider\", \"name\": \"Slider\" }]\n// Component prop: { \"name\": \"delay\", \"groupId\": \"slider\" }\n```\n\n## Rule 6: Shared Sub-Components\n\nIf multiple sections share similar UI elements (buttons, modals, cards), create them in `src/sub-components/` instead of registering them in ikas.config.json.\n\n## Example Decomposition\n\n**Old: ProductGrid component** with props:\n- `products` (CUSTOM → DYNAMIC_LIST of `{ tab: string, products: IkasProductList }`)\n- `showedTags` (CUSTOM → DYNAMIC_LIST of `{ tag: string, color: string }`)\n- `columns` (SLIDER)\n- `gap` (SLIDER)\n\n**New structure:**\n1. **ProductGridSection** (section)\n - `tabs` → COMPONENT_LIST (filteredComponentIds: [\"my-project-product-tab\"])\n - `tags` → COMPONENT_LIST (filteredComponentIds: [\"my-project-product-tag\"])\n - `columns` → NUMBER\n - `gap` → NUMBER\n\n2. **ProductTab** (component)\n - `tabName` → TEXT\n - `products` → PRODUCT_LIST\n\n3. **ProductTag** (component)\n - `tag` → TEXT\n - `color` → COLOR\n - `backgroundColor` → COLOR\n\n## Estimating Child Components\n\nCount the number of CUSTOM props across all components that reference DYNAMIC_LIST customData. Each one typically needs 1 child component. Some may share child components if the structure is identical.",
50
- "tags": ["migration", "decomposition", "section", "component", "COMPONENT_LIST", "header", "footer", "strategy"]
51
- },
52
- "complete-project-generation": {
53
- "title": "Complete Project Generation Guide",
54
- "description": "How to generate a full code-component project from an old theme in one pass",
55
- "content": "## Project Structure\n\nThe generated project should follow this structure:\n```\nmy-theme/\n├── src/\n│ ├── global.css\n│ └── components/\n│ ├── HeaderSection/\n│ │ ├── index.tsx\n│ │ ├── types.ts # Auto-generated (don't write manually)\n│ │ └── styles.css\n│ ├── HeroBanner/\n│ │ ├── index.tsx\n│ │ ├── types.ts\n│ │ └── styles.css\n│ ├── SlideItem/ # Child component for hero slides\n│ │ ├── index.tsx\n│ │ ├── types.ts\n│ │ └── styles.css\n│ ├── FooterSection/\n│ │ ├── index.tsx\n│ │ ├── types.ts\n│ │ └── styles.css\n│ └── index.ts # Barrel export\n├── ikas.config.json\n├── package.json\n├── tsconfig.json\n└── vite.config.ts\n```\n\n## Generation Order\n\n### 1. Generate ikas.config.json\n\nThis is the most critical file. It defines all components and their props.\n\n```json\n{\n \"name\": \"my-theme\",\n \"version\": \"1.0.0\",\n \"globalStyles\": \"./src/global.css\",\n \"components\": [\n {\n \"id\": \"my-theme-header\",\n \"name\": \"HeaderSection\",\n \"type\": \"section\",\n \"isHeader\": true,\n \"entry\": \"./src/components/HeaderSection/index.tsx\",\n \"styles\": \"./src/components/HeaderSection/styles.css\",\n \"props\": [...],\n \"propGroups\": [...]\n }\n ],\n \"customTypes\": [...]\n}\n```\n\nKey rules:\n- Component `id` format: `{projectName}-{component-kebab-case}`\n- Sections use `type: \"section\"`, children use `type: \"component\"` (or omit for default)\n- COMPONENT_LIST props should have `filteredComponentIds` pointing to specific child components\n- Create `customTypes` entries for all ENUM types\n\n### 2. Generate global.css\n\nConvert theme settings to CSS:\n```css\n/* Colors from settings.colors */\n:root {\n --ann-color: #ac8748;\n --fsp: #a9a9a9;\n --primary: #fed9d9;\n --button-bg-1: #000000;\n}\n\n/* Font from settings.fontFamily */\n@import url('https://fonts.googleapis.com/css2?family=Quicksand:wght@300;400;500;600;700&display=swap');\n\nbody {\n font-family: 'Quicksand', sans-serif;\n}\n\n/* Common utility styles migrated from old global.css */\n.wrapper {\n max-width: 1700px;\n margin: 0 auto;\n padding: 0 20px;\n}\n\n@media (min-width: 1024px) {\n .wrapper {\n padding: 0 60px;\n }\n}\n```\n\n### 3. Generate Component Source Files\n\nFor each component, generate `index.tsx` and `styles.css`.\n\n**Do NOT generate `types.ts`** — the CLI auto-generates it from ikas.config.json.\n\nEach component should:\n- Import `Props` from `./types`\n- Use named default function export\n- Import from `@ikas/bp-storefront` (not `@ikas/storefront`)\n- Use `<IkasComponentRenderer>` for COMPONENT_LIST props\n- Use `getDefaultSrc()` for images\n- Use scoped CSS classes (not Tailwind)\n\n### 4. Barrel Export\n\n`src/components/index.ts`:\n```typescript\nexport { default as HeaderSection } from './HeaderSection';\nexport { default as HeroBanner } from './HeroBanner';\nexport { default as SlideItem } from './SlideItem';\n// ... etc\n```\n\n## Naming Conventions\n\n| Old | New |\n|----|-----|\n| Component name: `Navbar` | Section: `HeaderSection`, ID: `my-theme-header-section` |\n| Custom type: `Maaza` | Child component: `StoreCard`, ID: `my-theme-store-card` |\n| Custom type: `GrselveLink` | Child component: `BannerSlide`, ID: `my-theme-banner-slide` |\n\nUse descriptive English names even if the old theme used Turkish names.\n\n## Common Pitfalls\n\n1. **Don't forget `filteredComponentIds`** — Without it, any component can be placed in a COMPONENT_LIST slot\n2. **Don't manually write types.ts** — Always let the CLI generate it\n3. **Don't use `observer()` on root components** — Only on sub-components\n4. **Convert all `IkasSlider` usage** — `.value` access must become direct number access\n5. **Replace ALL library imports** — No swiper, headlessui, recharts, etc.\n6. **Convert Tailwind to plain CSS** — No utility classes in the new system",
56
- "tags": ["migration", "project", "generation", "ikas-config", "global-css", "structure", "complete"]
57
- },
58
- "settings-conversion": {
59
- "title": "Theme Settings Conversion",
60
- "description": "How to convert old theme settings (colors, fonts, favicon) to the new system",
61
- "content": "## Colors\n\nOld theme settings define CSS custom properties:\n```json\n\"settings\": {\n \"colors\": [\n { \"displayName\": \"Primary\", \"color\": \"#fed9d9\", \"key\": \"--primary\" },\n { \"displayName\": \"Button BG\", \"color\": \"#000000\", \"key\": \"--button-bg-1\" }\n ]\n}\n```\n\nConvert to CSS variables in `global.css`:\n```css\n:root {\n --primary: #fed9d9;\n --button-bg-1: #000000;\n}\n```\n\nThese variables can then be used in component CSS files:\n```css\n.button { background-color: var(--button-bg-1); }\n```\n\nAlternatively, expose colors as COLOR props on individual components so store owners can customize per-component.\n\n## Fonts\n\nOld:\n```json\n\"fontFamily\": { \"name\": \"Quicksand\", \"variants\": [\"300\", \"regular\", \"500\", \"600\", \"700\"] }\n```\n\nNew `global.css`:\n```css\n@import url('https://fonts.googleapis.com/css2?family=Quicksand:wght@300;400;500;600;700&display=swap');\n\nbody {\n font-family: 'Quicksand', sans-serif;\n}\n```\n\n## Favicon\n\nFavicon handling is outside the code-component scope. It's managed at the store level.\n\n## Stock Preference\n\nThe `stockPreference` setting (SHOW_ALL, HIDE_OUT_OF_STOCK) is handled by the storefront API, not component code.",
62
- "tags": ["migration", "settings", "colors", "fonts", "css-variables", "global-css"]
63
- }
64
- }
65
- }