@netlify/spark-ui 1.26.0-alpha.6 → 1.26.0-alpha.8

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.
@@ -1 +1 @@
1
- .n-fadeIn{opacity:0;animation:fadeIn var(--animation-duration, .3s) ease-out forwards}.n-fadeInUp{opacity:0;transform:translateY(var(--animation-transformY, 5px));animation:fadeInUp var(--animation-duration, .3s) ease-out forwards}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}@keyframes fadeInUp{to{opacity:1;transform:translateY(0)}}.n-dropzone{--n-dropzone-accent: var(--color-brand-primary, #0e7fe1);--n-dropzone-radius: var(--radius-2xl, 1rem);box-sizing:border-box;position:relative;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:var(--space-s);width:100%;height:100%;min-height:18rem;padding:var(--space-2xl, 2.5rem) var(--space-l, 1.5rem);text-align:center;color:var(--color-text-default, #14161a);background:var(--color-bg, #fff);border:2px dashed var(--color-border-default, rgba(0, 0, 0, .15));border-radius:var(--n-dropzone-radius);transition:border-color .2s ease-out,background-color .2s ease-out,box-shadow .2s ease-out}.n-dropzone.is-active{border-color:var(--n-dropzone-accent);background:var(--color-bg-2, #f6f8fa);box-shadow:0 0 0 4px color-mix(in srgb,var(--n-dropzone-accent) 18%,transparent)}.n-dropzone.is-busy{border-style:solid;border-color:var(--color-border-subtle, rgba(0, 0, 0, .08))}.n-dropzone:before{content:"";position:absolute;top:0;right:0;bottom:0;left:0;border-radius:inherit;background:radial-gradient(120% 120% at 50% 0%,color-mix(in srgb,var(--n-dropzone-accent) 8%,transparent),transparent 60%);opacity:0;transition:opacity .2s ease-out;pointer-events:none}.n-dropzone.is-active:before{opacity:1}.n-dropzone-panel{display:flex;flex-direction:column;align-items:center;gap:var(--space-xs, .75rem);width:100%;animation:dropPanelIn .32s cubic-bezier(.22,1,.36,1) both}@keyframes dropPanelIn{0%{opacity:0;transform:translateY(8px) scale(.98)}to{opacity:1;transform:none}}.n-dropzone-title{margin:0;font-size:1.125rem;font-weight:var(--font-weight-semibold, 600);line-height:var(--line-height-snug, 1.3)}.n-dropzone-title--error{color:var(--color-invalid-text, #d00)}.n-dropzone-subtitle{margin:0;max-width:30rem;font-size:.9375rem;color:var(--color-text-weak, #5c6570);line-height:var(--line-height-text, 1.5)}.n-dropzone-glyph{display:grid;place-items:center;width:3.5rem;height:3.5rem;color:var(--n-dropzone-accent)}.n-dropzone-glyph svg{width:100%;height:100%}.n-dropzone-glyph-arrow{animation:dropArrowBob 2.4s ease-in-out infinite}.n-dropzone.is-active .n-dropzone-glyph-arrow{animation-duration:.9s}@keyframes dropArrowBob{0%,to{transform:translateY(0)}50%{transform:translateY(-3px)}}.n-dropzone-loading{display:flex;flex-direction:column;align-items:center;gap:var(--space-s, 1rem)}.n-dropzone-spinner{width:2.25rem;height:2.25rem;border-radius:var(--radius-pill, 999px);border:3px solid color-mix(in srgb,var(--n-dropzone-accent) 22%,transparent);border-top-color:var(--n-dropzone-accent);animation:dropSpin .72s linear infinite}@keyframes dropSpin{to{transform:rotate(360deg)}}.n-dropzone-nudge{display:block;font-size:.8125rem;color:var(--color-text-weaker, #8a929c)}.n-dropzone-progress{width:min(22rem,100%);height:.5rem;border-radius:var(--radius-pill, 999px);background:var(--color-bg-3, rgba(0, 0, 0, .08));overflow:hidden}.n-dropzone-progress-bar{height:100%;border-radius:inherit;background:var(--n-dropzone-accent);transition:width .3s cubic-bezier(.22,1,.36,1)}.n-dropzone-browse{display:inline-flex;align-items:center;justify-content:center;padding:var(--space-2xs, .5rem) var(--space-m, 1.25rem);border:none;border-radius:var(--radius-pill, 999px);background:var(--n-dropzone-accent);color:var(--neutral-000, #fff);font-size:.9375rem;font-weight:var(--font-weight-semibold, 600);line-height:var(--line-height-none, 1);text-decoration:none;cursor:pointer;transition:background-color .15s ease-out,transform .12s ease-out}.n-dropzone-browse:hover{background:var(--color-brand-primary-hover, #0b6bc0);transform:translateY(-1px)}.n-dropzone-browse:active{transform:translateY(0)}.n-dropzone-warning{display:flex;align-items:center;gap:var(--space-2xs, .5rem);margin:0;margin-block-start:var(--space-xs, .75rem);padding:var(--space-2xs, .5rem) var(--space-s, 1rem);max-width:32rem;border-radius:var(--radius-m, .5rem);background:var(--color-warning-bg, #fdf3d7);color:var(--color-warning-text, #7a5b00);font-size:.8125rem;text-align:left}.n-dropzone-warning-icon{flex:none;width:1rem;height:1rem}@media (prefers-reduced-motion: reduce){.n-dropzone,.n-dropzone-panel,.n-dropzone-glyph-arrow,.n-dropzone-spinner,.n-dropzone-progress-bar{animation:none!important;transition:none!important}.n-dropzone-spinner{border-top-color:var(--n-dropzone-accent)}}
1
+ .n-fadeIn{opacity:0;animation:fadeIn var(--animation-duration, .3s) ease-out forwards}.n-fadeInUp{opacity:0;transform:translateY(var(--animation-transformY, 5px));animation:fadeInUp var(--animation-duration, .3s) ease-out forwards}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}@keyframes fadeInUp{to{opacity:1;transform:translateY(0)}}.n-dropzone{--n-dropzone-accent: var(--color-brand-primary, #0e7fe1);--n-dropzone-radius: var(--radius-2xl, 1rem);box-sizing:border-box;position:relative;min-height:4rem;color:var(--color-text-default, #14161a);border:2px dashed var(--color-border-default, rgba(0, 0, 0, .15));border-radius:var(--n-dropzone-radius);outline:none;transition:border-color .2s ease-out,background-color .2s ease-out,box-shadow .2s ease-out}.n-dropzone:focus-visible{border-color:var(--n-dropzone-accent);box-shadow:0 0 0 3px color-mix(in srgb,var(--n-dropzone-accent) 40%,transparent)}.n-dropzone.is-active{border-color:var(--n-dropzone-accent);box-shadow:0 0 0 4px color-mix(in srgb,var(--n-dropzone-accent) 18%,transparent)}.n-dropzone.is-busy{border-style:solid;border-color:var(--color-border-subtle, rgba(0, 0, 0, .08))}.n-dropzone-content{transition:opacity .15s ease-out}.n-dropzone.is-active:not(.is-bare) .n-dropzone-content,.n-dropzone.is-busy:not(.is-bare) .n-dropzone-content{opacity:0;pointer-events:none}.n-dropzone-overlay{position:absolute;top:0;right:0;bottom:0;left:0;z-index:1;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:var(--space-s, 1rem);padding:var(--space-l, 1.5rem);text-align:center;border-radius:inherit;pointer-events:none;background:color-mix(in srgb,var(--n-dropzone-accent) 10%,transparent)}.n-dropzone.is-busy .n-dropzone-overlay,.n-dropzone[data-state=error] .n-dropzone-overlay{pointer-events:auto;background:color-mix(in srgb,var(--color-bg, #fff) 88%,transparent);-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px)}.n-dropzone-hint{margin:0;font-size:1.0625rem;font-weight:var(--font-weight-semibold, 600);color:var(--n-dropzone-accent)}.n-dropzone-title{margin:0;font-size:1.125rem;font-weight:var(--font-weight-semibold, 600);line-height:var(--line-height-snug, 1.3)}.n-dropzone-title--error{color:var(--color-invalid-text, #d00)}.n-dropzone-subtitle{margin:0;max-width:30rem;font-size:.9375rem;color:var(--color-text-weak, #5c6570);line-height:var(--line-height-text, 1.5)}.n-dropzone-loading{display:flex;flex-direction:column;align-items:center;gap:var(--space-s, 1rem)}.n-dropzone-spinner{width:2.25rem;height:2.25rem;border-radius:var(--radius-pill, 999px);border:3px solid color-mix(in srgb,var(--n-dropzone-accent) 22%,transparent);border-top-color:var(--n-dropzone-accent);animation:dropSpin .72s linear infinite}@keyframes dropSpin{to{transform:rotate(360deg)}}.n-dropzone-nudge{display:block;font-size:.8125rem;color:var(--color-text-weaker, #8a929c)}.n-dropzone-progress{width:min(22rem,100%);height:.5rem;border-radius:var(--radius-pill, 999px);background:var(--color-bg-3, rgba(0, 0, 0, .08));overflow:hidden}.n-dropzone-progress-bar{height:100%;border-radius:inherit;background:var(--n-dropzone-accent);transition:width .3s cubic-bezier(.22,1,.36,1)}.n-dropzone-browse{display:inline-flex;align-items:center;justify-content:center;padding:var(--space-2xs, .5rem) var(--space-m, 1.25rem);border:none;border-radius:var(--radius-pill, 999px);background:var(--n-dropzone-accent);color:var(--neutral-000, #fff);font-size:.9375rem;font-weight:var(--font-weight-semibold, 600);line-height:var(--line-height-none, 1);text-decoration:none;cursor:pointer;transition:background-color .15s ease-out,transform .12s ease-out}.n-dropzone-browse:hover{background:var(--color-brand-primary-hover, #0b6bc0);transform:translateY(-1px)}.n-dropzone-browse:active{transform:translateY(0)}.n-dropzone-warning{margin:0;padding:var(--space-2xs, .5rem) var(--space-s, 1rem);max-width:32rem;border-radius:var(--radius-m, .5rem);background:var(--color-warning-bg, #fdf3d7);color:var(--color-warning-text, #7a5b00);font-size:.8125rem}@media (prefers-reduced-motion: reduce){.n-dropzone,.n-dropzone-overlay,.n-dropzone-spinner,.n-dropzone-progress-bar{animation:none!important;transition:none!important}.n-dropzone-spinner{border-top-color:var(--n-dropzone-accent)}}
@@ -0,0 +1,40 @@
1
+ export interface FilterOption {
2
+ value: string;
3
+ label: string;
4
+ }
5
+ export interface FilterCategory {
6
+ key: string;
7
+ label: string;
8
+ legend?: string;
9
+ control?: 'checkbox' | 'radio';
10
+ /** Radio-only: prepend a checked `value=""` reset row. */
11
+ anyOption?: {
12
+ label: string;
13
+ };
14
+ matchMode?: 'any' | 'all';
15
+ options: FilterOption[];
16
+ }
17
+ export interface FilterableGridProps {
18
+ filters: FilterCategory[];
19
+ results: {
20
+ singular: string;
21
+ plural: string;
22
+ };
23
+ /** Server-rendered initial count, shown before any client JS runs. */
24
+ totalCount: number;
25
+ /** Global match mode; per-category `matchMode` overrides it for that category. */
26
+ matchMode: 'any' | 'all';
27
+ id?: string;
28
+ }
29
+ /**
30
+ * Builds the `data-filter-{key}` attributes a slotted grid item needs so
31
+ * `<filter-container>` can match it against the active facets. Spread the
32
+ * result onto whatever bespoke element you render inside <FilterableGrid>.
33
+ *
34
+ * @example
35
+ * <article {...getFilterAttrs({ topic: ['react', 'astro'], level: 'beginner' })}>
36
+ * ...
37
+ * </article>
38
+ * // -> data-filter-topic="react,astro" data-filter-level="beginner"
39
+ */
40
+ export declare function getFilterAttrs(values: Record<string, string | string[]>, delimiter?: string): Record<`data-filter-${string}`, string>;
@@ -0,0 +1,2 @@
1
+ export { default as FilterableGrid } from './FilterableGrid.astro';
2
+ export * from './filter-types';
@@ -1,5 +1,9 @@
1
+ import { ComponentChildren } from 'preact';
1
2
  import { DropClient } from './drop-core';
3
+ import { DeployedSite, TrackFn } from './useDropDeploy';
2
4
  interface Props {
5
+ /** Content the drop layer sits on top of. The zone is invisible until you drag over it. */
6
+ children?: ComponentChildren;
3
7
  /** Override the API base (e.g. for a staging environment). */
4
8
  apiBase?: string;
5
9
  /** Inject a custom client (e.g. for testing). Defaults to AnonymousDropClient. */
@@ -7,13 +11,31 @@ interface Props {
7
11
  /** Build the "claim this site" URL from the assigned site name (subdomain). */
8
12
  claimUrl?: (siteName: string) => string;
9
13
  /** Called once the deploy is live. */
10
- onDeploy?: (result: {
11
- url: string;
12
- token: string;
13
- siteName: string;
14
- siteId: string;
15
- }) => void;
14
+ onDeploy?: (result: DeployedSite) => void;
15
+ /**
16
+ * Render the built-in status overlay (drag tint, spinner, progress bar, error).
17
+ * Set to `false` to suppress it entirely and keep your children visible, then
18
+ * drive your own feedback from the `data-state` attribute on the root element.
19
+ * Note: in this mode you're responsible for surfacing errors yourself.
20
+ */
21
+ showStatus?: boolean;
22
+ /**
23
+ * Analytics hook. Called at each tracked moment with an event name and
24
+ * properties. Stays provider-agnostic — wire it to Segment/Amplitude/GA in the
25
+ * consuming app, e.g. `onTrack={(name, props) => new Analytics().track(name, props)}`.
26
+ * Emitted events (all include `dropzone_id` when `analyticsId` is set):
27
+ * - `dropzone_files_dropped` — files dropped via drag ({ method: 'drag', file_count })
28
+ * - `dropzone_browse_opened` — file picker opened ({ method: 'keyboard' | 'click' })
29
+ * - `dropzone_deploy_succeeded` — deploy went live ({ method, site_name, file_count })
30
+ */
31
+ onTrack?: TrackFn;
32
+ /**
33
+ * Stable identifier for this instance, attached to every tracked event as
34
+ * `dropzone_id`. Set it when a page renders more than one Drop Zone so the
35
+ * events can be told apart in Amplitude.
36
+ */
37
+ analyticsId?: string;
16
38
  className?: string;
17
39
  }
18
- export default function DropZone({ apiBase, client, claimUrl, onDeploy, className, }: Props): import("preact").JSX.Element;
40
+ export default function DropZone({ children, apiBase, client, claimUrl, onDeploy, showStatus, onTrack, analyticsId, className, }: Props): import("preact").JSX.Element;
19
41
  export {};
@@ -1,135 +1,133 @@
1
1
  import { u as e } from "../../../chunks/jsxRuntime.module.Bzuv3cXw.js";
2
- import { d as m, A as C } from "../../../chunks/hooks.module.DRcxknIz.js";
3
- import I from "../Icon/index.js";
4
- import { getSingleNonIndexHtmlFileName as N, hasMultipleHtmlFilesWithoutIndex as D, readDataTransfer as F, readFileList as T } from "./drop-core/readFiles.js";
5
- import { AnonymousDropClient as M } from "./drop-core/client.js";
6
- import { deployFiles as P } from "./drop-core/deploy.js";
7
- import { Fragment as k } from "preact";
8
- import '../../../assets/DropZone.css';const U = (n) => `https://app.netlify.com/drop/${n}`;
9
- function A(n) {
10
- switch (n.phase) {
11
- case "creating":
12
- return { kind: "creating" };
13
- case "uploading":
14
- return { kind: "uploading", uploaded: n.uploaded ?? 0, total: n.total ?? 0 };
15
- case "processing":
16
- case "ready":
17
- return { kind: "processing" };
18
- }
19
- }
20
- function O({
21
- apiBase: n,
22
- client: p,
23
- claimUrl: u = U,
24
- onDeploy: i,
25
- className: s
2
+ import { A as k, d as S, y as $ } from "../../../chunks/hooks.module.DRcxknIz.js";
3
+ import { readFileList as E, readDataTransfer as I } from "./drop-core/readFiles.js";
4
+ import { useDropDeploy as P } from "./useDropDeploy.js";
5
+ import { Fragment as D } from "preact";
6
+ import '../../../assets/DropZone.css';const x = 120, F = (n) => `https://app.netlify.com/drop/${n}`;
7
+ function K({
8
+ children: n,
9
+ apiBase: l,
10
+ client: i,
11
+ claimUrl: s = F,
12
+ onDeploy: u,
13
+ showStatus: f = !0,
14
+ onTrack: d,
15
+ analyticsId: g,
16
+ className: w
26
17
  }) {
27
- const [o, a] = m({ kind: "idle" }), [w, d] = m(null), [v, g] = m(!1), z = C(p ?? new M(n)).current, h = o.kind !== "idle" && o.kind !== "error";
28
- async function y(t) {
29
- d(null), a({ kind: "reading" });
30
- try {
31
- const r = await t();
32
- if (!r.length) {
33
- a({ kind: "error", message: "No files found to deploy." });
34
- return;
35
- }
36
- const b = N(r);
37
- b ? d(`No index.html found — "${b}" won’t be served at the site root.`) : D(r) && d("No index.html found — your site may not have a landing page.");
38
- const { url: $, token: f, deploy: c } = await P(z, r, {
39
- onProgress: (x) => a(A(x))
40
- });
41
- try {
42
- localStorage.setItem("drop-token", f), localStorage.setItem("dropSiteName", c.subdomain);
43
- } catch {
44
- }
45
- a({ kind: "redirecting" }), i == null || i({ url: $, token: f, siteName: c.subdomain, siteId: c.id }), window.location.assign(`${u(c.subdomain)}#drop_token=${f}`);
46
- } catch (r) {
47
- a({ kind: "error", message: r instanceof Error ? r.message : String(r) });
48
- }
49
- }
50
- function S() {
51
- d(null), a({ kind: "idle" });
52
- }
18
+ const v = (r, t = {}) => d == null ? void 0 : d(r, g ? { dropzone_id: g, ...t } : t), { status: a, warning: h, busy: o, deploy: b, reset: A } = P({
19
+ apiBase: l,
20
+ client: i,
21
+ claimUrl: s,
22
+ onDeploy: u,
23
+ track: v
24
+ }), { active: p, keepActive: C, endDrag: T } = M(), m = k(null), y = (r) => {
25
+ var t;
26
+ v("dropzone_browse_opened", { method: r }), (t = m.current) == null || t.click();
27
+ }, _ = f && (p || o || a.kind === "error"), R = [
28
+ "n-dropzone",
29
+ p && "is-active",
30
+ o && "is-busy",
31
+ !f && "is-bare",
32
+ w
33
+ ].filter(Boolean).join(" ");
53
34
  return /* @__PURE__ */ e(
54
35
  "div",
55
36
  {
56
- class: `n-dropzone${v ? " is-active" : ""}${h ? " is-busy" : ""}${s ? ` ${s}` : ""}`,
57
- "data-state": o.kind,
58
- onDragOver: (t) => {
59
- h || (t.preventDefault(), g(!0));
37
+ class: R,
38
+ "data-state": a.kind,
39
+ role: "button",
40
+ tabIndex: o ? -1 : 0,
41
+ "aria-label": "Drop a site folder or zip here to deploy it, or press Enter to browse for files",
42
+ "aria-busy": o,
43
+ onDragOver: (r) => {
44
+ o || (r.preventDefault(), C());
60
45
  },
61
- onDragLeave: (t) => {
62
- t.currentTarget === t.target && g(!1);
46
+ onDrop: (r) => {
47
+ if (r.preventDefault(), T(), o) return;
48
+ const t = r.dataTransfer;
49
+ t && b(() => I(t), "drag");
63
50
  },
64
- onDrop: (t) => {
65
- if (t.preventDefault(), g(!1), h) return;
66
- const r = t.dataTransfer;
67
- r && y(() => F(r));
51
+ onClick: (r) => {
52
+ !o && r.target === r.currentTarget && y("click");
53
+ },
54
+ onKeyDown: (r) => {
55
+ o || (r.key === "Enter" || r.key === " ") && (r.preventDefault(), y("keyboard"));
68
56
  },
69
57
  children: [
70
- /* @__PURE__ */ e("div", { class: "n-dropzone-panel", children: /* @__PURE__ */ e(L, { status: o, onReset: S, onFiles: y }) }, o.kind),
71
- w && o.kind !== "error" && /* @__PURE__ */ e("p", { class: "n-dropzone-warning n-fadeIn", role: "status", children: [
72
- /* @__PURE__ */ e(I, { name: "lightbulb", className: "n-dropzone-warning-icon" }),
73
- w
74
- ] })
58
+ /* @__PURE__ */ e("div", { class: "n-dropzone-content", children: n }),
59
+ /* @__PURE__ */ e(
60
+ "input",
61
+ {
62
+ ref: m,
63
+ type: "file",
64
+ hidden: !0,
65
+ multiple: !0,
66
+ webkitdirectory: "",
67
+ directory: "",
68
+ onChange: (r) => {
69
+ var z;
70
+ const t = r.currentTarget;
71
+ (z = t.files) != null && z.length && b(() => E(t.files), "browse"), t.value = "";
72
+ }
73
+ }
74
+ ),
75
+ _ && /* @__PURE__ */ e(
76
+ "div",
77
+ {
78
+ class: "n-dropzone-overlay n-fadeIn",
79
+ children: [
80
+ /* @__PURE__ */ e(O, { status: a, active: p, onReset: A }),
81
+ h && a.kind !== "error" && o && /* @__PURE__ */ e("p", { class: "n-dropzone-warning", role: "status", children: h })
82
+ ]
83
+ },
84
+ a.kind === "idle" ? "active" : a.kind
85
+ )
75
86
  ]
76
87
  }
77
88
  );
78
89
  }
79
- function L({
90
+ function M() {
91
+ const [n, l] = S(!1), i = k();
92
+ return $(() => () => clearTimeout(i.current), []), { active: n, keepActive: () => {
93
+ l(!0), clearTimeout(i.current), i.current = setTimeout(() => l(!1), x);
94
+ }, endDrag: () => {
95
+ clearTimeout(i.current), l(!1);
96
+ } };
97
+ }
98
+ function O({
80
99
  status: n,
81
- onReset: p,
82
- onFiles: u
100
+ active: l,
101
+ onReset: i
83
102
  }) {
84
103
  switch (n.kind) {
104
+ // Idle-but-active means a drag is hovering the zone.
85
105
  case "idle":
86
- return /* @__PURE__ */ e(k, { children: [
87
- /* @__PURE__ */ e(R, {}),
88
- /* @__PURE__ */ e("p", { class: "n-dropzone-title", children: "Drag & drop your site folder or zip" }),
89
- /* @__PURE__ */ e("p", { class: "n-dropzone-subtitle", children: "Deploys instantly to a live URL — no account needed." }),
90
- /* @__PURE__ */ e("label", { class: "n-dropzone-browse", children: [
91
- "Browse files",
92
- /* @__PURE__ */ e(
93
- "input",
94
- {
95
- type: "file",
96
- hidden: !0,
97
- multiple: !0,
98
- webkitdirectory: "",
99
- directory: "",
100
- onChange: (i) => {
101
- var o;
102
- const s = i.currentTarget;
103
- (o = s.files) != null && o.length && u(() => T(s.files));
104
- }
105
- }
106
- )
107
- ] })
108
- ] });
106
+ return l ? /* @__PURE__ */ e("p", { class: "n-dropzone-hint", children: "Drop to deploy" }) : null;
109
107
  case "reading":
110
- return /* @__PURE__ */ e(l, { label: "Reading files…" });
108
+ return /* @__PURE__ */ e(c, { label: "Reading files…" });
111
109
  case "creating":
112
- return /* @__PURE__ */ e(l, { label: "Creating deploy…" });
110
+ return /* @__PURE__ */ e(c, { label: "Creating deploy…" });
113
111
  case "processing":
114
- return /* @__PURE__ */ e(l, { label: "Processing — almost live…" });
112
+ return /* @__PURE__ */ e(c, { label: "Processing — almost live…" });
115
113
  case "redirecting":
116
- return /* @__PURE__ */ e(l, { label: "Site’s live — taking you to claim it…" });
114
+ return /* @__PURE__ */ e(c, { label: "Site’s live — taking you to claim it…" });
117
115
  case "uploading": {
118
- const i = n.total ? Math.round(n.uploaded / n.total * 100) : 0;
119
- return /* @__PURE__ */ e(k, { children: [
120
- /* @__PURE__ */ e(l, { label: `Uploading ${n.uploaded}/${n.total} files…` }),
121
- /* @__PURE__ */ e("div", { class: "n-dropzone-progress", role: "progressbar", "aria-valuenow": i, children: /* @__PURE__ */ e("div", { class: "n-dropzone-progress-bar", style: { width: `${i}%` } }) })
116
+ const s = n.total ? Math.round(n.uploaded / n.total * 100) : 0;
117
+ return /* @__PURE__ */ e(D, { children: [
118
+ /* @__PURE__ */ e(c, { label: `Uploading ${n.uploaded}/${n.total} files…` }),
119
+ /* @__PURE__ */ e("div", { class: "n-dropzone-progress", role: "progressbar", "aria-valuenow": s, children: /* @__PURE__ */ e("div", { class: "n-dropzone-progress-bar", style: { width: `${s}%` } }) })
122
120
  ] });
123
121
  }
124
122
  case "error":
125
- return /* @__PURE__ */ e(k, { children: [
123
+ return /* @__PURE__ */ e(D, { children: [
126
124
  /* @__PURE__ */ e("p", { class: "n-dropzone-title n-dropzone-title--error", children: "Something went wrong" }),
127
125
  /* @__PURE__ */ e("p", { class: "n-dropzone-subtitle", role: "alert", children: n.message }),
128
- /* @__PURE__ */ e("button", { type: "button", class: "n-dropzone-browse", onClick: p, children: "Try again" })
126
+ /* @__PURE__ */ e("button", { type: "button", class: "n-dropzone-browse", onClick: i, children: "Try again" })
129
127
  ] });
130
128
  }
131
129
  }
132
- function l({ label: n }) {
130
+ function c({ label: n }) {
133
131
  return /* @__PURE__ */ e("div", { class: "n-dropzone-loading", children: [
134
132
  /* @__PURE__ */ e("span", { class: "n-dropzone-spinner", "aria-hidden": "true" }),
135
133
  /* @__PURE__ */ e("p", { class: "n-dropzone-subtitle", "aria-live": "polite", children: [
@@ -139,31 +137,6 @@ function l({ label: n }) {
139
137
  ] })
140
138
  ] });
141
139
  }
142
- function R() {
143
- return /* @__PURE__ */ e("span", { class: "n-dropzone-glyph", "aria-hidden": "true", children: /* @__PURE__ */ e("svg", { viewBox: "0 0 48 48", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [
144
- /* @__PURE__ */ e(
145
- "path",
146
- {
147
- class: "n-dropzone-glyph-tray",
148
- d: "M8 30v6a4 4 0 0 0 4 4h24a4 4 0 0 0 4-4v-6",
149
- stroke: "currentColor",
150
- "stroke-width": "3",
151
- "stroke-linecap": "round"
152
- }
153
- ),
154
- /* @__PURE__ */ e(
155
- "path",
156
- {
157
- class: "n-dropzone-glyph-arrow",
158
- d: "M24 32V10m0 0-8 8m8-8 8 8",
159
- stroke: "currentColor",
160
- "stroke-width": "3",
161
- "stroke-linecap": "round",
162
- "stroke-linejoin": "round"
163
- }
164
- )
165
- ] }) });
166
- }
167
140
  export {
168
- O as default
141
+ K as default
169
142
  };
@@ -0,0 +1,50 @@
1
+ import { DropClient, ProcessedFile } from './drop-core';
2
+ /** How the files reached the zone — carried through to analytics. */
3
+ export type DeploySource = 'drag' | 'browse';
4
+ /** The live site handed back once a deploy succeeds. */
5
+ export interface DeployedSite {
6
+ url: string;
7
+ token: string;
8
+ siteName: string;
9
+ siteId: string;
10
+ }
11
+ /** Finite states of a single deploy attempt. */
12
+ export type DeployStatus = {
13
+ kind: 'idle';
14
+ } | {
15
+ kind: 'reading';
16
+ } | {
17
+ kind: 'creating';
18
+ } | {
19
+ kind: 'uploading';
20
+ uploaded: number;
21
+ total: number;
22
+ } | {
23
+ kind: 'processing';
24
+ } | {
25
+ kind: 'redirecting';
26
+ } | {
27
+ kind: 'error';
28
+ message: string;
29
+ };
30
+ /** Provider-agnostic analytics sink. */
31
+ export type TrackFn = (event: string, properties?: Record<string, unknown>) => void;
32
+ interface UseDropDeployOptions {
33
+ apiBase?: string;
34
+ client?: DropClient;
35
+ claimUrl: (siteName: string) => string;
36
+ onDeploy?: (site: DeployedSite) => void;
37
+ track: TrackFn;
38
+ }
39
+ /**
40
+ * Owns the deploy state machine and its side effects (upload, local persistence,
41
+ * analytics, redirect), keeping DropZone a thin view over the returned `status`.
42
+ */
43
+ export declare function useDropDeploy({ apiBase, client, claimUrl, onDeploy, track, }: UseDropDeployOptions): {
44
+ status: DeployStatus;
45
+ warning: string | null;
46
+ busy: boolean;
47
+ deploy: (read: () => Promise<ProcessedFile[]>, source: DeploySource) => Promise<void>;
48
+ reset: () => void;
49
+ };
50
+ export {};
@@ -0,0 +1,64 @@
1
+ import { d as c, A as _ } from "../../../chunks/hooks.module.DRcxknIz.js";
2
+ import { getSingleNonIndexHtmlFileName as x, hasMultipleHtmlFilesWithoutIndex as S } from "./drop-core/readFiles.js";
3
+ import { AnonymousDropClient as b } from "./drop-core/client.js";
4
+ import { deployFiles as I } from "./drop-core/deploy.js";
5
+ function F(n) {
6
+ switch (n.phase) {
7
+ case "creating":
8
+ return { kind: "creating" };
9
+ case "uploading":
10
+ return { kind: "uploading", uploaded: n.uploaded ?? 0, total: n.total ?? 0 };
11
+ case "processing":
12
+ case "ready":
13
+ return { kind: "processing" };
14
+ }
15
+ }
16
+ function H(n) {
17
+ const i = x(n);
18
+ return i ? `No index.html found — "${i}" won’t be served at the site root.` : S(n) ? "No index.html found — your site may not have a landing page." : null;
19
+ }
20
+ function A({
21
+ apiBase: n,
22
+ client: i,
23
+ claimUrl: m,
24
+ onDeploy: r,
25
+ track: u
26
+ }) {
27
+ const [d, t] = c({ kind: "idle" }), [g, s] = c(null), p = _(i ?? new b(n)).current, f = d.kind !== "idle" && d.kind !== "error";
28
+ async function h(y, a) {
29
+ s(null), t({ kind: "reading" });
30
+ try {
31
+ const e = await y();
32
+ if (!e.length) {
33
+ t({ kind: "error", message: "No files found to deploy." });
34
+ return;
35
+ }
36
+ a === "drag" && u("dropzone_files_dropped", { method: a, file_count: e.length }), s(H(e));
37
+ const {
38
+ url: w,
39
+ token: l,
40
+ deploy: o
41
+ } = await I(p, e, {
42
+ onProgress: (N) => t(F(N))
43
+ });
44
+ try {
45
+ localStorage.setItem("drop-token", l), localStorage.setItem("dropSiteName", o.subdomain);
46
+ } catch {
47
+ }
48
+ t({ kind: "redirecting" }), u("dropzone_deploy_succeeded", {
49
+ method: a,
50
+ site_name: o.subdomain,
51
+ file_count: e.length
52
+ }), r == null || r({ url: w, token: l, siteName: o.subdomain, siteId: o.id }), window.location.assign(`${m(o.subdomain)}#drop_token=${l}`);
53
+ } catch (e) {
54
+ t({ kind: "error", message: e instanceof Error ? e.message : String(e) });
55
+ }
56
+ }
57
+ function k() {
58
+ s(null), t({ kind: "idle" });
59
+ }
60
+ return { status: d, warning: g, busy: f, deploy: h, reset: k };
61
+ }
62
+ export {
63
+ A as useDropDeploy
64
+ };
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.26.0-alpha.6",
4
+ "version": "1.26.0-alpha.8",
5
5
  "type": "module",
6
6
  "main": "dist/components/preact/index.js",
7
7
  "types": "dist/components/preact/index.d.ts",
@@ -31,6 +31,7 @@
31
31
  "./styles/*.css": "./packages/styles/*.css",
32
32
  "./assets/*": "./packages/assets/*",
33
33
  "./components": "./packages/components/index.ts",
34
+ "./components/FilterableGrid": "./packages/components/FilterableGrid/index.ts",
34
35
  "./components/*": "./packages/components/*",
35
36
  "./utils": "./packages/utils/index.ts",
36
37
  "./analytics": "./packages/analytics/index.ts"
@@ -39,6 +40,8 @@
39
40
  "@astro-community/astro-embed-youtube": "^0.5.10",
40
41
  "@kapaai/react-sdk": "^0.9.0",
41
42
  "@shikijs/transformers": "^3.3.0",
43
+ "@zachleat/details-utils": "^2.0.2",
44
+ "@zachleat/filter-container": "^4.0.0",
42
45
  "fflate": "^0.8.2",
43
46
  "marked": "^15.0.11",
44
47
  "rusha": "^0.8.14"
@@ -8,6 +8,7 @@ interface Props {
8
8
  target?: string;
9
9
  variant?: BUTTON_VARIANTS;
10
10
  href?: string;
11
+ type?: 'button' | 'submit' | 'reset';
11
12
  }
12
13
 
13
14
  const {
@@ -16,6 +17,7 @@ const {
16
17
  variant = 'primary',
17
18
  href,
18
19
  target = !href?.includes(Astro.url.origin) ? '_blank' : undefined,
20
+ type,
19
21
  ...rest
20
22
  } = Astro.props;
21
23
 
@@ -28,6 +30,7 @@ const Element = href ? 'a' : 'button';
28
30
  data-button-variant={variant}
29
31
  {href}
30
32
  {target}
33
+ type={!href ? type : undefined}
31
34
  {...rest}
32
35
  >
33
36
  <slot />