@netlify/spark-ui 1.26.0-alpha.2 → 1.26.0-alpha.4
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.
- package/dist/assets/DropZone.css +1 -1
- package/dist/components/index.d.ts +1 -1
- package/dist/components/preact/DropZone/DropZone.js +58 -96
- package/dist/components/preact/DropZone/drop-core/client.d.ts +5 -5
- package/dist/components/preact/DropZone/drop-core/client.js +1 -1
- package/dist/components/preact/DropZone/drop-core/readFiles.js +4 -1
- package/dist/components/preact/DropZone/drop-core/types.d.ts +11 -2
- package/package.json +1 -1
- package/packages/components/index.ts +1 -1
- package/packages/components/preact/DropZone/DropZone.css +1 -80
- package/packages/components/preact/DropZone/DropZone.tsx +16 -53
- package/packages/components/preact/DropZone/drop-core/client.ts +9 -6
- package/packages/components/preact/DropZone/drop-core/readFiles.ts +6 -1
- package/packages/components/preact/DropZone/drop-core/types.ts +11 -2
package/dist/assets/DropZone.css
CHANGED
|
@@ -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-
|
|
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)}}
|
|
@@ -7,5 +7,5 @@ export { default as SearchToggle } from './preact/SearchModal/components/SearchT
|
|
|
7
7
|
export { default as SearchModal } from './preact/SearchModal/components/SearchModal/SearchModal';
|
|
8
8
|
export { default as DropZone } from './preact/DropZone/DropZone';
|
|
9
9
|
export { AnonymousDropClient, deployFiles, readDataTransfer, readFileList, } from './preact/DropZone/drop-core';
|
|
10
|
-
export type { DropClient,
|
|
10
|
+
export type { DropClient, DropResponse, Digest, DeployResult, DeployProgress, DeployPhase, ProcessedFile, } from './preact/DropZone/drop-core';
|
|
11
11
|
export { type IconName } from './preact/Icon';
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { u as e } from "../../../chunks/jsxRuntime.module.Bzuv3cXw.js";
|
|
2
|
-
import { d as
|
|
3
|
-
import
|
|
4
|
-
import { getSingleNonIndexHtmlFileName as
|
|
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
5
|
import { AnonymousDropClient as M } from "./drop-core/client.js";
|
|
6
6
|
import { deployFiles as P } from "./drop-core/deploy.js";
|
|
7
|
-
import { Fragment as
|
|
8
|
-
import '../../../assets/DropZone.css';const U = (n) => `https://app.netlify.com/drop/${n}
|
|
7
|
+
import { Fragment as k } from "preact";
|
|
8
|
+
import '../../../assets/DropZone.css';const U = (n) => `https://app.netlify.com/drop/${n}`;
|
|
9
9
|
function A(n) {
|
|
10
10
|
switch (n.phase) {
|
|
11
11
|
case "creating":
|
|
@@ -17,75 +17,74 @@ function A(n) {
|
|
|
17
17
|
return { kind: "processing" };
|
|
18
18
|
}
|
|
19
19
|
}
|
|
20
|
-
function
|
|
20
|
+
function O({
|
|
21
21
|
apiBase: n,
|
|
22
|
-
client:
|
|
23
|
-
claimUrl:
|
|
24
|
-
onDeploy:
|
|
25
|
-
className:
|
|
22
|
+
client: p,
|
|
23
|
+
claimUrl: u = U,
|
|
24
|
+
onDeploy: i,
|
|
25
|
+
className: s
|
|
26
26
|
}) {
|
|
27
|
-
const [o,
|
|
28
|
-
async function
|
|
29
|
-
|
|
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
30
|
try {
|
|
31
|
-
const r = await
|
|
31
|
+
const r = await t();
|
|
32
32
|
if (!r.length) {
|
|
33
|
-
|
|
33
|
+
a({ kind: "error", message: "No files found to deploy." });
|
|
34
34
|
return;
|
|
35
35
|
}
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
const { url:
|
|
39
|
-
onProgress: (
|
|
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
40
|
});
|
|
41
41
|
try {
|
|
42
|
-
localStorage.setItem("drop-token",
|
|
42
|
+
localStorage.setItem("drop-token", f), localStorage.setItem("dropSiteName", c.subdomain);
|
|
43
43
|
} catch {
|
|
44
44
|
}
|
|
45
|
-
|
|
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
46
|
} catch (r) {
|
|
47
|
-
|
|
47
|
+
a({ kind: "error", message: r instanceof Error ? r.message : String(r) });
|
|
48
48
|
}
|
|
49
49
|
}
|
|
50
|
-
function
|
|
51
|
-
|
|
50
|
+
function S() {
|
|
51
|
+
d(null), a({ kind: "idle" });
|
|
52
52
|
}
|
|
53
53
|
return /* @__PURE__ */ e(
|
|
54
54
|
"div",
|
|
55
55
|
{
|
|
56
|
-
class: `n-dropzone${
|
|
56
|
+
class: `n-dropzone${v ? " is-active" : ""}${h ? " is-busy" : ""}${s ? ` ${s}` : ""}`,
|
|
57
57
|
"data-state": o.kind,
|
|
58
|
-
onDragOver: (
|
|
59
|
-
|
|
58
|
+
onDragOver: (t) => {
|
|
59
|
+
h || (t.preventDefault(), g(!0));
|
|
60
60
|
},
|
|
61
|
-
onDragLeave: (
|
|
62
|
-
|
|
61
|
+
onDragLeave: (t) => {
|
|
62
|
+
t.currentTarget === t.target && g(!1);
|
|
63
63
|
},
|
|
64
|
-
onDrop: (
|
|
65
|
-
if (
|
|
66
|
-
const r =
|
|
67
|
-
r &&
|
|
64
|
+
onDrop: (t) => {
|
|
65
|
+
if (t.preventDefault(), g(!1), h) return;
|
|
66
|
+
const r = t.dataTransfer;
|
|
67
|
+
r && y(() => F(r));
|
|
68
68
|
},
|
|
69
69
|
children: [
|
|
70
|
-
/* @__PURE__ */ e("div", { class: "n-dropzone-panel", children: /* @__PURE__ */ e(
|
|
70
|
+
/* @__PURE__ */ e("div", { class: "n-dropzone-panel", children: /* @__PURE__ */ e(L, { status: o, onReset: S, onFiles: y }) }, o.kind),
|
|
71
71
|
w && o.kind !== "error" && /* @__PURE__ */ e("p", { class: "n-dropzone-warning n-fadeIn", role: "status", children: [
|
|
72
|
-
/* @__PURE__ */ e(
|
|
72
|
+
/* @__PURE__ */ e(I, { name: "lightbulb", className: "n-dropzone-warning-icon" }),
|
|
73
73
|
w
|
|
74
74
|
] })
|
|
75
75
|
]
|
|
76
76
|
}
|
|
77
77
|
);
|
|
78
78
|
}
|
|
79
|
-
function
|
|
79
|
+
function L({
|
|
80
80
|
status: n,
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
onFiles: s
|
|
81
|
+
onReset: p,
|
|
82
|
+
onFiles: u
|
|
84
83
|
}) {
|
|
85
84
|
switch (n.kind) {
|
|
86
85
|
case "idle":
|
|
87
|
-
return /* @__PURE__ */ e(
|
|
88
|
-
/* @__PURE__ */ e(
|
|
86
|
+
return /* @__PURE__ */ e(k, { children: [
|
|
87
|
+
/* @__PURE__ */ e(R, {}),
|
|
89
88
|
/* @__PURE__ */ e("p", { class: "n-dropzone-title", children: "Drag & drop your site folder or zip" }),
|
|
90
89
|
/* @__PURE__ */ e("p", { class: "n-dropzone-subtitle", children: "Deploys instantly to a live URL — no account needed." }),
|
|
91
90
|
/* @__PURE__ */ e("label", { class: "n-dropzone-browse", children: [
|
|
@@ -98,50 +97,39 @@ function B({
|
|
|
98
97
|
multiple: !0,
|
|
99
98
|
webkitdirectory: "",
|
|
100
99
|
directory: "",
|
|
101
|
-
onChange: (
|
|
102
|
-
var
|
|
103
|
-
const
|
|
104
|
-
(
|
|
100
|
+
onChange: (i) => {
|
|
101
|
+
var o;
|
|
102
|
+
const s = i.currentTarget;
|
|
103
|
+
(o = s.files) != null && o.length && u(() => T(s.files));
|
|
105
104
|
}
|
|
106
105
|
}
|
|
107
106
|
)
|
|
108
107
|
] })
|
|
109
108
|
] });
|
|
110
109
|
case "reading":
|
|
111
|
-
return /* @__PURE__ */ e(
|
|
110
|
+
return /* @__PURE__ */ e(l, { label: "Reading files…" });
|
|
112
111
|
case "creating":
|
|
113
|
-
return /* @__PURE__ */ e(
|
|
112
|
+
return /* @__PURE__ */ e(l, { label: "Creating deploy…" });
|
|
114
113
|
case "processing":
|
|
115
|
-
return /* @__PURE__ */ e(
|
|
114
|
+
return /* @__PURE__ */ e(l, { label: "Processing — almost live…" });
|
|
115
|
+
case "redirecting":
|
|
116
|
+
return /* @__PURE__ */ e(l, { label: "Site’s live — taking you to claim it…" });
|
|
116
117
|
case "uploading": {
|
|
117
|
-
const
|
|
118
|
-
return /* @__PURE__ */ e(
|
|
119
|
-
/* @__PURE__ */ e(
|
|
120
|
-
/* @__PURE__ */ e("div", { class: "n-dropzone-progress", role: "progressbar", "aria-valuenow":
|
|
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}%` } }) })
|
|
121
122
|
] });
|
|
122
123
|
}
|
|
123
|
-
case "done":
|
|
124
|
-
return /* @__PURE__ */ e(p, { children: [
|
|
125
|
-
/* @__PURE__ */ e(j, {}),
|
|
126
|
-
/* @__PURE__ */ e("p", { class: "n-dropzone-title", children: "Your site is live!" }),
|
|
127
|
-
/* @__PURE__ */ e("a", { class: "n-dropzone-link", href: n.url, target: "_blank", rel: "noopener noreferrer", children: [
|
|
128
|
-
n.url.replace(/^https?:\/\//, ""),
|
|
129
|
-
/* @__PURE__ */ e(b, { name: "link", className: "n-dropzone-link-icon" })
|
|
130
|
-
] }),
|
|
131
|
-
/* @__PURE__ */ e("div", { class: "n-dropzone-actions", children: [
|
|
132
|
-
/* @__PURE__ */ e("a", { class: "n-dropzone-browse", href: h(n.siteName), children: "Claim this site" }),
|
|
133
|
-
/* @__PURE__ */ e("button", { type: "button", class: "n-dropzone-reset", onClick: d, children: "Deploy another" })
|
|
134
|
-
] })
|
|
135
|
-
] });
|
|
136
124
|
case "error":
|
|
137
|
-
return /* @__PURE__ */ e(
|
|
125
|
+
return /* @__PURE__ */ e(k, { children: [
|
|
138
126
|
/* @__PURE__ */ e("p", { class: "n-dropzone-title n-dropzone-title--error", children: "Something went wrong" }),
|
|
139
127
|
/* @__PURE__ */ e("p", { class: "n-dropzone-subtitle", role: "alert", children: n.message }),
|
|
140
|
-
/* @__PURE__ */ e("button", { type: "button", class: "n-dropzone-browse", onClick:
|
|
128
|
+
/* @__PURE__ */ e("button", { type: "button", class: "n-dropzone-browse", onClick: p, children: "Try again" })
|
|
141
129
|
] });
|
|
142
130
|
}
|
|
143
131
|
}
|
|
144
|
-
function
|
|
132
|
+
function l({ label: n }) {
|
|
145
133
|
return /* @__PURE__ */ e("div", { class: "n-dropzone-loading", children: [
|
|
146
134
|
/* @__PURE__ */ e("span", { class: "n-dropzone-spinner", "aria-hidden": "true" }),
|
|
147
135
|
/* @__PURE__ */ e("p", { class: "n-dropzone-subtitle", "aria-live": "polite", children: [
|
|
@@ -151,7 +139,7 @@ function u({ label: n }) {
|
|
|
151
139
|
] })
|
|
152
140
|
] });
|
|
153
141
|
}
|
|
154
|
-
function
|
|
142
|
+
function R() {
|
|
155
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: [
|
|
156
144
|
/* @__PURE__ */ e(
|
|
157
145
|
"path",
|
|
@@ -176,32 +164,6 @@ function L() {
|
|
|
176
164
|
)
|
|
177
165
|
] }) });
|
|
178
166
|
}
|
|
179
|
-
function j() {
|
|
180
|
-
return /* @__PURE__ */ e("span", { class: "n-dropzone-glyph n-dropzone-glyph--success", "aria-hidden": "true", children: /* @__PURE__ */ e("svg", { viewBox: "0 0 48 48", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [
|
|
181
|
-
/* @__PURE__ */ e(
|
|
182
|
-
"circle",
|
|
183
|
-
{
|
|
184
|
-
class: "n-dropzone-check-ring",
|
|
185
|
-
cx: "24",
|
|
186
|
-
cy: "24",
|
|
187
|
-
r: "20",
|
|
188
|
-
stroke: "currentColor",
|
|
189
|
-
"stroke-width": "3"
|
|
190
|
-
}
|
|
191
|
-
),
|
|
192
|
-
/* @__PURE__ */ e(
|
|
193
|
-
"path",
|
|
194
|
-
{
|
|
195
|
-
class: "n-dropzone-check-mark",
|
|
196
|
-
d: "M15 24.5 21 30.5 33 18",
|
|
197
|
-
stroke: "currentColor",
|
|
198
|
-
"stroke-width": "3",
|
|
199
|
-
"stroke-linecap": "round",
|
|
200
|
-
"stroke-linejoin": "round"
|
|
201
|
-
}
|
|
202
|
-
)
|
|
203
|
-
] }) });
|
|
204
|
-
}
|
|
205
167
|
export {
|
|
206
|
-
|
|
168
|
+
O as default
|
|
207
169
|
};
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { DropResponse, Digest } from './types';
|
|
2
2
|
export interface DropClient {
|
|
3
3
|
getToken(): Promise<string>;
|
|
4
|
-
createDeploy(digest: Digest, token: string): Promise<
|
|
4
|
+
createDeploy(digest: Digest, token: string): Promise<DropResponse>;
|
|
5
5
|
uploadFile(deployId: string, path: string, content: ArrayBuffer, token: string): Promise<void>;
|
|
6
|
-
waitUntilReady(deploy:
|
|
6
|
+
waitUntilReady(deploy: DropResponse, token: string): Promise<void>;
|
|
7
7
|
}
|
|
8
8
|
/**
|
|
9
9
|
* Unauthenticated drop client. Authorized server-side by the Referer header,
|
|
@@ -13,7 +13,7 @@ export declare class AnonymousDropClient implements DropClient {
|
|
|
13
13
|
private apiBase;
|
|
14
14
|
constructor(apiBase?: string);
|
|
15
15
|
getToken(): Promise<string>;
|
|
16
|
-
createDeploy(files: Digest, token: string): Promise<
|
|
16
|
+
createDeploy(files: Digest, token: string): Promise<DropResponse>;
|
|
17
17
|
uploadFile(deployId: string, path: string, content: ArrayBuffer, token: string): Promise<void>;
|
|
18
|
-
waitUntilReady(deploy:
|
|
18
|
+
waitUntilReady(deploy: DropResponse, token: string): Promise<void>;
|
|
19
19
|
}
|
|
@@ -27,7 +27,7 @@ class n {
|
|
|
27
27
|
}
|
|
28
28
|
async waitUntilReady(e, o) {
|
|
29
29
|
for (let t = 0; t < 600; t++) {
|
|
30
|
-
const a = await fetch(`${this.apiBase}/deploys/${e.
|
|
30
|
+
const a = await fetch(`${this.apiBase}/deploys/${e.deploy_id}`, {
|
|
31
31
|
headers: { Authorization: `Bearer ${o}` }
|
|
32
32
|
});
|
|
33
33
|
if (a.ok && (await a.json()).state === "ready") return;
|
|
@@ -579,7 +579,10 @@ async function U(x) {
|
|
|
579
579
|
), p = [];
|
|
580
580
|
for (const [v, s] of Object.entries(b)) {
|
|
581
581
|
if (v.endsWith("/") || /(^|\/)(__MACOSX\/|\.)/.test(v)) continue;
|
|
582
|
-
const g = s.buffer.slice(
|
|
582
|
+
const g = s.buffer.slice(
|
|
583
|
+
s.byteOffset,
|
|
584
|
+
s.byteOffset + s.byteLength
|
|
585
|
+
);
|
|
583
586
|
p.push(k(v, g, v.split("/").pop()));
|
|
584
587
|
}
|
|
585
588
|
return O(p);
|
|
@@ -8,8 +8,17 @@ export interface ProcessedFile {
|
|
|
8
8
|
name: string;
|
|
9
9
|
}
|
|
10
10
|
export type Digest = Record<string, string>;
|
|
11
|
-
|
|
11
|
+
/**
|
|
12
|
+
* Response body of `POST /api/v1/drop`. Not a canonical Deploy resource —
|
|
13
|
+
* it's a mixed shape carrying both the anonymous site and the initial deploy
|
|
14
|
+
* so the client has everything it needs to upload, poll, and hand off to the
|
|
15
|
+
* user in one round trip.
|
|
16
|
+
*/
|
|
17
|
+
export interface DropResponse {
|
|
18
|
+
/** site id (UUID) — surfaced to callers so they can build claim links */
|
|
12
19
|
id: string;
|
|
20
|
+
/** deploy id (BSON) — used for `/deploys/{deploy_id}` reads and file PUTs */
|
|
21
|
+
deploy_id: string;
|
|
13
22
|
/** the *.netlify.app subdomain assigned to the anonymous site */
|
|
14
23
|
subdomain: string;
|
|
15
24
|
/** SHAs the backend does NOT yet have — only these get uploaded */
|
|
@@ -17,7 +26,7 @@ export interface Deploy {
|
|
|
17
26
|
state?: string;
|
|
18
27
|
}
|
|
19
28
|
export interface DeployResult {
|
|
20
|
-
deploy:
|
|
29
|
+
deploy: DropResponse;
|
|
21
30
|
/** drop token — persist this to later claim the site into an account */
|
|
22
31
|
token: string;
|
|
23
32
|
url: string;
|
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.
|
|
4
|
+
"version": "1.26.0-alpha.4",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/components/preact/index.js",
|
|
7
7
|
"types": "dist/components/preact/index.d.ts",
|
|
@@ -138,31 +138,6 @@
|
|
|
138
138
|
}
|
|
139
139
|
}
|
|
140
140
|
|
|
141
|
-
/* ----------------------------- */
|
|
142
|
-
/* Success glyph (draws in) */
|
|
143
|
-
/* ----------------------------- */
|
|
144
|
-
.n-dropzone-glyph--success {
|
|
145
|
-
color: var(--color-success-text, #108b46);
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
.n-dropzone-check-ring {
|
|
149
|
-
stroke-dasharray: 126;
|
|
150
|
-
stroke-dashoffset: 126;
|
|
151
|
-
animation: dropDraw 500ms ease-out 80ms forwards;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
.n-dropzone-check-mark {
|
|
155
|
-
stroke-dasharray: 32;
|
|
156
|
-
stroke-dashoffset: 32;
|
|
157
|
-
animation: dropDraw 320ms ease-out 460ms forwards;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
@keyframes dropDraw {
|
|
161
|
-
to {
|
|
162
|
-
stroke-dashoffset: 0;
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
|
|
166
141
|
/* ----------------------------- */
|
|
167
142
|
/* Loading / spinner */
|
|
168
143
|
/* ----------------------------- */
|
|
@@ -212,37 +187,6 @@
|
|
|
212
187
|
transition: width 300ms cubic-bezier(0.22, 1, 0.36, 1);
|
|
213
188
|
}
|
|
214
189
|
|
|
215
|
-
/* ----------------------------- */
|
|
216
|
-
/* Done state links & actions */
|
|
217
|
-
/* ----------------------------- */
|
|
218
|
-
.n-dropzone-link {
|
|
219
|
-
display: inline-flex;
|
|
220
|
-
align-items: center;
|
|
221
|
-
gap: var(--space-3xs, 0.35rem);
|
|
222
|
-
font-size: 1rem;
|
|
223
|
-
font-weight: var(--font-weight-semibold, 600);
|
|
224
|
-
color: var(--color-link, #0e7fe1);
|
|
225
|
-
text-decoration: none;
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
.n-dropzone-link:hover {
|
|
229
|
-
text-decoration: underline;
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
.n-dropzone-link-icon {
|
|
233
|
-
width: 0.9em;
|
|
234
|
-
height: 0.9em;
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
.n-dropzone-actions {
|
|
238
|
-
display: flex;
|
|
239
|
-
flex-wrap: wrap;
|
|
240
|
-
align-items: center;
|
|
241
|
-
justify-content: center;
|
|
242
|
-
gap: var(--space-s, 1rem);
|
|
243
|
-
margin-block-start: var(--space-2xs, 0.5rem);
|
|
244
|
-
}
|
|
245
|
-
|
|
246
190
|
/* ----------------------------- */
|
|
247
191
|
/* Buttons */
|
|
248
192
|
/* ----------------------------- */
|
|
@@ -274,22 +218,6 @@
|
|
|
274
218
|
transform: translateY(0);
|
|
275
219
|
}
|
|
276
220
|
|
|
277
|
-
.n-dropzone-reset {
|
|
278
|
-
padding: var(--space-2xs, 0.5rem) var(--space-m, 1.25rem);
|
|
279
|
-
border: 1px solid var(--color-border-default, rgba(0, 0, 0, 0.15));
|
|
280
|
-
border-radius: var(--radius-pill, 999px);
|
|
281
|
-
background: transparent;
|
|
282
|
-
color: var(--color-text-default, #14161a);
|
|
283
|
-
font-size: 0.9375rem;
|
|
284
|
-
font-weight: var(--font-weight-semibold, 600);
|
|
285
|
-
cursor: pointer;
|
|
286
|
-
transition: background-color 150ms ease-out;
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
.n-dropzone-reset:hover {
|
|
290
|
-
background: var(--color-bg-2, #f6f8fa);
|
|
291
|
-
}
|
|
292
|
-
|
|
293
221
|
/* ----------------------------- */
|
|
294
222
|
/* Warning banner */
|
|
295
223
|
/* ----------------------------- */
|
|
@@ -322,18 +250,11 @@
|
|
|
322
250
|
.n-dropzone-panel,
|
|
323
251
|
.n-dropzone-glyph-arrow,
|
|
324
252
|
.n-dropzone-spinner,
|
|
325
|
-
.n-dropzone-progress-bar
|
|
326
|
-
.n-dropzone-check-ring,
|
|
327
|
-
.n-dropzone-check-mark {
|
|
253
|
+
.n-dropzone-progress-bar {
|
|
328
254
|
animation: none !important;
|
|
329
255
|
transition: none !important;
|
|
330
256
|
}
|
|
331
257
|
|
|
332
|
-
.n-dropzone-check-ring,
|
|
333
|
-
.n-dropzone-check-mark {
|
|
334
|
-
stroke-dashoffset: 0;
|
|
335
|
-
}
|
|
336
|
-
|
|
337
258
|
.n-dropzone-spinner {
|
|
338
259
|
border-top-color: var(--n-dropzone-accent);
|
|
339
260
|
}
|
|
@@ -20,7 +20,7 @@ type Status =
|
|
|
20
20
|
| { kind: 'creating' }
|
|
21
21
|
| { kind: 'uploading'; uploaded: number; total: number }
|
|
22
22
|
| { kind: 'processing' }
|
|
23
|
-
| { kind: '
|
|
23
|
+
| { kind: 'redirecting' }
|
|
24
24
|
| { kind: 'error'; message: string };
|
|
25
25
|
|
|
26
26
|
interface Props {
|
|
@@ -35,7 +35,7 @@ interface Props {
|
|
|
35
35
|
className?: string;
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
const defaultClaimUrl = (siteName: string) => `https://app.netlify.com/drop/${siteName}
|
|
38
|
+
const defaultClaimUrl = (siteName: string) => `https://app.netlify.com/drop/${siteName}`;
|
|
39
39
|
|
|
40
40
|
function progressPhaseToStatus(e: DeployProgress): Status {
|
|
41
41
|
switch (e.phase) {
|
|
@@ -61,7 +61,7 @@ export default function DropZone({
|
|
|
61
61
|
const [active, setActive] = useState(false);
|
|
62
62
|
const dropClient = useRef(client ?? new AnonymousDropClient(apiBase)).current;
|
|
63
63
|
|
|
64
|
-
const busy = status.kind !== 'idle' && status.kind !== '
|
|
64
|
+
const busy = status.kind !== 'idle' && status.kind !== 'error';
|
|
65
65
|
|
|
66
66
|
async function run(read: () => Promise<ProcessedFile[]>) {
|
|
67
67
|
setWarning(null);
|
|
@@ -85,7 +85,10 @@ export default function DropZone({
|
|
|
85
85
|
onProgress: e => setStatus(progressPhaseToStatus(e)),
|
|
86
86
|
});
|
|
87
87
|
|
|
88
|
-
//
|
|
88
|
+
// Best-effort local persistence. Note this does NOT reach the dashboard:
|
|
89
|
+
// localStorage is per-origin, so a token written here (e.g. www.netlify.com)
|
|
90
|
+
// is invisible to app.netlify.com. The cross-origin handoff is the URL
|
|
91
|
+
// fragment below; this only helps if the drop and claim share an origin.
|
|
89
92
|
try {
|
|
90
93
|
localStorage.setItem('drop-token', token);
|
|
91
94
|
localStorage.setItem('dropSiteName', deploy.subdomain);
|
|
@@ -93,8 +96,13 @@ export default function DropZone({
|
|
|
93
96
|
/* private mode / storage disabled — non-fatal */
|
|
94
97
|
}
|
|
95
98
|
|
|
96
|
-
setStatus({ kind: '
|
|
99
|
+
setStatus({ kind: 'redirecting' });
|
|
97
100
|
onDeploy?.({ url, token, siteName: deploy.subdomain, siteId: deploy.id });
|
|
101
|
+
|
|
102
|
+
// Hand the drop token to the dashboard via the URL fragment — localStorage
|
|
103
|
+
// does not cross origins. The dashboard's /drop/:name page reads
|
|
104
|
+
// `#drop_token=` and persists it for the claim + post-signup steps.
|
|
105
|
+
window.location.assign(`${claimUrl(deploy.subdomain)}#drop_token=${token}`);
|
|
98
106
|
} catch (err) {
|
|
99
107
|
setStatus({ kind: 'error', message: err instanceof Error ? err.message : String(err) });
|
|
100
108
|
}
|
|
@@ -131,7 +139,7 @@ export default function DropZone({
|
|
|
131
139
|
>
|
|
132
140
|
{/* keyed so each state change remounts and replays its enter animation */}
|
|
133
141
|
<div class="n-dropzone-panel" key={status.kind}>
|
|
134
|
-
<StatusView status={status}
|
|
142
|
+
<StatusView status={status} onReset={reset} onFiles={run} />
|
|
135
143
|
</div>
|
|
136
144
|
|
|
137
145
|
{warning && status.kind !== 'error' && (
|
|
@@ -146,12 +154,10 @@ export default function DropZone({
|
|
|
146
154
|
|
|
147
155
|
function StatusView({
|
|
148
156
|
status,
|
|
149
|
-
claimUrl,
|
|
150
157
|
onReset,
|
|
151
158
|
onFiles,
|
|
152
159
|
}: {
|
|
153
160
|
status: Status;
|
|
154
|
-
claimUrl: (siteName: string) => string;
|
|
155
161
|
onReset: () => void;
|
|
156
162
|
onFiles: (read: () => Promise<ProcessedFile[]>) => void;
|
|
157
163
|
}) {
|
|
@@ -186,6 +192,8 @@ function StatusView({
|
|
|
186
192
|
return <Spinner label="Creating deploy…" />;
|
|
187
193
|
case 'processing':
|
|
188
194
|
return <Spinner label="Processing — almost live…" />;
|
|
195
|
+
case 'redirecting':
|
|
196
|
+
return <Spinner label="Site’s live — taking you to claim it…" />;
|
|
189
197
|
|
|
190
198
|
case 'uploading': {
|
|
191
199
|
const pct = status.total ? Math.round((status.uploaded / status.total) * 100) : 0;
|
|
@@ -199,26 +207,6 @@ function StatusView({
|
|
|
199
207
|
);
|
|
200
208
|
}
|
|
201
209
|
|
|
202
|
-
case 'done':
|
|
203
|
-
return (
|
|
204
|
-
<>
|
|
205
|
-
<SuccessGlyph />
|
|
206
|
-
<p class="n-dropzone-title">Your site is live!</p>
|
|
207
|
-
<a class="n-dropzone-link" href={status.url} target="_blank" rel="noopener noreferrer">
|
|
208
|
-
{status.url.replace(/^https?:\/\//, '')}
|
|
209
|
-
<Icon name="link" className="n-dropzone-link-icon" />
|
|
210
|
-
</a>
|
|
211
|
-
<div class="n-dropzone-actions">
|
|
212
|
-
<a class="n-dropzone-browse" href={claimUrl(status.siteName)}>
|
|
213
|
-
Claim this site
|
|
214
|
-
</a>
|
|
215
|
-
<button type="button" class="n-dropzone-reset" onClick={onReset}>
|
|
216
|
-
Deploy another
|
|
217
|
-
</button>
|
|
218
|
-
</div>
|
|
219
|
-
</>
|
|
220
|
-
);
|
|
221
|
-
|
|
222
210
|
case 'error':
|
|
223
211
|
return (
|
|
224
212
|
<>
|
|
@@ -268,28 +256,3 @@ function UploadGlyph() {
|
|
|
268
256
|
</span>
|
|
269
257
|
);
|
|
270
258
|
}
|
|
271
|
-
|
|
272
|
-
function SuccessGlyph() {
|
|
273
|
-
return (
|
|
274
|
-
<span class="n-dropzone-glyph n-dropzone-glyph--success" aria-hidden="true">
|
|
275
|
-
<svg viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
276
|
-
<circle
|
|
277
|
-
class="n-dropzone-check-ring"
|
|
278
|
-
cx="24"
|
|
279
|
-
cy="24"
|
|
280
|
-
r="20"
|
|
281
|
-
stroke="currentColor"
|
|
282
|
-
stroke-width="3"
|
|
283
|
-
/>
|
|
284
|
-
<path
|
|
285
|
-
class="n-dropzone-check-mark"
|
|
286
|
-
d="M15 24.5 21 30.5 33 18"
|
|
287
|
-
stroke="currentColor"
|
|
288
|
-
stroke-width="3"
|
|
289
|
-
stroke-linecap="round"
|
|
290
|
-
stroke-linejoin="round"
|
|
291
|
-
/>
|
|
292
|
-
</svg>
|
|
293
|
-
</span>
|
|
294
|
-
);
|
|
295
|
-
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { DropResponse, Digest } from './types';
|
|
2
2
|
|
|
3
3
|
export interface DropClient {
|
|
4
4
|
getToken(): Promise<string>;
|
|
5
|
-
createDeploy(digest: Digest, token: string): Promise<
|
|
5
|
+
createDeploy(digest: Digest, token: string): Promise<DropResponse>;
|
|
6
6
|
uploadFile(deployId: string, path: string, content: ArrayBuffer, token: string): Promise<void>;
|
|
7
|
-
waitUntilReady(deploy:
|
|
7
|
+
waitUntilReady(deploy: DropResponse, token: string): Promise<void>;
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
const API = 'https://api.netlify.com/api/v1';
|
|
@@ -22,7 +22,7 @@ export class AnonymousDropClient implements DropClient {
|
|
|
22
22
|
return (await res.json()).token;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
async createDeploy(files: Digest, token: string): Promise<
|
|
25
|
+
async createDeploy(files: Digest, token: string): Promise<DropResponse> {
|
|
26
26
|
const res = await fetch(`${this.apiBase}/drop`, {
|
|
27
27
|
method: 'POST',
|
|
28
28
|
headers: { 'Content-Type': 'application/json' },
|
|
@@ -47,10 +47,13 @@ export class AnonymousDropClient implements DropClient {
|
|
|
47
47
|
if (!res.ok) throw new Error(`upload ${path} failed: ${res.status}`);
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
async waitUntilReady(deploy:
|
|
50
|
+
async waitUntilReady(deploy: DropResponse, token: string): Promise<void> {
|
|
51
51
|
// Poll the deploy with the drop token until ready.
|
|
52
|
+
// Note: `/deploys/{id}` expects the deploy_id (BSON), not the site id (UUID)
|
|
53
|
+
// that lives at `deploy.id`. See drop_controller.rb; Deploy.find(uuid) is nil
|
|
54
|
+
// and the request 401s misleadingly via require_current_actor!.
|
|
52
55
|
for (let i = 0; i < 600; i++) {
|
|
53
|
-
const res = await fetch(`${this.apiBase}/deploys/${deploy.
|
|
56
|
+
const res = await fetch(`${this.apiBase}/deploys/${deploy.deploy_id}`, {
|
|
54
57
|
headers: { Authorization: `Bearer ${token}` },
|
|
55
58
|
});
|
|
56
59
|
if (res.ok && (await res.json()).state === 'ready') return;
|
|
@@ -99,7 +99,12 @@ export async function readZip(zip: File): Promise<ProcessedFile[]> {
|
|
|
99
99
|
// fflate uses a trailing slash to signal directory entries; also skip the
|
|
100
100
|
// usual junk (macOS metadata, dotfiles at any depth).
|
|
101
101
|
if (path.endsWith('/') || /(^|\/)(__MACOSX\/|\.)/.test(path)) continue;
|
|
102
|
-
|
|
102
|
+
// `bytes.buffer` is ArrayBufferLike; fflate always backs entries with a plain
|
|
103
|
+
// ArrayBuffer, so narrow it for toProcessedFile's ArrayBuffer parameter.
|
|
104
|
+
const content = bytes.buffer.slice(
|
|
105
|
+
bytes.byteOffset,
|
|
106
|
+
bytes.byteOffset + bytes.byteLength,
|
|
107
|
+
) as ArrayBuffer;
|
|
103
108
|
out.push(toProcessedFile(path, content, path.split('/').pop()!));
|
|
104
109
|
}
|
|
105
110
|
return normalizeFiles(out);
|
|
@@ -10,8 +10,17 @@ export interface ProcessedFile {
|
|
|
10
10
|
|
|
11
11
|
export type Digest = Record<string, string>; // { "/index.html": "<sha1>" }
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
/**
|
|
14
|
+
* Response body of `POST /api/v1/drop`. Not a canonical Deploy resource —
|
|
15
|
+
* it's a mixed shape carrying both the anonymous site and the initial deploy
|
|
16
|
+
* so the client has everything it needs to upload, poll, and hand off to the
|
|
17
|
+
* user in one round trip.
|
|
18
|
+
*/
|
|
19
|
+
export interface DropResponse {
|
|
20
|
+
/** site id (UUID) — surfaced to callers so they can build claim links */
|
|
14
21
|
id: string;
|
|
22
|
+
/** deploy id (BSON) — used for `/deploys/{deploy_id}` reads and file PUTs */
|
|
23
|
+
deploy_id: string;
|
|
15
24
|
/** the *.netlify.app subdomain assigned to the anonymous site */
|
|
16
25
|
subdomain: string;
|
|
17
26
|
/** SHAs the backend does NOT yet have — only these get uploaded */
|
|
@@ -20,7 +29,7 @@ export interface Deploy {
|
|
|
20
29
|
}
|
|
21
30
|
|
|
22
31
|
export interface DeployResult {
|
|
23
|
-
deploy:
|
|
32
|
+
deploy: DropResponse;
|
|
24
33
|
/** drop token — persist this to later claim the site into an account */
|
|
25
34
|
token: string;
|
|
26
35
|
url: string; // https://<subdomain>.netlify.app
|