@netlify/spark-ui 1.5.0 → 1.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) 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/Switchback.astro +129 -0
  36. package/packages/components/Ticker.astro +29 -18
  37. package/packages/components/TrustBarTicker.astro +26 -0
  38. package/packages/components/TrustBarTickerItem.astro +54 -0
  39. package/packages/components/preact/AskNetlify/AskNetlify.css +282 -0
  40. package/packages/components/preact/AskNetlify/AskNetlify.tsx +22 -0
  41. package/packages/components/preact/AskNetlify/components/BackButton.tsx +19 -0
  42. package/packages/components/preact/AskNetlify/components/ChatInterface.tsx +79 -0
  43. package/packages/components/preact/AskNetlify/components/ChatMessages.tsx +33 -0
  44. package/packages/components/preact/AskNetlify/components/QuestionList/QuestionList.tsx +27 -0
  45. package/packages/components/preact/AskNetlify/components/QuestionList/questions.ts +10 -0
  46. package/packages/components/preact/Icon/index.tsx +72 -53
  47. package/packages/components/preact/SearchModal/components/SearchHeading/SearchHeading.css +4 -3
  48. package/packages/components/preact/SearchModal/components/SearchInput.tsx +9 -7
  49. package/packages/components/preact/SearchModal/components/SearchLink/SearchLink.css +20 -10
  50. package/packages/components/preact/SearchModal/components/SearchLink/SearchLink.tsx +27 -7
  51. package/packages/components/preact/SearchModal/components/SearchModal/SearchModal.css +68 -29
  52. package/packages/components/preact/SearchModal/components/SearchModal/SearchModal.tsx +59 -30
  53. package/packages/components/preact/SearchModal/components/SearchWindow.tsx +34 -18
  54. package/packages/components/preact/SearchModal/hooks/useKeyboardNav.ts +2 -2
  55. package/packages/components/preact/SearchModal/hooks/useSearchToggles.ts +11 -3
  56. package/packages/components/preact/SearchModal/types/search.types.tsx +5 -2
  57. package/packages/components/preact/SpriteIcon/index.tsx +13 -0
  58. package/packages/components/preact/index.ts +1 -0
  59. package/packages/styles/fonts.css +25 -1
  60. package/packages/styles/layout.css +6 -6
  61. package/packages/styles/tokens.css +72 -24
  62. 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.5.0",
