@netlify/spark-ui 1.6.0 → 1.8.1

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 (59) hide show
  1. package/dist/assets/AskNetlify.css +1 -0
  2. package/dist/assets/SearchHeading.css +1 -1
  3. package/dist/assets/SearchLink.css +1 -1
  4. package/dist/assets/SearchModal.css +1 -1
  5. package/dist/components/preact/AskNetlify/AskNetlify.d.ts +7 -0
  6. package/dist/components/preact/AskNetlify/AskNetlify.js +20 -0
  7. package/dist/components/preact/AskNetlify/components/BackButton.d.ts +5 -0
  8. package/dist/components/preact/AskNetlify/components/BackButton.js +20 -0
  9. package/dist/components/preact/AskNetlify/components/ChatInterface.d.ts +7 -0
  10. package/dist/components/preact/AskNetlify/components/ChatInterface.js +53 -0
  11. package/dist/components/preact/AskNetlify/components/ChatMessages.d.ts +3 -0
  12. package/dist/components/preact/AskNetlify/components/ChatMessages.js +19 -0
  13. package/dist/components/preact/AskNetlify/components/QuestionList/QuestionList.d.ts +4 -0
  14. package/dist/components/preact/AskNetlify/components/QuestionList/QuestionList.js +23 -0
  15. package/dist/components/preact/AskNetlify/components/QuestionList/questions.d.ts +1 -0
  16. package/dist/components/preact/AskNetlify/components/QuestionList/questions.js +10 -0
  17. package/dist/components/preact/Icon/index.d.ts +2 -2
  18. package/dist/components/preact/Icon/index.js +108 -72
  19. package/dist/components/preact/SearchModal/components/SearchInput.d.ts +2 -0
  20. package/dist/components/preact/SearchModal/components/SearchLink/SearchLink.d.ts +1 -1
  21. package/dist/components/preact/SearchModal/components/SearchLink/SearchLink.js +34 -16
  22. package/dist/components/preact/SearchModal/components/SearchModal/SearchModal.js +76 -53
  23. package/dist/components/preact/SearchModal/components/SearchWindow.d.ts +1 -1
  24. package/dist/components/preact/SearchModal/components/SearchWindow.js +49 -29
  25. package/dist/components/preact/SearchModal/hooks/useKeyboardNav.js +6 -6
  26. package/dist/components/preact/SearchModal/hooks/useSearchToggles.d.ts +3 -1
  27. package/dist/components/preact/SearchModal/hooks/useSearchToggles.js +13 -10
  28. package/dist/components/preact/SearchModal/types/search.types.d.ts +6 -1
  29. package/dist/components/preact/SpriteIcon/index.d.ts +7 -0
  30. package/dist/components/preact/SpriteIcon/index.js +7 -0
  31. package/dist/components/preact/index.d.ts +1 -0
  32. package/dist/components/preact/index.js +6 -4
  33. package/dist/utils/theme.d.ts +3 -0
  34. package/package.json +4 -1
  35. package/packages/components/Ticker.astro +29 -18
  36. package/packages/components/TrustBarTicker.astro +26 -0
  37. package/packages/components/TrustBarTickerItem.astro +54 -0
  38. package/packages/components/preact/AskNetlify/AskNetlify.css +282 -0
  39. package/packages/components/preact/AskNetlify/AskNetlify.tsx +22 -0
  40. package/packages/components/preact/AskNetlify/components/BackButton.tsx +19 -0
  41. package/packages/components/preact/AskNetlify/components/ChatInterface.tsx +79 -0
  42. package/packages/components/preact/AskNetlify/components/ChatMessages.tsx +33 -0
  43. package/packages/components/preact/AskNetlify/components/QuestionList/QuestionList.tsx +27 -0
  44. package/packages/components/preact/AskNetlify/components/QuestionList/questions.ts +10 -0
  45. package/packages/components/preact/Icon/index.tsx +72 -53
  46. package/packages/components/preact/SearchModal/components/SearchHeading/SearchHeading.css +4 -3
  47. package/packages/components/preact/SearchModal/components/SearchInput.tsx +9 -7
  48. package/packages/components/preact/SearchModal/components/SearchLink/SearchLink.css +20 -10
  49. package/packages/components/preact/SearchModal/components/SearchLink/SearchLink.tsx +27 -7
  50. package/packages/components/preact/SearchModal/components/SearchModal/SearchModal.css +68 -29
  51. package/packages/components/preact/SearchModal/components/SearchModal/SearchModal.tsx +59 -30
  52. package/packages/components/preact/SearchModal/components/SearchWindow.tsx +34 -18
  53. package/packages/components/preact/SearchModal/hooks/useKeyboardNav.ts +2 -2
  54. package/packages/components/preact/SearchModal/hooks/useSearchToggles.ts +11 -3
  55. package/packages/components/preact/SearchModal/types/search.types.tsx +5 -2
  56. package/packages/components/preact/SpriteIcon/index.tsx +13 -0
  57. package/packages/components/preact/index.ts +1 -0
  58. package/packages/styles/tokens.css +72 -24
  59. package/packages/utils/theme.ts +16 -0
