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

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 (54) hide show
  1. package/data/framework.json +34 -34
  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 +260 -0
  33. package/data/section-templates/account-info-section/children/AccountFavorites/ikas-config-snippet.json +3 -3
  34. package/data/section-templates/account-info-section/ikas-config-snippet.json +5 -5
  35. package/data/section-templates/category-images-section/ikas-config-snippet.json +1 -1
  36. package/data/section-templates/category-list-section/ikas-config-snippet.json +3 -3
  37. package/data/section-templates/component-renderer/ikas-config-snippet.json +3 -3
  38. package/data/section-templates/features-section/ikas-config-snippet.json +1 -1
  39. package/data/section-templates/footer-section/ikas-config-snippet.json +1 -1
  40. package/data/section-templates/header-section/children/Announcements/ikas-config-snippet.json +1 -1
  41. package/data/section-templates/header-section/children/Navbar/ikas-config-snippet.json +3 -3
  42. package/data/section-templates/header-section/ikas-config-snippet.json +3 -3
  43. package/data/section-templates/hero-slider-section/ikas-config-snippet.json +1 -1
  44. package/data/section-templates/image-handling/ikas-config-snippet.json +13 -13
  45. package/data/section-templates/navigation/ikas-config-snippet.json +3 -3
  46. package/data/section-templates/product-detail-section/children/ProductDetailDescription/ikas-config-snippet.json +1 -1
  47. package/data/section-templates/product-detail-section/children/ProductDetailFeatures/ikas-config-snippet.json +1 -1
  48. package/data/section-templates/product-detail-section/ikas-config-snippet.json +13 -13
  49. package/data/section-templates/product-slider-section/ikas-config-snippet.json +3 -3
  50. package/data/storefront-api.json +1 -1
  51. package/data/storefront-types.json +1 -1
  52. package/dist/index.js +1737 -47
  53. package/dist/index.js.map +1 -1
  54. package/package.json +1 -1
@@ -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
+ }
@@ -0,0 +1,60 @@
1
+ import { Image } from '@ikas/storefront';
2
+ import { observer } from 'mobx-react-lite';
3
+ import { CTAProps } from '../__generated__/types';
4
+
5
+ const CTA = ({
6
+ image,
7
+ aspect,
8
+ title,
9
+ content,
10
+ marginTop,
11
+ mobileMarginTop,
12
+ reverse,
13
+ bg,
14
+ miniImage,
15
+ miniImageAspect,
16
+ imageMaxWidth,
17
+ textMaxWidth,
18
+ }: CTAProps) => {
19
+ return (
20
+ <div style={{ backgroundColor: bg || 'transparent' }}>
21
+ <div
22
+ style={{
23
+ '--mth': `${marginTop?.value || 0}px`,
24
+ '--mthm': `${mobileMarginTop?.value || 0}px`,
25
+ } as React.CSSProperties}
26
+ className={`wrapper mt-[var(--mthm)] grid grid-cols-1 items-center max-lg:pt-[60px] lg:mt-[var(--mth)] lg:grid-cols-2`}
27
+ >
28
+ <div
29
+ style={{ aspectRatio: aspect, maxWidth: imageMaxWidth || '316px' }}
30
+ className={`relative m-auto w-full overflow-hidden lg:mt-[60px] ${reverse ? 'order-last' : ''}`}
31
+ >
32
+ <Image image={image} layout="fill" objectFit="cover" />
33
+ </div>
34
+
35
+ <div
36
+ style={{ maxWidth: textMaxWidth || '600px' }}
37
+ className="m-auto flex h-full flex-col items-center justify-center p-6 pt-0 xl:p-10"
38
+ >
39
+ {miniImage && (
40
+ <div style={{ aspectRatio: miniImageAspect }} className="relative mb-[30px] w-[188px] lg:mb-10">
41
+ <Image image={miniImage} layout="fill" objectFit="contain" sizes="200px" />
42
+ </div>
43
+ )}
44
+ {title && (
45
+ <div
46
+ className="prose mb-4 max-w-none text-center text-[20px] font-light !leading-tight text-white lg:text-[30px]"
47
+ dangerouslySetInnerHTML={{ __html: title }}
48
+ />
49
+ )}
50
+ <div
51
+ className="prose prose-sm max-w-none text-center font-light !leading-tight text-white"
52
+ dangerouslySetInnerHTML={{ __html: content }}
53
+ />
54
+ </div>
55
+ </div>
56
+ </div>
57
+ );
58
+ };
59
+
60
+ export default observer(CTA);
@@ -0,0 +1,4 @@
1
+ {
2
+ "title": "Simple CUSTOM (OBJECT) → Inline Props",
3
+ "description": "When a CUSTOM prop references an OBJECT with 3 or fewer simple fields, flatten them into direct props on the parent component instead of creating a child component."
4
+ }