@netlify/spark-ui 1.27.6 → 1.27.7

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,6 +1,6 @@
1
1
  import { ComponentChildren } from 'preact';
2
2
  import { DropClient } from './drop-core';
3
- import { DeployedSite, TrackFn } from './useDropDeploy';
3
+ import { DeployedSite, DeployError, TrackFn } from './useDropDeploy';
4
4
  interface Props {
5
5
  /** Content the drop layer sits on top of. The zone is invisible until you drag over it. */
6
6
  children?: ComponentChildren;
@@ -12,11 +12,18 @@ interface Props {
12
12
  claimUrl?: (siteName: string) => string;
13
13
  /** Called once the deploy is live. */
14
14
  onDeploy?: (result: DeployedSite) => void;
15
+ /**
16
+ * Called when a deploy fails, with a humanized `message` (ready to show a
17
+ * user — the same copy the built-in overlay uses) and the raw `reason`. Pair
18
+ * this with `showStatus={false}` to render your own error UI.
19
+ */
20
+ onError?: (error: DeployError) => void;
15
21
  /**
16
22
  * Render the built-in status overlay (drag tint, spinner, progress bar, error).
17
23
  * Set to `false` to suppress it entirely and keep your children visible, then
18
24
  * 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.
25
+ * Note: in this mode you're responsible for surfacing errors yourself — use
26
+ * `onError` to get the message.
20
27
  */
21
28
  showStatus?: boolean;
22
29
  /**
@@ -38,5 +45,5 @@ interface Props {
38
45
  analyticsId?: string;
39
46
  className?: string;
40
47
  }
41
- export default function DropZone({ children, apiBase, client, claimUrl, onDeploy, showStatus, onTrack, analyticsId, className, }: Props): import("preact").JSX.Element;
48
+ export default function DropZone({ children, apiBase, client, claimUrl, onDeploy, onError, showStatus, onTrack, analyticsId, className, }: Props): import("preact").JSX.Element;
42
49
  export {};
@@ -1,52 +1,54 @@
1
1
  import { u as e } from "../../../chunks/jsxRuntime.module.Bzuv3cXw.js";
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";
2
+ import { A as k, d as $, y as E } from "../../../chunks/hooks.module.DRcxknIz.js";
3
+ import { readFileList as I, readDataTransfer as P } from "./drop-core/readFiles.js";
4
+ import { useDropDeploy as x } from "./useDropDeploy.js";
5
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({
6
+ import '../../../assets/DropZone.css';const F = 120, M = (n) => `https://app.netlify.com/drop/${n}`;
7
+ function L({
8
8
  children: n,
9
9
  apiBase: l,
10
10
  client: i,
11
- claimUrl: s = F,
11
+ claimUrl: s = M,
12
12
  onDeploy: u,
13
+ onError: w,
13
14
  showStatus: f = !0,
14
15
  onTrack: d,
15
16
  analyticsId: g,
16
- className: w
17
+ className: A
17
18
  }) {
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
+ 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: C } = x({
19
20
  apiBase: l,
20
21
  client: i,
21
22
  claimUrl: s,
22
23
  onDeploy: u,
24
+ onError: w,
23
25
  track: v
24
- }), { active: p, keepActive: C, endDrag: T } = M(), m = k(null), y = (r) => {
26
+ }), { active: p, keepActive: T, endDrag: _ } = O(), m = k(null), y = (r) => {
25
27
  var t;
26
28
  v("dropzone_browse_opened", { method: r }), (t = m.current) == null || t.click();
27
- }, _ = f && (p || o || a.kind === "error"), R = [
29
+ }, R = f && (p || o || a.kind === "error"), S = [
28
30
  "n-dropzone",
29
31
  p && "is-active",
30
32
  o && "is-busy",
31
33
  !f && "is-bare",
32
- w
34
+ A
33
35
  ].filter(Boolean).join(" ");
34
36
  return /* @__PURE__ */ e(
35
37
  "div",
36
38
  {
37
- class: R,
39
+ class: S,
38
40
  "data-state": a.kind,
39
41
  role: "button",
40
42
  tabIndex: o ? -1 : 0,
41
43
  "aria-label": "Drop a site folder or zip here to deploy it, or press Enter to browse for files",
42
44
  "aria-busy": o,
43
45
  onDragOver: (r) => {
44
- o || (r.preventDefault(), C());
46
+ o || (r.preventDefault(), T());
45
47
  },
46
48
  onDrop: (r) => {
47
- if (r.preventDefault(), T(), o) return;
49
+ if (r.preventDefault(), _(), o) return;
48
50
  const t = r.dataTransfer;
49
- t && b(() => I(t), "drag");
51
+ t && b(() => P(t), "drag");
50
52
  },
51
53
  onClick: (r) => {
52
54
  !o && r.target === r.currentTarget && y("click");
@@ -68,16 +70,16 @@ function K({
68
70
  onChange: (r) => {
69
71
  var z;
70
72
  const t = r.currentTarget;
71
- (z = t.files) != null && z.length && b(() => E(t.files), "browse"), t.value = "";
73
+ (z = t.files) != null && z.length && b(() => I(t.files), "browse"), t.value = "";
72
74
  }
73
75
  }
74
76
  ),
75
- _ && /* @__PURE__ */ e(
77
+ R && /* @__PURE__ */ e(
76
78
  "div",
77
79
  {
78
80
  class: "n-dropzone-overlay n-fadeIn",
79
81
  children: [
80
- /* @__PURE__ */ e(O, { status: a, active: p, onReset: A }),
82
+ /* @__PURE__ */ e(U, { status: a, active: p, onReset: C }),
81
83
  h && a.kind !== "error" && o && /* @__PURE__ */ e("p", { class: "n-dropzone-warning", role: "status", children: h })
82
84
  ]
83
85
  },
@@ -87,15 +89,15 @@ function K({
87
89
  }
88
90
  );
89
91
  }
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);
92
+ function O() {
93
+ const [n, l] = $(!1), i = k();
94
+ return E(() => () => clearTimeout(i.current), []), { active: n, keepActive: () => {
95
+ l(!0), clearTimeout(i.current), i.current = setTimeout(() => l(!1), F);
94
96
  }, endDrag: () => {
95
97
  clearTimeout(i.current), l(!1);
96
98
  } };
97
99
  }
98
- function O({
100
+ function U({
99
101
  status: n,
100
102
  active: l,
101
103
  onReset: i
@@ -138,5 +140,5 @@ function c({ label: n }) {
138
140
  ] });
139
141
  }
140
142
  export {
141
- K as default
143
+ L as default
142
144
  };
@@ -8,6 +8,13 @@ export interface DeployedSite {
8
8
  siteName: string;
9
9
  siteId: string;
10
10
  }
11
+ /** A deploy failure, handed to `onError` so consumers can render their own error UI. */
12
+ export interface DeployError {
13
+ /** Human-readable text ready to show a user — the same copy the built-in overlay displays. */
14
+ message: string;
15
+ /** The raw underlying reason (e.g. "drop failed: 429"), useful for logging. */
16
+ reason: string;
17
+ }
11
18
  /** Finite states of a single deploy attempt. */
12
19
  export type DeployStatus = {
13
20
  kind: 'idle';
@@ -34,13 +41,14 @@ interface UseDropDeployOptions {
34
41
  client?: DropClient;
35
42
  claimUrl: (siteName: string) => string;
36
43
  onDeploy?: (site: DeployedSite) => void;
44
+ onError?: (error: DeployError) => void;
37
45
  track: TrackFn;
38
46
  }
39
47
  /**
40
48
  * Owns the deploy state machine and its side effects (upload, local persistence,
41
49
  * analytics, redirect), keeping DropZone a thin view over the returned `status`.
42
50
  */
43
- export declare function useDropDeploy({ apiBase, client, claimUrl, onDeploy, track, }: UseDropDeployOptions): {
51
+ export declare function useDropDeploy({ apiBase, client, claimUrl, onDeploy, onError, track, }: UseDropDeployOptions): {
44
52
  status: DeployStatus;
45
53
  warning: string | null;
46
54
  busy: boolean;
@@ -1,71 +1,72 @@
1
- import { d as p, A as I } from "../../../chunks/hooks.module.DRcxknIz.js";
2
- import { renameSingleNonIndexHtmlToIndex as S, getSingleNonIndexHtmlFileName as b, hasMultipleHtmlFilesWithoutIndex as z } from "./drop-core/readFiles.js";
3
- import { AnonymousDropClient as F } from "./drop-core/client.js";
4
- import { deployFiles as H } from "./drop-core/deploy.js";
5
- import { humanizeDropError as E } from "./drop-core/errors.js";
6
- function W(e) {
7
- switch (e.phase) {
1
+ import { d as f, A as S } from "../../../chunks/hooks.module.DRcxknIz.js";
2
+ import { renameSingleNonIndexHtmlToIndex as b, getSingleNonIndexHtmlFileName as z, hasMultipleHtmlFilesWithoutIndex as F } from "./drop-core/readFiles.js";
3
+ import { AnonymousDropClient as H } from "./drop-core/client.js";
4
+ import { deployFiles as W } from "./drop-core/deploy.js";
5
+ import { humanizeDropError as $ } from "./drop-core/errors.js";
6
+ function v(n) {
7
+ switch (n.phase) {
8
8
  case "creating":
9
9
  return { kind: "creating" };
10
10
  case "uploading":
11
- return { kind: "uploading", uploaded: e.uploaded ?? 0, total: e.total ?? 0 };
11
+ return { kind: "uploading", uploaded: n.uploaded ?? 0, total: n.total ?? 0 };
12
12
  case "processing":
13
13
  case "ready":
14
14
  return { kind: "processing" };
15
15
  }
16
16
  }
17
- function $(e) {
18
- const d = b(e);
19
- return d ? `No index.html found — "${d}" won’t be served at the site root.` : z(e) ? "No index.html found — your site may not have a landing page." : null;
17
+ function A(n) {
18
+ const r = z(n);
19
+ return r ? `No index.html found — "${r}" won’t be served at the site root.` : F(n) ? "No index.html found — your site may not have a landing page." : null;
20
20
  }
21
- function P({
22
- apiBase: e,
23
- client: d,
24
- claimUrl: f,
25
- onDeploy: l,
26
- track: a
21
+ function q({
22
+ apiBase: n,
23
+ client: r,
24
+ claimUrl: g,
25
+ onDeploy: s,
26
+ onError: a,
27
+ track: u
27
28
  }) {
28
- const [u, t] = p({ kind: "idle" }), [g, c] = p(null), h = I(d ?? new F(e)).current, y = u.kind !== "idle" && u.kind !== "error";
29
- async function k(N, s) {
30
- c(null), t({ kind: "reading" });
31
- const m = (n, i, o) => {
32
- t({ kind: "error", message: n }), a("dropzone_deploy_failed", { method: s, reason: i, ...o });
33
- }, x = (n, i) => {
34
- const o = n instanceof Error ? n.message : String(n);
35
- m(E(n), o, i);
29
+ const [c, i] = f({ kind: "idle" }), [h, m] = f(null), y = S(r ?? new H(n)).current, k = c.kind !== "idle" && c.kind !== "error";
30
+ async function _(x, l) {
31
+ m(null), i({ kind: "reading" });
32
+ const p = (e, o, t) => {
33
+ i({ kind: "error", message: e }), u("dropzone_deploy_failed", { method: l, reason: o, ...t }), a == null || a({ message: e, reason: o });
34
+ }, w = (e, o) => {
35
+ const t = e instanceof Error ? e.message : String(e);
36
+ p($(e), t, o);
36
37
  };
37
38
  try {
38
- const n = S(await N());
39
- if (!n.length) {
40
- m("No files found to deploy.", "No files found to deploy.", { file_count: 0 });
39
+ const e = b(await x());
40
+ if (!e.length) {
41
+ p("No files found to deploy.", "No files found to deploy.", { file_count: 0 });
41
42
  return;
42
43
  }
43
- s === "drag" && a("dropzone_files_dropped", { method: s, file_count: n.length }), c($(n));
44
+ l === "drag" && u("dropzone_files_dropped", { method: l, file_count: e.length }), m(A(e));
44
45
  const {
45
- url: i,
46
- token: o,
47
- deploy: r
48
- } = await H(h, n, {
49
- onProgress: (w) => t(W(w))
46
+ url: o,
47
+ token: t,
48
+ deploy: d
49
+ } = await W(y, e, {
50
+ onProgress: (I) => i(v(I))
50
51
  });
51
52
  try {
52
- localStorage.setItem("drop-token", o), localStorage.setItem("dropSiteName", r.subdomain);
53
+ localStorage.setItem("drop-token", t), localStorage.setItem("dropSiteName", d.subdomain);
53
54
  } catch {
54
55
  }
55
- t({ kind: "redirecting" }), a("dropzone_deploy_succeeded", {
56
- method: s,
57
- site_name: r.subdomain,
58
- file_count: n.length
59
- }), l == null || l({ url: i, token: o, siteName: r.subdomain, siteId: r.id }), window.location.assign(`${f(r.subdomain)}#drop_token=${o}`);
60
- } catch (n) {
61
- x(n);
56
+ i({ kind: "redirecting" }), u("dropzone_deploy_succeeded", {
57
+ method: l,
58
+ site_name: d.subdomain,
59
+ file_count: e.length
60
+ }), s == null || s({ url: o, token: t, siteName: d.subdomain, siteId: d.id }), window.location.assign(`${g(d.subdomain)}#drop_token=${t}`);
61
+ } catch (e) {
62
+ w(e);
62
63
  }
63
64
  }
64
- function _() {
65
- c(null), t({ kind: "idle" });
65
+ function N() {
66
+ m(null), i({ kind: "idle" });
66
67
  }
67
- return { status: u, warning: g, busy: y, deploy: k, reset: _ };
68
+ return { status: c, warning: h, busy: k, deploy: _, reset: N };
68
69
  }
69
70
  export {
70
- P as useDropDeploy
71
+ q as useDropDeploy
71
72
  };
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.27.6",
4
+ "version": "1.27.7",
5
5
  "type": "module",
6
6
  "main": "dist/components/preact/index.js",
7
7
  "types": "dist/components/preact/index.d.ts",
@@ -1,7 +1,13 @@
1
1
  import { useEffect, useRef, useState } from 'preact/hooks';
2
2
  import type { ComponentChildren } from 'preact';
3
3
  import { readDataTransfer, readFileList, type DropClient } from './drop-core';
4
- import { useDropDeploy, type DeployedSite, type DeployStatus, type TrackFn } from './useDropDeploy';
4
+ import {
5
+ useDropDeploy,
6
+ type DeployedSite,
7
+ type DeployError,
8
+ type DeployStatus,
9
+ type TrackFn,
10
+ } from './useDropDeploy';
5
11
 
6
12
  import './DropZone.css';
7
13
 
@@ -21,11 +27,18 @@ interface Props {
21
27
  claimUrl?: (siteName: string) => string;
22
28
  /** Called once the deploy is live. */
23
29
  onDeploy?: (result: DeployedSite) => void;
30
+ /**
31
+ * Called when a deploy fails, with a humanized `message` (ready to show a
32
+ * user — the same copy the built-in overlay uses) and the raw `reason`. Pair
33
+ * this with `showStatus={false}` to render your own error UI.
34
+ */
35
+ onError?: (error: DeployError) => void;
24
36
  /**
25
37
  * Render the built-in status overlay (drag tint, spinner, progress bar, error).
26
38
  * Set to `false` to suppress it entirely and keep your children visible, then
27
39
  * drive your own feedback from the `data-state` attribute on the root element.
28
- * Note: in this mode you're responsible for surfacing errors yourself.
40
+ * Note: in this mode you're responsible for surfacing errors yourself — use
41
+ * `onError` to get the message.
29
42
  */
30
43
  showStatus?: boolean;
31
44
  /**
@@ -54,6 +67,7 @@ export default function DropZone({
54
67
  client,
55
68
  claimUrl = defaultClaimUrl,
56
69
  onDeploy,
70
+ onError,
57
71
  showStatus = true,
58
72
  onTrack,
59
73
  analyticsId,
@@ -69,6 +83,7 @@ export default function DropZone({
69
83
  client,
70
84
  claimUrl,
71
85
  onDeploy,
86
+ onError,
72
87
  track,
73
88
  });
74
89
 
@@ -22,6 +22,14 @@ export interface DeployedSite {
22
22
  siteId: string;
23
23
  }
24
24
 
25
+ /** A deploy failure, handed to `onError` so consumers can render their own error UI. */
26
+ export interface DeployError {
27
+ /** Human-readable text ready to show a user — the same copy the built-in overlay displays. */
28
+ message: string;
29
+ /** The raw underlying reason (e.g. "drop failed: 429"), useful for logging. */
30
+ reason: string;
31
+ }
32
+
25
33
  /** Finite states of a single deploy attempt. */
26
34
  export type DeployStatus =
27
35
  | { kind: 'idle' }
@@ -40,6 +48,7 @@ interface UseDropDeployOptions {
40
48
  client?: DropClient;
41
49
  claimUrl: (siteName: string) => string;
42
50
  onDeploy?: (site: DeployedSite) => void;
51
+ onError?: (error: DeployError) => void;
43
52
  track: TrackFn;
44
53
  }
45
54
 
@@ -77,6 +86,7 @@ export function useDropDeploy({
77
86
  client,
78
87
  claimUrl,
79
88
  onDeploy,
89
+ onError,
80
90
  track,
81
91
  }: UseDropDeployOptions) {
82
92
  const [status, setStatus] = useState<DeployStatus>({ kind: 'idle' });
@@ -96,9 +106,12 @@ export function useDropDeploy({
96
106
  // included where the file set is known (it isn't if `read()` itself threw).
97
107
  // `reason` goes to analytics as-is (raw, e.g. "drop failed: 429"); `message`
98
108
  // is what the user sees, which for a caught error is humanized separately.
109
+ // `onError` gets both so a consumer (e.g. with `showStatus={false}`) can show
110
+ // the humanized message while keeping the raw reason for their own logging.
99
111
  const fail = (message: string, reason: string, extra?: Record<string, unknown>) => {
100
112
  setStatus({ kind: 'error', message });
101
113
  track('dropzone_deploy_failed', { method: source, reason, ...extra });
114
+ onError?.({ message, reason });
102
115
  };
103
116
 
104
117
  const failFromError = (err: unknown, extra?: Record<string, unknown>) => {