@@ -1,48 +1,68 @@
1
1
  import { u as e } from "../../../../chunks/jsxRuntime.module.Bzuv3cXw.js";
2
- import { T as o } from "../../../../chunks/hooks.module.D9IrxdEh.js";
3
- import t from "./SearchHeading/SearchHeading.js";
2
+ import { T as h } from "../../../../chunks/hooks.module.D9IrxdEh.js";
3
+ import s from "./SearchHeading/SearchHeading.js";
4
4
  import a from "./SearchLink/SearchLink.js";
5
- const h = ({
6
- searchLoading: s,
5
+ import { Fragment as d } from "preact";
6
+ const u = {
7
+ light: "dark",
8
+ dark: "light"
9
+ }, g = (n) => {
10
+ const r = document.getElementById("site-theme-select");
11
+ r && (r.value = n), document.documentElement.setAttribute("data-theme", n), localStorage.setItem("theme", n);
12
+ }, f = ({
13
+ searchLoading: n,
7
14
  searchTerm: r,
8
- results: n
15
+ results: t
9
16
  }) => {
10
- if (s)
11
- return /* @__PURE__ */ e(t, { children: [
17
+ if (n)
18
+ return /* @__PURE__ */ e(s, { children: [
12
19
  "Searching for ",
13
20
  /* @__PURE__ */ e("span", { className: "n-search-term", children: r }),
14
21
  " ..."
15
22
  ] });
16
- if (!n || n.length === 0)
17
- return /* @__PURE__ */ e(t, { children: [
23
+ if (!t || t.length === 0)
24
+ return /* @__PURE__ */ e(s, { children: [
18
25
  "0 results for ",
19
26
  /* @__PURE__ */ e("span", { className: "n-search-term", children: r })
20
27
  ] });
21
- const l = o(() => n.map((i, c) => /* @__PURE__ */ e("li", { className: "n-results-list-item", children: /* @__PURE__ */ e(a, { ...i }) }, c)), [n]);
22
- return /* @__PURE__ */ e("ul", { className: "n-results-list", role: "list", children: l });
28
+ const c = h(() => t.map((l, i) => /* @__PURE__ */ e("li", { className: "n-results-list-item", children: /* @__PURE__ */ e(a, { ...l }) }, i)), [t]);
29
+ return /* @__PURE__ */ e("ul", { className: "n-results-list", role: "list", children: c });
23
30
  };
24
- function p({
25
- searchLoading: s,
26
- searchTerm: r,
27
- results: n,
31
+ function A({
32
+ setModalState: n,
33
+ searchLoading: r,
34
+ searchTerm: t,
35
+ results: c,
28
36
  children: l
29
37
  }) {
30
- return /* @__PURE__ */ e("div", { className: "n-search-window", children: /* @__PURE__ */ e("div", { className: "search-links", children: [
31
- /* @__PURE__ */ e(a, { ...{
32
- icon: "sparkles",
33
- label: "Ask Netlify AI",
34
- url: "https://docs.netlify.com/ask-netlify/"
35
- } }),
36
- r && r.length > 1 ? /* @__PURE__ */ e(
37
- h,
38
+ return /* @__PURE__ */ e("div", { className: "n-search-window", children: /* @__PURE__ */ e("div", { className: "n-search-links", children: t && t.length > 1 ? /* @__PURE__ */ e(f, { searchLoading: r, searchTerm: t, results: c }) : l ? /* @__PURE__ */ e(d, { children: [
39
+ /* @__PURE__ */ e(
40
+ a,
41
+ {
42
+ as: "button",
43
+ icon: "askNetlify",
44
+ label: "Ask Netlify AI",
45
+ onClick: () => {
46
+ n({ current: "ask" });
47
+ }
48
+ }
49
+ ),
50
+ l,
51
+ /* @__PURE__ */ e(s, { children: "Actions" }),
52
+ /* @__PURE__ */ e(
53
+ a,
38
54
  {
39
- searchLoading: s,
40
- searchTerm: r,
41
- results: n
55
+ as: "button",
56
+ icon: "lightbulb",
57
+ label: "Toggle theme",
58
+ onClick: () => {
59
+ const i = window.matchMedia("(prefers-color-scheme: dark)").matches, m = document.documentElement.getAttribute("data-theme"), o = i ? "dark" : m || "light";
60
+ o && g(u[o]);
61
+ }
42
62
  }
43
- ) : l || null
44
- ] }) });
63
+ )
64
+ ] }) : null }) });
45
65
  }
46
66
  export {
47
- p as default
67
+ A as default
48
68
  };
@@ -1,6 +1,6 @@
1
- import { y as l } from "../../../../chunks/hooks.module.D9IrxdEh.js";
2
- function s(r, t, o, c) {
3
- l(() => {
1
+ import { y as f } from "../../../../chunks/hooks.module.D9IrxdEh.js";
2
+ function k(r, t, o, c) {
3
+ f(() => {
4
4
  if (!r) return;
5
5
  const u = (e) => {
6
6
  if (o) {
@@ -12,8 +12,8 @@ function s(r, t, o, c) {
12
12
  e.preventDefault();
13
13
  const d = t.current;
14
14
  if (!d) return;
15
- const n = Array.from(d.querySelectorAll("a")), y = n.findIndex((i) => i === document.activeElement), f = e.key === "ArrowDown" ? (y + 1) % n.length : (y - 1 + n.length) % n.length;
16
- n[f].focus();
15
+ const n = Array.from(d.querySelectorAll(".n-search-link")), y = n.findIndex((l) => l === document.activeElement), i = e.key === "ArrowDown" ? (y + 1) % n.length : (y - 1 + n.length) % n.length;
16
+ n[i].focus();
17
17
  }
18
18
  }
19
19
  };
@@ -21,5 +21,5 @@ function s(r, t, o, c) {
21
21
  }, [r, t, o, c]);
22
22
  }
23
23
  export {
24
- s as useKeyboardNav
24
+ k as useKeyboardNav
25
25
  };
@@ -9,4 +9,6 @@
9
9
  *
10
10
  * @param onOpen - A function to be called when a toggle element is clicked.
11
11
  */
12
- export declare function useSearchToggles(isPagefindLoaded: boolean, onOpen: () => void): void;
12
+ export declare function useSearchToggles(isPagefindLoaded: boolean, onOpen: () => void, setState: (state: {
13
+ current: string;
14
+ }) => void): void;
@@ -1,16 +1,19 @@
1
- import { y as l } from "../../../../chunks/hooks.module.D9IrxdEh.js";
2
- function i(c, r) {
3
- l(() => {
1
+ import { y as a } from "../../../../chunks/hooks.module.D9IrxdEh.js";
2
+ function s(c, t, l) {
3
+ a(() => {
4
4
  if (!c) return;
5
- const t = document.querySelectorAll("[data-ntl-search-toggle]");
6
- if (t.length)
7
- return t.forEach((e) => {
8
- e.removeAttribute("disabled"), e.addEventListener("click", r);
5
+ const r = document.querySelectorAll("[data-ntl-search-toggle]");
6
+ if (r.length)
7
+ return r.forEach((e) => {
8
+ const o = e.dataset.ntlSearchToggle ? e.dataset.ntlSearchToggle : "search";
9
+ e.removeAttribute("disabled"), e.addEventListener("click", () => {
10
+ t(), l({ current: o });
11
+ });
9
12
  }), () => {
10
- t.forEach((e) => e.removeEventListener("click", r));
13
+ r.forEach((e) => e.removeEventListener("click", t));
11
14
  };
12
- }, [c, r]);
15
+ }, [c, t]);
13
16
  }
14
17
  export {
15
- i as useSearchToggles
18
+ s as useSearchToggles
16
19
  };
@@ -1,13 +1,18 @@
1
1
  import { ComponentChildren } from 'preact';
2
2
  import { IconName } from '../../Icon';
3
3
  export interface SearchLinkProps {
4
+ as?: any;
4
5
  icon: IconName;
5
6
  label: string;
6
7
  resource?: 'blog' | 'changelog' | 'docs' | 'guide';
7
- url: string;
8
+ url?: string;
8
9
  excerpt?: string;
10
+ onClick?: () => void;
9
11
  }
10
12
  export interface SearchWindowProps {
13
+ setModalState: (e: {
14
+ current: string;
15
+ }) => void;
11
16
  searchLoading: boolean;
12
17
  searchTerm?: string;
13
18
  results?: SearchLinkProps[];
@@ -0,0 +1,7 @@
1
+ interface IconProps {
2
+ name: string;
3
+ size?: number;
4
+ className?: string;
5
+ }
6
+ export declare function SpriteIcon({ name, size, className }: IconProps): import("preact").JSX.Element;
7
+ export {};
@@ -0,0 +1,7 @@
1
+ import { u as e } from "../../../chunks/jsxRuntime.module.Bzuv3cXw.js";
2
+ function c({ name: i, size: n = 24, className: r = "" }) {
3
+ return /* @__PURE__ */ e("svg", { className: `icon-${i} ${r || ""}`, "aria-hidden": "true", children: /* @__PURE__ */ e("use", { href: `/icons.svg#icon-${i}` }) });
4
+ }
5
+ export {
6
+ c as SpriteIcon
7
+ };
@@ -4,5 +4,6 @@ export { default as Icon } from './Icon';
4
4
  export { default as SearchLink } from './SearchModal/components/SearchLink/SearchLink';
5
5
  export { default as SearchHeading } from './SearchModal/components/SearchHeading/SearchHeading';
6
6
  export { default as SearchToggle } from './SearchModal/components/SearchToggle/SearchToggle';
7
+ export { default as AskNetlify } from './AskNetlify/AskNetlify';
7
8
  export { SearchModal } from './SearchModal';
8
9
  export { type IconName } from './Icon';
@@ -3,16 +3,18 @@ import { default as d } from "./Modal/Modal.js";
3
3
  import { default as m } from "./Icon/index.js";
4
4
  import { default as s } from "./SearchModal/components/SearchLink/SearchLink.js";
5
5
  import { default as x } from "./SearchModal/components/SearchHeading/SearchHeading.js";
6
- import { default as h } from "./SearchModal/components/SearchToggle/SearchToggle.js";
6
+ import { default as i } from "./SearchModal/components/SearchToggle/SearchToggle.js";
7
+ import { default as S } from "./AskNetlify/AskNetlify.js";
7
8
  import "preact";
8
9
  import "../../chunks/hooks.module.D9IrxdEh.js";
9
- import { default as S } from "./SearchModal/components/SearchModal/SearchModal.js";
10
+ import { default as n } from "./SearchModal/components/SearchModal/SearchModal.js";
10
11
  export {
12
+ S as AskNetlify,
11
13
  m as Icon,
12
14
  t as KeyboardKey,
13
15
  d as Modal,
14
16
  x as SearchHeading,
15
17
  s as SearchLink,
16
- S as SearchModal,
17
- h as SearchToggle
18
+ n as SearchModal,
19
+ i as SearchToggle
18
20
  };
@@ -0,0 +1,3 @@
1
+ export type Theme = 'light' | 'dark';
2
+ export declare const ThemeMap: Record<Theme, Theme>;
3
+ export declare const handleThemeChange: (value: Theme) => void;
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.6.0",
4
+ "version": "1.8.1",
5
5
  "type": "module",
6
6
  "main": "dist/components/preact/index.js",
7
7
  "types": "dist/components/preact/index.d.ts",
@@ -25,6 +25,7 @@
25
25
  "exports": {
26
26
  "./preact": "./dist/components/preact/index.js",
27
27
  "./assets/*.css": "./dist/assets/*.css",
28
+ "./assets/icons/*.svg": "./packages/assets/icons/*.svg",
28
29
  "./logos/*.svg": "./packages/assets/logos/*.svg",
29
30
  "./styles/*.css": "./packages/styles/*.css",
30
31
  "./assets/*": "./packages/assets/*",
@@ -34,9 +35,11 @@
34
35
  "./analytics": "./packages/analytics/index.ts"
35
36
  },
36
37
  "dependencies": {
38
+ "@kapaai/react-sdk": "^0.1.3",
37
39
  "@preact/preset-vite": "^2.10.1",
38
40
  "@shikijs/transformers": "^3.3.0",
39
41
  "glob": "^11.0.1",
42
+ "marked": "^15.0.11",
40
43
  "preact": "^10.26.4",
41
44
  "vite": "^6.2.5",
42
45
  "vite-plugin-dts": "^4.5.3",
@@ -1,5 +1,6 @@
1
1
  ---
2
2
  interface Props {
3
+ as?: HTMLElement;
3
4
  id?: string;
4
5
  class?: string;
5
6
  gap?: string;
@@ -10,39 +11,49 @@ interface Props {
10
11
  }
11
12
 
12
13
  const {
14
+ as = 'div',
13
15
  id,
14
16
  class: className,
15
17
  gap,
16
18
  pauseOnHover,
17
19
  reverse,
18
20
  scrollY,
19
- speed = 30,
21
+ speed = 50,
20
22
  ...rest
21
23
  } = Astro.props;
22
24
 
23
- const css = {
24
- 'n-ticker-animation': scrollY ? 'scroll-y' : 'scroll-x',
25
- 'n-ticker-direction': scrollY ? 'column' : 'row',
26
- 'n-ticker-duration': speed + 's',
27
- 'n-ticker-gap': gap || undefined,
28
- 'n-ticker-hover-play-state': pauseOnHover ? 'paused' : 'running',
29
- 'n-ticker-mask-direction': scrollY ? 'to bottom' : 'to right',
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
+ }),
30
34
  };
35
+
36
+ const Element = as;
31
37
  ---
32
38
 
33
- <div id={id} class:list={['ticker', reverse && 'ticker-reverse', className]} {...rest}>
39
+ <Element
40
+ id={id}
41
+ class:list={['n-ticker', reverse && 'n-ticker-reverse', className]}
42
+ style={customStyles}
43
+ {...rest}
44
+ >
34
45
  {
35
46
  Array.from({ length: 2 }, (_, index) => (
36
- <div class="ticker-content" aria-hidden={index !== 0 ? 'true' : undefined}>
47
+ <div class="n-ticker-content" aria-hidden={index !== 0 ? 'true' : undefined}>
37
48
  <slot />
38
49
  </div>
39
50
  ))
40
51
  }
41
- </div>
52
+ </Element>
42
53
 
43
- <style define:vars={css}>
44
- .ticker {
45
- --_gap: var(--n-ticker-gap, 1rem);
54
+ <style>
55
+ .n-ticker {
56
+ --_gap: var(--n-ticker-gap, var(--space-s));
46
57
  display: flex;
47
58
  flex-direction: var(--n-ticker-direction, row);
48
59
  overflow: hidden;
@@ -60,7 +71,7 @@ const css = {
60
71
  );
61
72
  }
62
73
 
63
- .ticker-content {
74
+ .n-ticker-content {
64
75
  flex-shrink: 0;
65
76
  display: flex;
66
77
  flex-direction: var(--n-ticker-direction, row);
@@ -70,7 +81,7 @@ const css = {
70
81
  }
71
82
 
72
83
  /** Animation */
73
- .ticker-content {
84
+ .n-ticker-content {
74
85
  animation-name: var(--n-ticker-animation, scroll-x);
75
86
  animation-duration: var(--n-ticker-duration, 30s);
76
87
  animation-play-state: var(--n-ticker-play-state, running);
@@ -78,11 +89,11 @@ const css = {
78
89
  animation-iteration-count: infinite;
79
90
  }
80
91
 
81
- .ticker:hover .ticker-content {
92
+ .n-ticker:hover .n-ticker-content {
82
93
  animation-play-state: var(--n-ticker-hover-play-state, running);
83
94
  }
84
95
 
85
- .ticker-reverse .ticker-content {
96
+ .n-ticker-reverse .n-ticker-content {
86
97
  animation-direction: reverse;
87
98
  }
88
99
 
@@ -0,0 +1,26 @@
1
+ ---
2
+ import Ticker from './Ticker.astro';
3
+
4
+ interface Props {
5
+ as?: HTMLElement;
6
+ id?: string;
7
+ class?: string;
8
+ pauseOnHover?: boolean;
9
+ reverse?: boolean;
10
+ }
11
+
12
+ const { as, pauseOnHover, reverse, id, class: className, ...rest } = Astro.props;
13
+ ---
14
+
15
+ <Ticker
16
+ as={as}
17
+ id={id}
18
+ gap="var(--space-2xl)"
19
+ pauseOnHover={pauseOnHover}
20
+ reverse={reverse}
21
+ speed={50}
22
+ class={className}
23
+ {...rest}
24
+ >
25
+ <slot />
26
+ </Ticker>
@@ -0,0 +1,54 @@
1
+ ---
2
+ import { Image } from 'astro:assets';
3
+
4
+ interface Props {
5
+ as?: HTMLElement;
6
+ id?: string;
7
+ iconSrc?: string;
8
+ text?: string;
9
+ class?: string;
10
+ }
11
+
12
+ const { as = 'div', id, iconSrc, text, class: className, ...rest } = Astro.props;
13
+
14
+ const Element = as;
15
+ ---
16
+
17
+ <Element {id} class:list={['n-trust-bar-ticker-item', className]} {...rest}>
18
+ {
19
+ iconSrc && (
20
+ <div class="n-trust-bar-ticker-item-icon">
21
+ <Image src={iconSrc} alt="" width="30" height="30" />
22
+ </div>
23
+ )
24
+ }
25
+ <div class="n-trust-bar-ticker-item-content">
26
+ {text ? text : <slot />}
27
+ </div>
28
+ </Element>
29
+
30
+ <style>
31
+ .n-trust-bar-ticker-item {
32
+ display: flex;
33
+ align-items: center;
34
+ gap: var(--space-xs);
35
+ }
36
+
37
+ .n-trust-bar-ticker-item-icon {
38
+ flex-shrink: 0;
39
+ aspect-ratio: 1;
40
+ padding: var(--space-3xs);
41
+ inline-size: 2.5rem;
42
+ border-radius: 50%;
43
+ border: 1px solid var(--color-stroke);
44
+ background: var(--code-bg-color);
45
+ }
46
+
47
+ .n-trust-bar-ticker-item-content :global(:where(img, picture, svg)) {
48
+ width: 100%;
49
+ height: auto;
50
+ max-height: 4rem;
51
+ object-fit: contain;
52
+ filter: var(--filter-invert);
53
+ }
54
+ </style>