4
+ "version": "1.8.0",
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",
@@ -0,0 +1,129 @@
1
+ ---
2
+ import { CallToActionProps } from '@utils/types';
3
+ import Eyebrow from './Eyebrow.astro';
4
+ import ActionLink from './ActionLink.astro';
5
+
6
+ interface Props {
7
+ id?: string;
8
+ class?: string;
9
+ isReversed?: boolean;
10
+ eyebrow?: string;
11
+ heading?: string;
12
+ text?: string;
13
+ list?: string[];
14
+ cta?: CallToActionProps;
15
+ mediaPattern?: 1 | 2;
16
+ }
17
+
18
+ const {
19
+ eyebrow,
20
+ heading,
21
+ text,
22
+ list,
23
+ cta,
24
+ isReversed,
25
+ mediaPattern = 1,
26
+ id,
27
+ class: className,
28
+ ...rest
29
+ } = Astro.props;
30
+ ---
31
+
32
+ <div
33
+ id={id}
34
+ class:list={['n-switchback n-sidebar-left', isReversed && 'n-switchback-reversed', className]}
35
+ data-theme="dark"
36
+ {...rest}
37
+ >
38
+ <div class="n-switchback-content n-flow">
39
+ {eyebrow && <Eyebrow>{eyebrow}</Eyebrow>}
40
+ {heading && <h3 class="n-switchback-heading h5">{heading}</h3>}
41
+ {text && <p class="text" set:html={text} />}
42
+ {
43
+ list && list.length > 0 && (
44
+ <ul data-list-variant="contained">
45
+ {list.map((item: string) => (
46
+ <li>{item}</li>
47
+ ))}
48
+ </ul>
49
+ )
50
+ }
51
+ {
52
+ cta && (
53
+ <div class="n-switchback-cta">
54
+ <ActionLink id={cta.id} href={cta.href}>
55
+ {cta.text}
56
+ </ActionLink>
57
+ </div>
58
+ )
59
+ }
60
+ </div>
61
+ {
62
+ Astro.slots.has('media') && (
63
+ <div class="n-switchback-media" data-switchback-media-pattern={mediaPattern}>
64
+ <slot name="media" />
65
+ </div>
66
+ )
67
+ }
68
+ </div>
69
+
70
+ <style>
71
+ .n-switchback {
72
+ --n-sidebar-size: 25rem;
73
+ --n-sidebar-min: 50%;
74
+ --n-sidebar-gap: 0;
75
+ overflow: hidden;
76
+ color: var(--color-text);
77
+ background: linear-gradient(
78
+ var(--n-switchback-gradient-angle, 101deg),
79
+ #0d1818 8.17%,
80
+ #0c2a2a 56.72%
81
+ );
82
+ border: 1px solid rgba(208, 255, 254, 0.2);
83
+ border-radius: var(--radius-m);
84
+ }
85
+
86
+ .n-switchback-reversed {
87
+ --n-switchback-gradient-angle: 248deg;
88
+ flex-direction: row-reverse;
89
+ }
90
+
91
+ .n-switchback-content {
92
+ padding: var(--space-l);
93
+ }
94
+
95
+ .n-switchback-heading {
96
+ color: var(--heading-text-color);
97
+ }
98
+
99
+ .n-switchback-media {
100
+ display: grid;
101
+ place-items: center;
102
+ padding: var(--space-xl);
103
+ background: var(--red-400);
104
+ min-height: 10rem;
105
+
106
+ :is(img, picture) {
107
+ max-width: 100%;
108
+ height: auto;
109
+ object-fit: cover;
110
+ }
111
+ }
112
+
113
+ /** Patterns */
114
+ [data-switchback-media-pattern='1'] {
115
+ background:
116
+ url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNzY5IiBoZWlnaHQ9IjQ5MCIgdmlld0JveD0iMCAwIDc2OSA0OTAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHg9IjM4NC4zMjQiIHk9IjEwLjc1NDciIHdpZHRoPSIzMzEuMTY0IiBoZWlnaHQ9IjMzMS4xNjQiIHRyYW5zZm9ybT0icm90YXRlKDQ1IDM4NC4zMjQgMTAuNzU0NykiIHN0cm9rZT0id2hpdGUiLz4KPHJlY3QgeD0iNjIxLjMyNCIgeT0iNy45MjI4NSIgd2lkdGg9IjQ3NCIgaGVpZ2h0PSI0NzQiIHRyYW5zZm9ybT0icm90YXRlKDkwIDYyMS4zMjQgNy45MjI4NSkiIHN0cm9rZT0id2hpdGUiLz4KPHJlY3QgeD0iODU5LjEwMSIgeT0iMjQ0LjkyMyIgd2lkdGg9IjY3MS40MzYiIGhlaWdodD0iNjcxLjQzNiIgdHJhbnNmb3JtPSJyb3RhdGUoMTM1IDg1OS4xMDEgMjQ0LjkyMykiIHN0cm9rZT0id2hpdGUiLz4KPHJlY3QgeD0iNTAxLjUwNCIgeT0iMTI3Ljc0MyIgd2lkdGg9IjIzNC4zNTkiIGhlaWdodD0iMjM0LjM1OSIgdHJhbnNmb3JtPSJyb3RhdGUoOTAgNTAxLjUwNCAxMjcuNzQzKSIgc3Ryb2tlPSJ3aGl0ZSIvPgo8cmVjdCB4PSI1MDEuMjk3IiB5PSIyNDQuOTIzIiB3aWR0aD0iMTY1LjQyMyIgaGVpZ2h0PSIxNjUuNDIzIiB0cmFuc2Zvcm09InJvdGF0ZSgxMzUgNTAxLjI5NyAyNDQuOTIzKSIgc3Ryb2tlPSJ3aGl0ZSIvPgo8cmVjdCB4PSI0NDMuNTA0IiB5PSIzMDQuMTAzIiB3aWR0aD0iMTE4LjM2MSIgaGVpZ2h0PSIxMTguMzYxIiB0cmFuc2Zvcm09InJvdGF0ZSgtMTgwIDQ0My41MDQgMzA0LjEwMykiIHN0cm9rZT0id2hpdGUiLz4KPC9zdmc+Cg=='),
117
+ radial-gradient(circle at 50% 70%, var(--red-500) 0%, var(--purple-300) 100%);
118
+ background-size: cover;
119
+ background-position: center;
120
+ }
121
+
122
+ [data-switchback-media-pattern='2'] {
123
+ background:
124
+ repeating-linear-gradient(-30deg, var(--red-000) 0 1px, transparent 1px 150px),
125
+ radial-gradient(circle at 10% 70%, var(--blue-500) 0%, var(--teal-200) 100%);
126
+ background-size: cover;
127
+ background-position: center;
128
+ }
129
+ </style>
@@ -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>