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

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 (36) hide show
  1. package/data/migration-examples/complex-header-migration/_meta.json +4 -0
  2. package/data/migration-examples/complex-header-migration/after-config-snippet.json +55 -0
  3. package/data/migration-examples/complex-header-migration/after-section.tsx +64 -0
  4. package/data/migration-examples/complex-header-migration/before-props-summary.json +42 -0
  5. package/data/migration-examples/custom-dynamic-list-to-component-list/_meta.json +4 -0
  6. package/data/migration-examples/custom-dynamic-list-to-component-list/after-child-styles.css +38 -0
  7. package/data/migration-examples/custom-dynamic-list-to-component-list/after-child.tsx +22 -0
  8. package/data/migration-examples/custom-dynamic-list-to-component-list/after-config-snippet.json +31 -0
  9. package/data/migration-examples/custom-dynamic-list-to-component-list/after-section-styles.css +25 -0
  10. package/data/migration-examples/custom-dynamic-list-to-component-list/after-section.tsx +17 -0
  11. package/data/migration-examples/custom-dynamic-list-to-component-list/before-component.tsx +32 -0
  12. package/data/migration-examples/custom-dynamic-list-to-component-list/before-theme-snippet.json +53 -0
  13. package/data/migration-examples/full-component-with-tailwind/_meta.json +4 -0
  14. package/data/migration-examples/full-component-with-tailwind/after-component.tsx +43 -0
  15. package/data/migration-examples/full-component-with-tailwind/after-config-snippet.json +25 -0
  16. package/data/migration-examples/full-component-with-tailwind/after-styles.css +99 -0
  17. package/data/migration-examples/full-component-with-tailwind/before-component.tsx +60 -0
  18. package/data/migration-examples/object-custom-to-inline-props/_meta.json +4 -0
  19. package/data/migration-examples/object-custom-to-inline-props/after-component.tsx +34 -0
  20. package/data/migration-examples/object-custom-to-inline-props/after-config-snippet.json +23 -0
  21. package/data/migration-examples/object-custom-to-inline-props/after-styles.css +38 -0
  22. package/data/migration-examples/object-custom-to-inline-props/before-component.tsx +30 -0
  23. package/data/migration-examples/object-custom-to-inline-props/before-theme-snippet.json +26 -0
  24. package/data/migration-examples/slider-library-replacement/_meta.json +4 -0
  25. package/data/migration-examples/slider-library-replacement/after-child.tsx +13 -0
  26. package/data/migration-examples/slider-library-replacement/after-config-snippet.json +29 -0
  27. package/data/migration-examples/slider-library-replacement/after-section.tsx +38 -0
  28. package/data/migration-examples/slider-library-replacement/after-styles.css +43 -0
  29. package/data/migration-examples/slider-library-replacement/before-component.tsx +39 -0
  30. package/data/migration-examples/slider-library-replacement/before-types-snippet.ts +14 -0
  31. package/data/migration.json +95 -0
  32. package/data/storefront-api.json +1 -1
  33. package/data/storefront-types.json +1 -1
  34. package/dist/index.js +1282 -3
  35. package/dist/index.js.map +1 -1
  36. 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
+ }