@netlify/spark-ui 1.8.6 → 1.9.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.
@@ -0,0 +1,13 @@
1
+ class n {
2
+ /**
3
+ * Pushes an event payload into the dataLayer.
4
+ * @param eventName e.g. 'www:search_new_query'
5
+ * @param payload additional key/value data
6
+ */
7
+ track(t, e = {}) {
8
+ typeof gtag == "function" ? gtag("event", t, { ...e }) : console.error(`gtag is not defined. Event [${t}] failed.`);
9
+ }
10
+ }
11
+ export {
12
+ n as A
13
+ };
@@ -1,8 +1,9 @@
1
1
  import { u as t } from "../../../chunks/jsxRuntime.module.Bzuv3cXw.js";
2
2
  import { A as a } from "../../../chunks/hooks.module.DGkdD5n9.js";
3
3
  import { C as s, K as i } from "../../../chunks/ChatInterface.BD7rlv4H.js";
4
- import '../../../assets/AskNetlify.css';function m(o) {
5
- const e = a();
4
+ import { A as c } from "../../../chunks/analytics.Ch1vuuOl.js";
5
+ import '../../../assets/AskNetlify.css';function k(o) {
6
+ const e = a(new c());
6
7
  return /* @__PURE__ */ t(
7
8
  i,
8
9
  {
@@ -30,5 +31,5 @@ import '../../../assets/AskNetlify.css';function m(o) {
30
31
  );
31
32
  }
32
33
  export {
33
- m as default
34
+ k as default
34
35
  };
@@ -1,37 +1,28 @@
1
- import { A as y, y as h, q as k } from "../../../../chunks/hooks.module.DGkdD5n9.js";
2
- class s {
3
- /**
4
- * Pushes an event payload into the dataLayer.
5
- * @param eventName e.g. 'www:search_new_query'
6
- * @param payload additional key/value data
7
- */
8
- track(n, i = {}) {
9
- typeof gtag == "function" ? gtag("event", n, { ...i }) : console.error(`gtag is not defined. Event [${n}] failed.`);
10
- }
11
- }
12
- function p(r, n) {
13
- const l = y(new s()).current, e = (c) => c.debug_mode = !0;
1
+ import { A as y, y as h, q as d } from "../../../../chunks/hooks.module.DGkdD5n9.js";
2
+ import { A as k } from "../../../../chunks/analytics.Ch1vuuOl.js";
3
+ function b(r, l) {
4
+ const n = y(new k()).current, e = (c) => c.debug_mode = !0;
14
5
  return h(() => {
15
6
  if (!(r != null && r.searchTerm)) return;
16
7
  const c = { query: r == null ? void 0 : r.searchTerm };
17
- n && e(c), l.track("search:new_query", c);
8
+ l && e(c), n.track("search:new_query", c);
18
9
  }, [r == null ? void 0 : r.searchTerm]), h(() => {
19
10
  if (!(r != null && r.results)) return;
20
11
  const c = { query: r == null ? void 0 : r.searchTerm };
21
- n && e(c), r != null && r.searchTerm && r.results.length === 0 && l.track("search:no_results", c);
12
+ l && e(c), r != null && r.searchTerm && r.results.length === 0 && n.track("search:no_results", c);
22
13
  }, [r == null ? void 0 : r.results]), {
23
- handleClick: k((c) => {
24
- var f;
14
+ handleClick: d((c) => {
15
+ var i;
25
16
  const o = c.target.closest("a");
26
17
  if (!o) return;
27
- const g = (f = o.querySelector(".n-search-link-label")) == null ? void 0 : f.innerHTML, d = o.getAttribute("href") || void 0, u = {
28
- resultTitle: g,
29
- resultUrl: d
18
+ const f = (i = o.querySelector(".n-search-link-label")) == null ? void 0 : i.innerHTML, m = o.getAttribute("href") || void 0, u = {
19
+ resultTitle: f,
20
+ resultUrl: m
30
21
  };
31
- n && e(u), l.track("search:result_selected", u);
22
+ l && e(u), n.track("search:result_selected", u);
32
23
  }, [])
33
24
  };
34
25
  }
35
26
  export {
36
- p as useSearchLogger
27
+ b as useSearchLogger
37
28
  };
@@ -1,3 +1,3 @@
1
- export type Theme = 'light' | 'dark';
2
- export declare const ThemeMap: Record<Theme, Theme>;
3
- export declare const handleThemeChange: (value: Theme) => void;
1
+ import { THEMES } from './types';
2
+ export declare const ThemeMap: Record<THEMES, THEMES>;
3
+ export declare const handleThemeChange: (value: THEMES) => void;
@@ -1,3 +1,4 @@
1
+ export type THEMES = 'light' | 'dark';
1
2
  export type BUTTON_VARIANTS = 'primary' | 'secondary' | 'outline' | 'ghost';
2
3
  export interface CallToActionProps {
3
4
  id?: string;
@@ -5,3 +6,9 @@ export interface CallToActionProps {
5
6
  href: string;
6
7
  variant?: BUTTON_VARIANTS;
7
8
  }
9
+ export interface ImageProps {
10
+ src: string;
11
+ alt: string;
12
+ width: string;
13
+ height: string;
14
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@netlify/spark-ui",
3
3
  "description": "Assets, design tokens, components, and utilities",
4
- "version": "1.8.6",
4
+ "version": "1.9.0",
5
5
  "type": "module",
6
6
  "main": "dist/components/preact/index.js",
7
7
  "types": "dist/components/preact/index.d.ts",
@@ -16,7 +16,7 @@ interface Props {
16
16
  }
17
17
 
18
18
  const {
19
- as = 'div',
19
+ as: Element = 'div',
20
20
  eyebrow,
21
21
  paragraph,
22
22
  headline,
@@ -25,8 +25,6 @@ const {
25
25
  class: className,
26
26
  ...rest
27
27
  } = Astro.props;
28
-
29
- const Element = as;
30
28
  ---
31
29
 
32
30
  <Element class:list={['n-card n-flow', isCardClickable && 'n-card-clickable', className]} {...rest}>
@@ -66,6 +64,14 @@ const Element = as;
66
64
  border-radius: var(--radius-m);
67
65
  }
68
66
 
67
+ .n-card-eyebrow {
68
+ --n-flow-space: var(--space-l);
69
+ }
70
+
71
+ .n-card-eyebrow + .n-card-headline {
72
+ --n-flow-space: var(--space-s);
73
+ }
74
+
69
75
  .n-card-headline {
70
76
  font-size: var(--font-size-display-sm);
71
77
  font-family: var(--font-family-display);
@@ -78,10 +84,6 @@ const Element = as;
78
84
  line-height: var(--line-height-text);
79
85
  }
80
86
 
81
- .n-card-eyebrow {
82
- --n-flow-space: var(--space-l);
83
- }
84
-
85
87
  .n-card-paragraph:nth-last-child(2) {
86
88
  margin-block-end: auto;
87
89
  }
@@ -1,7 +1,5 @@
1
1
  ---
2
- const { as = 'div', class: className, ...rest } = Astro.props;
3
-
4
- const Element = as;
2
+ const { as: Element = 'div', class: className, ...rest } = Astro.props;
5
3
  ---
6
4
 
7
5
  <Element class:list={['eyebrow', className]} {...rest}>
@@ -12,7 +10,7 @@ const Element = as;
12
10
  .eyebrow {
13
11
  font-size: var(--font-size-text-sm);
14
12
  font-family: var(--font-family-mono);
15
- line-height: var(--line-height-none);
13
+ line-height: var(--line-height-heading);
16
14
  color: var(--eyebrow-text-color);
17
15
  text-transform: uppercase;
18
16
  }
@@ -53,7 +53,7 @@ const customStyles =
53
53
  {...rest}
54
54
  >
55
55
  {eyebrow && <Eyebrow class="n-heading-block-eyebrow">{eyebrow}</Eyebrow>}
56
- {heading && <Heading class="n-heading-block-heading h1">{heading}</Heading>}
56
+ {heading && <Heading class="n-heading-block-heading h1" set:html={heading} />}
57
57
  {text && <p class="n-heading-block-text" set:html={text} />}
58
58
  {
59
59
  ctas && ctas.length > 0 && (
@@ -69,10 +69,6 @@ const customStyles =
69
69
  </div>
70
70
 
71
71
  <style>
72
- .n-heading-block {
73
- padding-block: var(--space-xl);
74
- }
75
-
76
72
  .n-heading-block-center {
77
73
  --n-cluster-justify: center;
78
74
  text-align: center;
@@ -84,11 +80,14 @@ const customStyles =
84
80
  }
85
81
 
86
82
  .n-heading-block-heading {
83
+ font-size: var(--n-heading-block-heading-font-size, var(--font-size-display-2xl));
87
84
  inline-size: min(100%, var(--n-heading-block-heading-max-width, 50rem));
88
85
  text-wrap: balance;
86
+ color: var(--heading-text-color);
89
87
  }
90
88
 
91
89
  .n-heading-block-text {
90
+ font-size: var(--n-heading-block-text-font-size, var(--font-size-text-lg));
92
91
  inline-size: min(100%, var(--n-heading-block-text-max-width, 35rem));
93
92
  text-wrap: pretty;
94
93
  }
@@ -0,0 +1,20 @@
1
+ ---
2
+ import HeadingBlock from './HeadingBlock.astro';
3
+ import PageSection from './PageSection.astro';
4
+
5
+ const { class: className, heading, text, ctas, ...rest } = Astro.props;
6
+ ---
7
+
8
+ <PageSection class:list={['n-page-hero', className]} {...rest}>
9
+ <HeadingBlock heading={heading} headingLevel="1" text={text} alignment="center" ctas={ctas} />
10
+ <slot />
11
+ </PageSection>
12
+
13
+ <style>
14
+ .n-page-hero {
15
+ --n-page-section-space: var(--space-4xl);
16
+ --n-page-section-bg-color: linear-gradient(to bottom, var(--color-bg) 50%, var(--teal-800));
17
+ --n-heading-block-heading-font-size: var(--font-size-display-3xl);
18
+ --n-heading-block-text-font-size: var(--font-size-text-xl);
19
+ }
20
+ </style>
@@ -0,0 +1,45 @@
1
+ ---
2
+ import { THEMES } from '@utils/types';
3
+
4
+ interface Props {
5
+ id?: string;
6
+ class?: string;
7
+ spaceTop?: string;
8
+ spaceBottom?: string;
9
+ theme?: THEMES;
10
+ }
11
+
12
+ const { id, class: className, spaceTop, spaceBottom, theme, ...rest } = Astro.props;
13
+
14
+ const customStyles =
15
+ spaceTop || spaceBottom
16
+ ? {
17
+ ...(spaceTop && { '--n-page-section-space-top': spaceTop }),
18
+ ...(spaceBottom && { '--n-page-section-space-bottom': spaceBottom }),
19
+ }
20
+ : null;
21
+ ---
22
+
23
+ <section
24
+ id={id}
25
+ data-theme={theme}
26
+ class:list={['n-page-section n-breakout', className]}
27
+ style={customStyles}
28
+ {...rest}
29
+ >
30
+ <slot />
31
+ </section>
32
+
33
+ <style>
34
+ .n-page-section {
35
+ --_space: var(--space-3xl);
36
+ color: var(--n-page-section-text-color, var(--color-text));
37
+ background: var(--n-page-section-bg-color, var(--color-bg));
38
+ padding-block-start: var(--n-page-section-space-top, var(--_space));
39
+ padding-block-end: var(--n-page-section-space-bottom, var(--_space));
40
+ }
41
+
42
+ .n-page-section :global(> * + *) {
43
+ margin-block-start: var(--n-page-section-item-space, var(--space-2xl));
44
+ }
45
+ </style>
@@ -12,7 +12,7 @@ interface Props {
12
12
  text?: string;
13
13
  list?: string[];
14
14
  cta?: CallToActionProps;
15
- mediaPattern?: 1 | 2;
15
+ mediaPattern?: number;
16
16
  }
17
17
 
18
18
  const {
@@ -69,7 +69,7 @@ const {
69
69
 
70
70
  <style>
71
71
  .n-switchback {
72
- --n-sidebar-size: 25rem;
72
+ --n-sidebar-size: 28.75rem;
73
73
  --n-sidebar-min: 50%;
74
74
  --n-sidebar-gap: 0;
75
75
  overflow: hidden;
@@ -0,0 +1,13 @@
1
+ ---
2
+ const { class: className, ...rest } = Astro.props;
3
+ ---
4
+
5
+ <div class:list={['n-switchback-group', className]} {...rest}>
6
+ <slot />
7
+ </div>
8
+
9
+ <style>
10
+ .n-switchback-group :global(> * + *) {
11
+ margin-block-start: var(--n-switchback-group-space, var(--space-xl));
12
+ }
13
+ </style>
@@ -19,6 +19,7 @@ const { id, class: className, ...rest } = Astro.props;
19
19
  --_border-style: solid;
20
20
  --_border-color-inactive: var(--color-bg-3);
21
21
  --_border-color-active: var(--color-icon);
22
+ display: block;
22
23
  }
23
24
 
24
25
  .n-tabbed-content :global([role='tablist']) {
@@ -0,0 +1,158 @@
1
+ ---
2
+ import { CallToActionProps } from '@utils/types';
3
+ import CodeBlock from './CodeBlock.astro';
4
+ import Button from './Button.astro';
5
+ import TabPanel from './TabPanel.astro';
6
+
7
+ interface CodeBlockProps {
8
+ heading?: string;
9
+ icon?: string;
10
+ code: string;
11
+ lang: string;
12
+ }
13
+
14
+ interface Props {
15
+ id?: string;
16
+ class?: string;
17
+ isHidden?: boolean;
18
+ heading?: string;
19
+ text?: string;
20
+ list?: string[];
21
+ ctas?: CallToActionProps[];
22
+ codeBlock?: CodeBlockProps;
23
+ }
24
+
25
+ const {
26
+ id,
27
+ class: className,
28
+ heading,
29
+ list,
30
+ text,
31
+ ctas,
32
+ codeBlock,
33
+ isHidden,
34
+ ...rest
35
+ } = Astro.props;
36
+ ---
37
+
38
+ <TabPanel id={id} class:list={['n-tabbed-content-panel', className]} isHidden={isHidden} {...rest}>
39
+ <div class:list={['n-tabbed-content-wrapper', codeBlock && 'n-tabbed-content-wrapper-split']}>
40
+ <div class="n-tabbed-content-body n-flow">
41
+ {heading && <h3 class="h4">{heading}</h3>}
42
+ {text && <p set:html={text} />}
43
+ {
44
+ list && list.length > 0 && (
45
+ <ul data-list-variant="checklist">
46
+ {list.map((item: string) => (
47
+ <li set:html={item} />
48
+ ))}
49
+ </ul>
50
+ )
51
+ }
52
+ {
53
+ ctas && ctas.length > 0 && (
54
+ <div class="n-tabbed-content-ctas">
55
+ {ctas?.map((cta: CallToActionProps) => (
56
+ <Button id={cta.id} href={cta.href} variant={cta.variant}>
57
+ {cta.text}
58
+ </Button>
59
+ ))}
60
+ </div>
61
+ )
62
+ }
63
+ </div>
64
+ {
65
+ codeBlock && (
66
+ <div class="n-tabbed-content-code">
67
+ {codeBlock.heading && (
68
+ <div class="n-tabbed-content-code-heading">
69
+ {codeBlock.icon && (
70
+ <img
71
+ class="n-tabbed-content-code-icon"
72
+ src={codeBlock.icon}
73
+ alt=""
74
+ width="16"
75
+ height="16"
76
+ aria-hidden="true"
77
+ />
78
+ )}
79
+ {codeBlock.heading}
80
+ </div>
81
+ )}
82
+ <CodeBlock
83
+ class="n-tabbed-content-code-block"
84
+ code={codeBlock.code}
85
+ lang={codeBlock.lang}
86
+ showCopyOnHover
87
+ />
88
+ </div>
89
+ )
90
+ }
91
+ </div>
92
+ </TabPanel>
93
+
94
+ <style>
95
+ .n-tabbed-content-panel {
96
+ container-type: inline-size;
97
+ height: 100%;
98
+ }
99
+
100
+ .n-tabbed-content-wrapper-split {
101
+ display: grid;
102
+ gap: var(--_border-size);
103
+ height: 100%;
104
+ background-color: var(--color-icon); /** Shows through grid gaps to create inner borders */
105
+
106
+ @container (width > 55rem) {
107
+ grid-template-columns: 1fr 1fr;
108
+ }
109
+ }
110
+
111
+ .n-tabbed-content-body {
112
+ padding: var(--space-xl);
113
+ background-color: var(--card-bg-color);
114
+ }
115
+
116
+ .n-tabbed-content-code {
117
+ display: grid;
118
+ grid-template-rows: auto 1fr;
119
+ gap: var(--_border-size);
120
+ background-color: var(--tabbed-content-code-bg-color);
121
+ }
122
+
123
+ .n-tabbed-content-code-heading {
124
+ display: flex;
125
+ gap: var(--space-xs);
126
+ align-items: center;
127
+ padding: var(--space-xs);
128
+ font-family: var(--font-family-mono);
129
+ font-size: var(--font-size-text-md);
130
+ text-transform: uppercase;
131
+ background-color: var(--tabbed-content-code-bg-color);
132
+ border-block-end: var(--_border-size) var(--_border-style) var(--_border-color-active);
133
+ }
134
+
135
+ .n-tabbed-content-code-icon {
136
+ flex-shrink: 0;
137
+ width: 1em;
138
+ height: auto;
139
+ }
140
+
141
+ .n-tabbed-content-code-block {
142
+ --n-code-block-border: none;
143
+ --n-code-block-radius: 0;
144
+ --n-code-bg-color: var(--tabbed-content-code-bg-color);
145
+ flex-grow: 1;
146
+ }
147
+
148
+ .n-tabbed-content-ctas {
149
+ display: grid;
150
+ gap: var(--space-s);
151
+ }
152
+
153
+ @media (min-width: 30rem) {
154
+ .n-tabbed-content-ctas {
155
+ justify-items: start;
156
+ }
157
+ }
158
+ </style>
@@ -11,7 +11,7 @@ interface Props {
11
11
  }
12
12
 
13
13
  const {
14
- as = 'div',
14
+ as: Element = 'div',
15
15
  id,
16
16
  class: className,
17
17
  gap,
@@ -22,18 +22,19 @@ const {
22
22
  ...rest
23
23
  } = Astro.props;
24
24
 
25
- const customStyles = {
26
- ...(gap && { '--n-ticker-gap': gap }),
27
- ...(pauseOnHover && { '--n-ticker-hover-play-state': 'paused' }),
28
- ...(speed && { '--n-ticker-duration': speed + 's' }),
29
- ...(scrollY && {
30
- '--n-ticker-animation': 'scroll-y',
31
- '--n-ticker-direction': 'column',
32
- '--n-ticker-mask-direction': 'to bottom',
33
- }),
34
- };
35
-
36
- const Element = as;
25
+ const customStyles =
26
+ gap || pauseOnHover || speed || scrollY
27
+ ? {
28
+ ...(gap && { '--n-ticker-gap': gap }),
29
+ ...(pauseOnHover && { '--n-ticker-hover-play-state': 'paused' }),
30
+ ...(speed && { '--n-ticker-duration': speed + 's' }),
31
+ ...(scrollY && {
32
+ '--n-ticker-animation': 'scroll-y',
33
+ '--n-ticker-direction': 'column',
34
+ '--n-ticker-mask-direction': 'to bottom',
35
+ }),
36
+ }
37
+ : null;
37
38
  ---
38
39
 
39
40
  <Element
@@ -9,9 +9,7 @@ interface Props {
9
9
  class?: string;
10
10
  }
11
11
 
12
- const { as = 'div', id, iconSrc, text, class: className, ...rest } = Astro.props;
13
-
14
- const Element = as;
12
+ const { as: Element = 'div', id, iconSrc, text, class: className, ...rest } = Astro.props;
15
13
  ---
16
14
 
17
15
  <Element {id} class:list={['n-trust-bar-ticker-item', className]} {...rest}>
@@ -1,6 +1,6 @@
1
1
  import { useRef } from 'preact/hooks';
2
2
  import { KapaProvider } from '@kapaai/react-sdk';
3
- import Analytics from '@analtyics/analytics';
3
+ import Analytics from '../../../analytics/analytics';
4
4
  import ChatInterface from './components/ChatInterface';
5
5
  import './AskNetlify.css';
6
6
 
@@ -9,7 +9,7 @@ interface AskNetlifyProps {
9
9
  }
10
10
 
11
11
  export default function AskNetlify(props: AskNetlifyProps) {
12
- const analyticsRef = useRef<Analytics>();
12
+ const analyticsRef = useRef<Analytics>(new Analytics());
13
13
  return (
14
14
  <KapaProvider
15
15
  integrationId="3c0789a6-c6a8-4fbc-b0cc-235f9e4a6351"
@@ -173,7 +173,7 @@ ol li::marker {
173
173
  }
174
174
 
175
175
  li + li {
176
- margin-block-start: var(--space-s);
176
+ margin-block-start: var(--space-2xs);
177
177
  }
178
178
  }
179
179
 
@@ -61,3 +61,28 @@
61
61
  flex-grow: 999;
62
62
  min-inline-size: var(--n-sidebar-min, 60%);
63
63
  }
64
+
65
+ .n-breakout {
66
+ --_full: minmax(var(--page-gutter), 1fr);
67
+ --_content: min(
68
+ var(--n-breakout-max, var(--page-content-max-width)),
69
+ 100% - var(--page-gutter) * 2
70
+ );
71
+ --_popout: minmax(0, 2rem);
72
+ --_feature: minmax(0, 5rem);
73
+
74
+ display: grid;
75
+ place-content: start;
76
+ grid-template-columns:
77
+ [full-start] var(--_full)
78
+ [feature-start] var(--_feature)
79
+ [popout-start] var(--_popout)
80
+ [content-start] var(--_content) [content-end]
81
+ var(--_popout) [popout-end]
82
+ var(--_feature) [feature-end]
83
+ var(--_full) [full-end];
84
+ }
85
+
86
+ .n-breakout > * {
87
+ grid-column: var(--n-breakout-area, content);
88
+ }
@@ -119,6 +119,8 @@
119
119
  --neutral-700: color(srgb 0.20784313725490197 0.22745098039215686 0.24313725490196078);
120
120
  --neutral-800: color(srgb 0.09411764705882353 0.10196078431372549 0.10980392156862745);
121
121
  --neutral-900: color(srgb 0.023529411764705882 0.043137254901960784 0.06274509803921569);
122
+ --page-content-max-width: 80rem;
123
+ --page-gutter: var(--space-l);
122
124
  --pink-000: color(srgb 0.9882352941176471 0.9411764705882353 0.984313725490196);
123
125
  --pink-100: color(srgb 0.9647058823529412 0.8235294117647058 0.9490196078431372);
124
126
  --pink-200: color(srgb 0.9686274509803922 0.7372549019607844 0.9529411764705882);
@@ -1,11 +1,11 @@
1
- export type Theme = 'light' | 'dark';
1
+ import { THEMES } from './types';
2
2
 
3
- export const ThemeMap: Record<Theme, Theme> = {
3
+ export const ThemeMap: Record<THEMES, THEMES> = {
4
4
  light: 'dark',
5
5
  dark: 'light',
6
6
  };
7
7
 
8
- export const handleThemeChange = (value: Theme) => {
8
+ export const handleThemeChange = (value: THEMES) => {
9
9
  const themeSelect = document.getElementById('site-theme-select') as HTMLSelectElement;
10
10
  if (themeSelect) {
11
11
  themeSelect.value = value;
@@ -1,3 +1,4 @@
1
+ export type THEMES = 'light' | 'dark';
1
2
  export type BUTTON_VARIANTS = 'primary' | 'secondary' | 'outline' | 'ghost';
2
3
 
3
4
  export interface CallToActionProps {
@@ -6,3 +7,10 @@ export interface CallToActionProps {
6
7
  href: string;
7
8
  variant?: BUTTON_VARIANTS;
8
9
  }
10
+
11
+ export interface ImageProps {
12
+ src: string;
13
+ alt: string;
14
+ width: string;
15
+ height: string;
16
+ }
@@ -1,185 +0,0 @@
1
- ---
2
- import { CallToActionProps } from '@utils/types';
3
- import TabbedContent from '@netlify/spark-ui/components/TabbedContent.astro';
4
- import Tab from '@netlify/spark-ui/components/Tab.astro';
5
- import TabList from '@netlify/spark-ui/components/TabList.astro';
6
- import TabPanel from '@netlify/spark-ui/components/TabPanel.astro';
7
- import TabPanelGroup from '@netlify/spark-ui/components/TabPanelGroup.astro';
8
- import Button from '@netlify/spark-ui/components/Button.astro';
9
- import HeadingBlock from '@netlify/spark-ui/components/HeadingBlock.astro';
10
- import CodeBlock from './CodeBlock.astro';
11
-
12
- interface CodeBlockProps {
13
- heading?: string;
14
- icon?: string;
15
- code: string;
16
- lang: string;
17
- }
18
-
19
- interface ItemProps {
20
- title: string;
21
- icon?: string;
22
- isSelected?: boolean;
23
- heading?: string;
24
- text?: string;
25
- list?: string[];
26
- ctas?: CallToActionProps[];
27
- codeBlock?: CodeBlockProps;
28
- }
29
-
30
- interface Props {
31
- class?: string;
32
- id?: string;
33
- eyebrow?: string;
34
- heading?: string;
35
- items: ItemProps[];
36
- }
37
-
38
- const { id, eyebrow, heading, items, class: className, ...rest } = Astro.props;
39
- ---
40
-
41
- <section id={id} class:list={['n-section-tabbed-content', className]} {...rest}>
42
- <HeadingBlock eyebrow={eyebrow} heading={heading} />
43
- <TabbedContent>
44
- <TabList>
45
- {
46
- items.map(({ title, icon, isSelected }: ItemProps) => (
47
- <Tab isSelected={isSelected}>
48
- <img src={`${icon}`} alt="" width="16" height="16" />
49
- {title}
50
- </Tab>
51
- ))
52
- }
53
- </TabList>
54
- <TabPanelGroup isUniformHeight noPadding>
55
- {
56
- items.map(({ heading, list, text, ctas, codeBlock, isSelected }: ItemProps) => (
57
- <TabPanel class="n-section-tabbed-content-panel" isHidden={!isSelected}>
58
- <div
59
- class:list={[
60
- 'n-section-tabbed-content-panel-content',
61
- codeBlock && 'n-section-tabbed-content-wrapper-split',
62
- ]}
63
- >
64
- <div class="n-section-tabbed-content-body n-flow">
65
- {heading && <h3 class="h4">{heading}</h3>}
66
- {text && <p set:html={text} />}
67
- {list && list.length > 0 && (
68
- <ul data-list-variant="checklist">
69
- {list.map(item => (
70
- <li set:html={item} />
71
- ))}
72
- </ul>
73
- )}
74
- {ctas && ctas.length > 0 && (
75
- <div class="n-section-tabbed-content-ctas">
76
- {ctas?.map((cta: CallToActionProps) => (
77
- <Button id={cta.id} href={cta.href} variant={cta.variant}>
78
- {cta.text}
79
- </Button>
80
- ))}
81
- </div>
82
- )}
83
- </div>
84
- {codeBlock && (
85
- <div class="n-section-tabbed-content-code">
86
- {codeBlock.heading && (
87
- <div class="n-section-tabbed-content-code-heading">
88
- {codeBlock.icon && (
89
- <img
90
- class="n-section-tabbed-content-code-icon"
91
- src={codeBlock.icon}
92
- alt=""
93
- width="16"
94
- height="16"
95
- aria-hidden="true"
96
- />
97
- )}
98
- {codeBlock.heading}
99
- </div>
100
- )}
101
- <CodeBlock
102
- class="n-section-tabbed-content-code-block"
103
- code={codeBlock.code}
104
- lang={codeBlock.lang}
105
- showCopyOnHover
106
- />
107
- </div>
108
- )}
109
- </div>
110
- </TabPanel>
111
- ))
112
- }
113
- </TabPanelGroup>
114
- </TabbedContent>
115
- </section>
116
-
117
- <style>
118
- .n-section-tabbed-content-panel {
119
- container-type: inline-size;
120
- }
121
-
122
- .n-section-tabbed-content-wrapper-split {
123
- display: grid;
124
- grid-template-rows: auto 1fr;
125
- height: 100%;
126
- }
127
-
128
- .n-section-tabbed-content-body {
129
- padding: var(--space-xl);
130
- }
131
-
132
- .n-section-tabbed-content-code {
133
- display: flex;
134
- flex-direction: column;
135
- }
136
-
137
- .n-section-tabbed-content-code-heading {
138
- display: flex;
139
- gap: var(--space-xs);
140
- align-items: center;
141
- margin-block-start: calc(var(--_border-size) * -1); /* overlap the parent border */
142
- padding: var(--space-xs);
143
- font-family: var(--font-family-mono);
144
- font-size: var(--font-size-text-md);
145
- text-transform: uppercase;
146
- background-color: var(--tabbed-content-code-bg-color);
147
- border-block: var(--_border-size) var(--_border-style) var(--tabbed-content-border-color);
148
- }
149
-
150
- .n-section-tabbed-content-code-icon {
151
- flex-shrink: 0;
152
- width: 1em;
153
- height: auto;
154
- }
155
-
156
- .n-section-tabbed-content-code-block {
157
- --n-code-block-border: none;
158
- --n-code-block-radius: 0;
159
- --n-code-block-bg-color: var(--tabbed-content-code-bg-color);
160
- flex-grow: 1;
161
- }
162
-
163
- .n-section-tabbed-content-ctas {
164
- display: grid;
165
- gap: var(--space-s);
166
- }
167
-
168
- @media (min-width: 30rem) {
169
- .n-section-tabbed-content-ctas {
170
- justify-items: start;
171
- }
172
- }
173
-
174
- @container (width > 55rem) {
175
- .n-section-tabbed-content-wrapper-split {
176
- grid-template-columns: 1fr 1fr;
177
- grid-template-rows: 1fr;
178
- }
179
-
180
- .n-section-tabbed-content-code {
181
- border-inline-start: var(--_border-size) var(--_border-style)
182
- var(--tabbed-content-border-color);
183
- }
184
- }
185
- </style>