@jxsuite/studio 6.0.0 → 6.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunks/2020-va2wwra4.js +4 -0
- package/dist/chunks/adapter-f93pafd7.js +4 -0
- package/dist/chunks/{cf-account-picker-bb2yq946.js → cf-account-picker-af1h381v.js} +3 -3
- package/dist/chunks/cf-settings-125tdm0w.js +4 -0
- package/dist/chunks/components-ax2f1fnn.js +4 -0
- package/dist/chunks/content-source-108aqtk1.js +4 -0
- package/dist/chunks/{convert-to-repeater-vqkt7s3s.js → convert-to-repeater-ry5rmppr.js} +3 -3
- package/dist/chunks/editor-pfgdmkb0.js +4 -0
- package/dist/chunks/entry-commands-t0k1q0kx.js +4 -0
- package/dist/chunks/{extension-sections-8beq9ac3.js → extension-sections-6mwh2hf8.js} +3 -3
- package/dist/chunks/file-ops-meej5mqf.js +4 -0
- package/dist/chunks/files-44vp8a2s.js +4 -0
- package/dist/chunks/format-host-7q1petb9.js +4 -0
- package/dist/chunks/fs-events-h2mwnxaz.js +4 -0
- package/dist/chunks/git-panel-zngbymvd.js +4 -0
- package/dist/chunks/{grid-view-ca10def6.js → grid-view-sx9vynsp.js} +15 -15
- package/dist/chunks/icons-p44xhjf0.js +4 -0
- package/dist/chunks/iframe-host-qk3ycq87.js +4 -0
- package/dist/chunks/{index-9ydfe04y.js → index-2veqmnhr.js} +3 -3
- package/dist/chunks/index-wkyfjeqz.js +4 -0
- package/dist/chunks/{javascript-d9pqgc2p.js → javascript-8nbvs12s.js} +3 -3
- package/dist/chunks/jsonMode-s8kt9dsj.js +13 -0
- package/dist/chunks/jx-validate-psgd7vsx.js +4 -0
- package/dist/chunks/layers-panel-1kq2jyfk.js +4 -0
- package/dist/chunks/layers-xd92fg8d.js +4 -0
- package/dist/chunks/lit-html-ac4cwwr4.js +4 -0
- package/dist/chunks/media-picker-0svzkdyb.js +4 -0
- package/dist/chunks/menu-5c55ea3d.js +4 -0
- package/dist/chunks/{monaco-binding-62dtmgbf.js → monaco-binding-z6r70pdt.js} +3 -3
- package/dist/chunks/monaco-buffer-fz6gq5y7.js +4 -0
- package/dist/chunks/monaco-setup-q4axkgj7.js +4 -0
- package/dist/chunks/notify-0xgtn8q1.js +4 -0
- package/dist/chunks/platform-v1qctbe6.js +4 -0
- package/dist/chunks/publish-panel-ym1fmgq5.js +4 -0
- package/dist/chunks/quick-search-a54e6t5n.js +4 -0
- package/dist/chunks/references-gmqp82fs.js +4 -0
- package/dist/chunks/right-panel-v7t9awbk.js +4 -0
- package/dist/chunks/section-registry-vhyf3zsr.js +4 -0
- package/dist/chunks/studio-0bhx5hz6.js +5 -0
- package/dist/chunks/studio-0k1ddnyy.js +6 -0
- package/dist/chunks/studio-1b04pd0j.js +64 -0
- package/dist/chunks/{studio-sxc0zvpt.js → studio-1q1sh2ax.js} +3 -3
- package/dist/chunks/studio-4dwbj6gb.js +5 -0
- package/dist/chunks/studio-4tanh828.js +5 -0
- package/dist/chunks/{studio-fqq890ap.js → studio-55h4mstj.js} +3 -3
- package/dist/chunks/studio-5tnvjkz4.js +5 -0
- package/dist/chunks/studio-60dqgb0c.js +5 -0
- package/dist/chunks/studio-61dp09qg.js +9 -0
- package/dist/chunks/studio-63epcrj2.js +5 -0
- package/dist/chunks/studio-74k29dy9.js +5 -0
- package/dist/chunks/{studio-qa73881a.js → studio-7vg6jcd3.js} +4 -4
- package/dist/chunks/studio-7zx31knw.js +9 -0
- package/dist/chunks/studio-8bxk97km.js +5 -0
- package/dist/chunks/{studio-edbtvcd1.js → studio-8grwxtqk.js} +4 -4
- package/dist/chunks/studio-933e30n4.js +5 -0
- package/dist/chunks/{studio-rr4xkv81.js → studio-98wnwhbg.js} +4 -4
- package/dist/chunks/studio-9mccjwjh.js +14 -0
- package/dist/chunks/studio-a2ke1e4t.js +5 -0
- package/dist/chunks/studio-e3a2a6v3.js +4 -0
- package/dist/chunks/{studio-pwqfmt4p.js → studio-e81q1vb9.js} +4 -4
- package/dist/chunks/{studio-7pgy3xaf.js → studio-epgrmqqm.js} +4 -4
- package/dist/chunks/studio-g1shz58g.js +5 -0
- package/dist/chunks/studio-gf0fz03c.js +5 -0
- package/dist/chunks/studio-gjtpe62q.js +6 -0
- package/dist/chunks/{studio-tzegmy4w.js → studio-gkskhg7m.js} +4 -4
- package/dist/chunks/studio-gx4dnjzd.js +5 -0
- package/dist/chunks/{studio-8xcwewjq.js → studio-h46vtzk8.js} +4 -4
- package/dist/chunks/studio-j03zga3y.js +9 -0
- package/dist/chunks/{studio-4r0sjtx8.js → studio-jhpxeckz.js} +4 -4
- package/dist/chunks/{studio-zd4kayn2.js → studio-k01r7ykp.js} +4 -4
- package/dist/chunks/{studio-03wwfvyt.js → studio-kggey1jt.js} +4 -4
- package/dist/chunks/{studio-hnvnfbdv.js → studio-kk7h30ne.js} +6 -6
- package/dist/chunks/{studio-01qza1n3.js → studio-kt1zqney.js} +4 -4
- package/dist/chunks/{studio-r3kaka4d.js → studio-m3cwhvye.js} +4 -4
- package/dist/chunks/{studio-evwgp1ym.js → studio-mcayt6hz.js} +4 -4
- package/dist/chunks/{studio-5vy2wfm5.js → studio-myjcghv3.js} +4 -4
- package/dist/chunks/studio-n8bxwc3y.js +4 -0
- package/dist/chunks/{studio-ztvcxm3w.js → studio-ngm8gvpb.js} +4 -4
- package/dist/chunks/{studio-01kbbqg3.js → studio-nj5a01jy.js} +196 -196
- package/dist/chunks/{studio-xkfs0dp2.js → studio-pcgz8heb.js} +4 -4
- package/dist/chunks/{studio-0bbxhhce.js → studio-pre0skq6.js} +4 -4
- package/dist/chunks/{studio-n78gjfy8.js → studio-qcsq0t15.js} +4 -4
- package/dist/chunks/studio-qkstvve9.js +5 -0
- package/dist/chunks/studio-qv1pwd3c.js +5 -0
- package/dist/chunks/{studio-rmn2dz59.js → studio-rc6dvxjb.js} +5 -5
- package/dist/chunks/{studio-st75kn99.js → studio-rv11kfwa.js} +7 -7
- package/dist/chunks/studio-s8dmmz66.js +5 -0
- package/dist/chunks/studio-sp5xzbj4.js +6 -0
- package/dist/chunks/{studio-tmeq6mjb.js → studio-szsjbj0g.js} +4 -4
- package/dist/chunks/studio-t1xzawhr.js +16 -0
- package/dist/chunks/{studio-mk00zd5d.js → studio-t6b6qe31.js} +4 -4
- package/dist/chunks/{studio-bftp4n4b.js → studio-tdcprbqb.js} +4 -4
- package/dist/chunks/{studio-ge5fqjth.js → studio-vrmcjf9g.js} +5 -5
- package/dist/chunks/studio-vyc0b4he.js +10 -0
- package/dist/chunks/{studio-semd1s8s.js → studio-w0cgf6jg.js} +4 -4
- package/dist/chunks/{studio-gp02nqnw.js → studio-w3hskzqk.js} +4 -4
- package/dist/chunks/{studio-1wvfxkyy.js → studio-wszjyh0j.js} +4 -4
- package/dist/chunks/studio-xgrgcdyh.js +9 -0
- package/dist/chunks/{studio-y7jehjrp.js → studio-xz2yxfcb.js} +4 -4
- package/dist/chunks/{studio-aje966s9.js → studio-y6n42tgs.js} +4 -4
- package/dist/chunks/{studio-yr04mv2j.js → studio-yb560vxr.js} +4 -4
- package/dist/chunks/{studio-tp642n5g.js → studio-yn9f8vtx.js} +4 -4
- package/dist/chunks/studio-yw05w8f0.js +7 -0
- package/dist/chunks/{studio-cbr9czkn.js → studio-z16sgavc.js} +4 -4
- package/dist/chunks/studio-zey7zsdw.js +12 -0
- package/dist/chunks/{studio-1q6amtph.js → studio-zyqn40w4.js} +4 -4
- package/dist/chunks/tsMode-2frah6h1.js +14 -0
- package/dist/chunks/workspace-zt81g1ms.js +4 -0
- package/dist/chunks/ws-client-n2bh4hkf.js +4 -0
- package/dist/iframe-entry.js +13 -13
- package/dist/manifest.json +62 -62
- package/dist/studio.js +87 -87
- package/dist/workers/ts.worker.js +3 -3
- package/package.json +10 -10
- package/src/canvas/canvas-render.ts +4 -1
- package/src/canvas/canvas-utils.ts +73 -15
- package/src/canvas/iframe-host.ts +67 -21
- package/src/canvas/iframe-inline-edit.ts +22 -4
- package/src/canvas/iframe-interaction.ts +7 -1
- package/src/canvas/iframe-protocol.ts +3 -0
- package/src/canvas/iframe-render.ts +5 -2
- package/src/commands/defaults.ts +4 -1
- package/src/commands/live-context.ts +3 -1
- package/src/editor/shortcuts.ts +55 -27
- package/src/files/media-paths.ts +3 -1
- package/src/new-project/add-repo-modal.ts +459 -59
- package/src/panels/editors.ts +3 -1
- package/src/panels/style-panel.ts +36 -11
- package/src/platforms/cloud.ts +95 -18
- package/src/platforms/devserver.ts +47 -12
- package/src/publish/pages-service.ts +7 -0
- package/src/publish/publish-panel.ts +16 -2
- package/src/studio.ts +14 -2
- package/src/surfaces/add-repo.json +721 -224
- package/src/surfaces/add-repo.ts +196 -31
- package/src/surfaces/library-pane.ts +6 -0
- package/src/surfaces/rail.json +126 -73
- package/src/surfaces/rail.ts +31 -5
- package/src/tabs/typing.ts +31 -0
- package/src/types.ts +42 -3
- package/src/utils/edit-display.ts +30 -1
- package/src/utils/project-dir.ts +24 -0
- package/src/workspace/workspace.ts +7 -0
- package/dist/chunks/2020-cs2sb9t1.js +0 -4
- package/dist/chunks/adapter-y5gdq19c.js +0 -4
- package/dist/chunks/cf-settings-20dznc3j.js +0 -4
- package/dist/chunks/components-q037dya7.js +0 -4
- package/dist/chunks/content-source-t0q184js.js +0 -4
- package/dist/chunks/editor-j5sq149q.js +0 -4
- package/dist/chunks/entry-commands-fxzgcc4k.js +0 -4
- package/dist/chunks/file-ops-2n77759h.js +0 -4
- package/dist/chunks/files-fjy7zpxt.js +0 -4
- package/dist/chunks/format-host-wj3mf3n8.js +0 -4
- package/dist/chunks/fs-events-7kcgn4e9.js +0 -4
- package/dist/chunks/git-panel-7z2mpt2d.js +0 -4
- package/dist/chunks/icons-qbh8akb1.js +0 -4
- package/dist/chunks/iframe-host-a0zjd6xp.js +0 -4
- package/dist/chunks/index-m0x5ztax.js +0 -4
- package/dist/chunks/jsonMode-800yvbem.js +0 -13
- package/dist/chunks/jx-validate-zc9zp77s.js +0 -4
- package/dist/chunks/layers-p677nrh5.js +0 -4
- package/dist/chunks/layers-panel-y4w1dq90.js +0 -4
- package/dist/chunks/lit-html-cfn61dn7.js +0 -4
- package/dist/chunks/media-picker-xg7yjpz9.js +0 -4
- package/dist/chunks/menu-fpekzj6w.js +0 -4
- package/dist/chunks/monaco-buffer-nn4dtx27.js +0 -4
- package/dist/chunks/monaco-setup-tj13ndtm.js +0 -4
- package/dist/chunks/notify-ahs57a2z.js +0 -4
- package/dist/chunks/platform-v9xyfkbx.js +0 -4
- package/dist/chunks/publish-panel-xtbfgg2r.js +0 -4
- package/dist/chunks/quick-search-d3a6m7j3.js +0 -4
- package/dist/chunks/references-99ws0b0m.js +0 -4
- package/dist/chunks/right-panel-31vbk5xp.js +0 -4
- package/dist/chunks/section-registry-b2yzp9sw.js +0 -4
- package/dist/chunks/studio-1a99w39s.js +0 -5
- package/dist/chunks/studio-3ck2a5c4.js +0 -10
- package/dist/chunks/studio-4fde4a0x.js +0 -5
- package/dist/chunks/studio-4jj9gme2.js +0 -6
- package/dist/chunks/studio-4m50vh74.js +0 -16
- package/dist/chunks/studio-4xppwncr.js +0 -4
- package/dist/chunks/studio-5d7wfmnp.js +0 -5
- package/dist/chunks/studio-5e9zsamh.js +0 -9
- package/dist/chunks/studio-5gs9th4b.js +0 -5
- package/dist/chunks/studio-7qvs7v0m.js +0 -5
- package/dist/chunks/studio-84rtc120.js +0 -5
- package/dist/chunks/studio-aekhcdzw.js +0 -7
- package/dist/chunks/studio-aqmph6k4.js +0 -5
- package/dist/chunks/studio-c6zdm25z.js +0 -9
- package/dist/chunks/studio-capk0583.js +0 -9
- package/dist/chunks/studio-chdmwwzw.js +0 -5
- package/dist/chunks/studio-cp1gbff9.js +0 -14
- package/dist/chunks/studio-cqmtbg2p.js +0 -5
- package/dist/chunks/studio-e47215hh.js +0 -5
- package/dist/chunks/studio-fbx6twps.js +0 -6
- package/dist/chunks/studio-fthwn4kf.js +0 -5
- package/dist/chunks/studio-g8dsj3d2.js +0 -6
- package/dist/chunks/studio-gjknvzam.js +0 -9
- package/dist/chunks/studio-hjbg8d5y.js +0 -12
- package/dist/chunks/studio-nbxqcgvf.js +0 -5
- package/dist/chunks/studio-t1eedsyx.js +0 -4
- package/dist/chunks/studio-vavmwshr.js +0 -5
- package/dist/chunks/studio-x5g1xk77.js +0 -5
- package/dist/chunks/studio-x92tgnee.js +0 -64
- package/dist/chunks/studio-xg1942as.js +0 -5
- package/dist/chunks/studio-zev8ssp1.js +0 -5
- package/dist/chunks/tsMode-k9s9fcag.js +0 -14
- package/dist/chunks/workspace-5vmgsagj.js +0 -4
- package/dist/chunks/ws-client-0ja08haa.js +0 -4
package/src/surfaces/add-repo.ts
CHANGED
|
@@ -3,29 +3,36 @@
|
|
|
3
3
|
* The repository picker as a mounted document.
|
|
4
4
|
*
|
|
5
5
|
* `new-project/add-repo-modal.ts` is the flow — it asks the platform which repositories the account
|
|
6
|
-
* link reaches,
|
|
6
|
+
* link reaches, which projects a chosen repository holds, adopts the one that was confirmed and
|
|
7
7
|
* resolves the promise its two entry points return — and this is the surface it draws into: the
|
|
8
|
-
* reactive scope the document reads, the
|
|
9
|
-
*
|
|
8
|
+
* reactive scope the document reads, the discriminants it branches on, and the mount that lives in
|
|
9
|
+
* the dialog layer until the flow takes it down.
|
|
10
|
+
*
|
|
11
|
+
* **Two fields, each with the list it drives.** The filter drives the repository list and the
|
|
12
|
+
* project-folder field drives the list of folders that repository holds a `project.json` in. A
|
|
13
|
+
* `jx-listbox` never takes focus (ui.md §5.3), so a list a reader can only click is a list a
|
|
14
|
+
* keyboard cannot reach; pairing each list with a field is what makes both of them operable from
|
|
15
|
+
* the keys — the palette's shape, twice. The folder field is also the answer when a list cannot be:
|
|
16
|
+
* a repository too large to scan, or a platform with no way to scan one.
|
|
10
17
|
*
|
|
11
18
|
* **The flow states facts; the surface derives what the document branches on.** Which repositories
|
|
12
|
-
* are visible,
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* anything" are two sentences with two different remedies.
|
|
19
|
+
* are visible, which one is chosen and which folders it holds are the flow's; whether that adds up
|
|
20
|
+
* to a list, a spinner or one of four different absences, which row id each listbox is told is
|
|
21
|
+
* active, and whether the dialog can be answered yet are {@link derive}'s.
|
|
16
22
|
*
|
|
17
23
|
* **Await the ELEMENT, not just the mount.** `mountSurface` resolving means the DOCUMENT rendered;
|
|
18
24
|
* a `jx-dialog`'s own template is one `connectedCallback` later, and `showModal` in between finds
|
|
19
25
|
* no `<dialog>` to open — the body is all there, correctly styled, and never shows. `whenReady` is
|
|
20
26
|
* shared with `surfaces/dialog.ts` and `surfaces/cf-account-picker.ts` rather than reimplemented.
|
|
21
27
|
*
|
|
22
|
-
* @docs studio/
|
|
28
|
+
* @docs studio/interface/welcome-screen
|
|
23
29
|
*/
|
|
24
30
|
|
|
25
31
|
import { reactive } from "../reactivity";
|
|
26
32
|
import { mountSurface, registerSurface } from "../ui/surface";
|
|
27
33
|
import { overlayRegion, REGION_ATTR } from "../ui/regions";
|
|
28
34
|
import { close as closeDialog, showModal } from "@jxsuite/ui/behaviors/dialog";
|
|
35
|
+
import { focusField } from "@jxsuite/ui/behaviors/textfield";
|
|
29
36
|
import { whenReady } from "./dialog";
|
|
30
37
|
import addRepoDoc from "./add-repo.json";
|
|
31
38
|
import type { SurfaceHandle } from "../ui/surface";
|
|
@@ -35,17 +42,23 @@ registerSurface("add-repo", addRepoDoc as unknown as JxDocument);
|
|
|
35
42
|
|
|
36
43
|
/** One repository, as the list draws it. Every field is already a string or a flag. */
|
|
37
44
|
export interface AddRepoRow {
|
|
38
|
-
/** `owner/name`, which is the row's key, its title and the text it leads with. */
|
|
45
|
+
/** `owner/name`, which is the row's key, its value, its title and the text it leads with. */
|
|
39
46
|
fullName: string;
|
|
40
|
-
/** The branch
|
|
47
|
+
/** The default branch, beside the name. */
|
|
41
48
|
meta: string;
|
|
42
49
|
/** The repository already carries the Jx topic, so it gets the badge. */
|
|
43
50
|
isJx: boolean;
|
|
44
51
|
isPrivate: boolean;
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** One folder of the chosen repository that holds a `project.json`. */
|
|
55
|
+
export interface AddRepoLocation {
|
|
56
|
+
/** The folder, repository-relative and canonical; `""` is the root. The row's value. */
|
|
57
|
+
dir: string;
|
|
58
|
+
/** What the row says: the project's own name, else the folder's. */
|
|
59
|
+
label: string;
|
|
60
|
+
/** Where it is: `/` for the root, `sites/blog/` for a folder. */
|
|
61
|
+
path: string;
|
|
49
62
|
}
|
|
50
63
|
|
|
51
64
|
/** One way to widen what the GitHub App can reach. */
|
|
@@ -55,24 +68,50 @@ export interface AddRepoAccessLink {
|
|
|
55
68
|
title: string;
|
|
56
69
|
}
|
|
57
70
|
|
|
71
|
+
/** One entry of the account filter. `""` is every account. */
|
|
72
|
+
export interface AddRepoOwner {
|
|
73
|
+
value: string;
|
|
74
|
+
label: string;
|
|
75
|
+
}
|
|
76
|
+
|
|
58
77
|
/**
|
|
59
|
-
* Why the list is empty, when it is.
|
|
78
|
+
* Why the repository list is empty, when it is.
|
|
60
79
|
*
|
|
61
80
|
* Four, not one, because only two of them are the reader's to fix and each is fixed differently: a
|
|
62
81
|
* filter is cleared, write access is asked for, and an App that was never installed is installed.
|
|
63
82
|
*/
|
|
64
83
|
export type AddRepoEmptyState = "filter" | "write" | "install" | "none";
|
|
65
84
|
|
|
85
|
+
/**
|
|
86
|
+
* What the detail pane knows about the chosen repository's projects.
|
|
87
|
+
*
|
|
88
|
+
* - `none`: no repository is chosen yet.
|
|
89
|
+
* - `loading`: the platform is looking.
|
|
90
|
+
* - `listed`: it found at least one folder with a `project.json`.
|
|
91
|
+
* - `empty`: it looked and found none.
|
|
92
|
+
* - `unlisted`: the platform cannot look, so the folder is the reader's to name.
|
|
93
|
+
* - `failed`: the look itself failed; the folder field still works.
|
|
94
|
+
*/
|
|
95
|
+
export type AddRepoDetailState = "none" | "loading" | "listed" | "empty" | "unlisted" | "failed";
|
|
96
|
+
|
|
66
97
|
/** What the flow knows. Every one of these may change while the dialog is up. */
|
|
67
98
|
export interface AddRepoView {
|
|
68
99
|
/** The headline, which is also the dialog's accessible name: the two flows name themselves. */
|
|
69
100
|
title: string;
|
|
101
|
+
/** The primary answer: Open or Add, and the progressive form while an adoption runs. */
|
|
102
|
+
confirmLabel: string;
|
|
70
103
|
/** The filter text, held by the flow so a repaint cannot lose what the reader typed. */
|
|
71
104
|
filter: string;
|
|
105
|
+
/** The account the list is narrowed to; `""` is every account. */
|
|
106
|
+
owner: string;
|
|
107
|
+
/** The account filter's entries, "All accounts" first; empty hides it (one account, or none). */
|
|
108
|
+
owners: AddRepoOwner[];
|
|
72
109
|
/** The listing is in flight. Its own state, because "not yet asked" is not "reaches nothing". */
|
|
73
110
|
loading: boolean;
|
|
74
111
|
/** The repositories to draw, already filtered and ordered. */
|
|
75
112
|
rows: AddRepoRow[];
|
|
113
|
+
/** The chosen repository's full name; `""` when none is. It may be filtered out of `rows`. */
|
|
114
|
+
selected: string;
|
|
76
115
|
/** Which absence to say, read only when `rows` is empty and the listing has landed. */
|
|
77
116
|
emptyState: AddRepoEmptyState;
|
|
78
117
|
/** Where the GitHub App is installed from, for the sentence that offers to install it. */
|
|
@@ -81,6 +120,30 @@ export interface AddRepoView {
|
|
|
81
120
|
failure: string;
|
|
82
121
|
/** The access footer's links; empty for a platform with no account status to widen. */
|
|
83
122
|
access: AddRepoAccessLink[];
|
|
123
|
+
/** The chosen repository's projects, as far as the platform has looked. */
|
|
124
|
+
detailState: AddRepoDetailState;
|
|
125
|
+
/** The chosen repository's full name and the line beside it, for the pane's heading. */
|
|
126
|
+
detailName: string;
|
|
127
|
+
detailMeta: string;
|
|
128
|
+
/** The folders the chosen repository holds a `project.json` in. */
|
|
129
|
+
locations: AddRepoLocation[];
|
|
130
|
+
/** The folder field's text: the folder the dialog will open, `""` for the repository root. */
|
|
131
|
+
folder: string;
|
|
132
|
+
/**
|
|
133
|
+
* The same folder in its canonical spelling (`utils/project-dir.ts`), which is what a folder row
|
|
134
|
+
* is matched against: `sites/blog/` typed by hand is the `sites/blog` row.
|
|
135
|
+
*/
|
|
136
|
+
chosenFolder: string;
|
|
137
|
+
/** Why the folder field's text cannot be opened; `""` when it can. */
|
|
138
|
+
folderError: string;
|
|
139
|
+
/** The sentence under the folder field. */
|
|
140
|
+
folderHelp: string;
|
|
141
|
+
/** What confirming will do, said in one line; `""` when nothing is chosen. */
|
|
142
|
+
summary: string;
|
|
143
|
+
/** The dialog can be answered: a repository is chosen, its folder is usable, nothing is running. */
|
|
144
|
+
canConfirm: boolean;
|
|
145
|
+
/** An adoption is in flight. */
|
|
146
|
+
busy: boolean;
|
|
84
147
|
}
|
|
85
148
|
|
|
86
149
|
export interface AddRepoSurfaceOptions {
|
|
@@ -90,8 +153,22 @@ export interface AddRepoSurfaceOptions {
|
|
|
90
153
|
view: AddRepoView;
|
|
91
154
|
/** The reader typed in the filter field. The flow decides what it hides. */
|
|
92
155
|
onFilter: (value: string) => void;
|
|
93
|
-
/**
|
|
94
|
-
|
|
156
|
+
/** The reader narrowed the list to one account, or widened it to all of them (`""`). */
|
|
157
|
+
onOwner: (value: string) => void;
|
|
158
|
+
/** A repository row was chosen, by pointer or by the filter's arrows. */
|
|
159
|
+
onSelect: (fullName: string) => void;
|
|
160
|
+
/** The caret moved by `delta` rows through the visible repositories. */
|
|
161
|
+
onMove: (delta: number) => void;
|
|
162
|
+
/** The reader typed in the folder field. */
|
|
163
|
+
onFolder: (value: string) => void;
|
|
164
|
+
/** A folder row was chosen. */
|
|
165
|
+
onLocation: (dir: string) => void;
|
|
166
|
+
/** The caret moved by `delta` rows through the chosen repository's folders. */
|
|
167
|
+
onMoveLocation: (delta: number) => void;
|
|
168
|
+
/** Open what is chosen: the confirm button, Enter in either field, or a double-click on a row. */
|
|
169
|
+
onConfirm: () => void;
|
|
170
|
+
/** A repository row was double-clicked: choose it and open it as soon as that is unambiguous. */
|
|
171
|
+
onOpenRepo: (fullName: string) => void;
|
|
95
172
|
/** Read the listing again, after access was granted in another tab. */
|
|
96
173
|
onRefresh: () => void;
|
|
97
174
|
/** The dialog closed, for any reason the platform owns as well as this module's `close`. */
|
|
@@ -109,43 +186,101 @@ export interface AddRepoSurfaceHandle {
|
|
|
109
186
|
close: () => void;
|
|
110
187
|
}
|
|
111
188
|
|
|
189
|
+
/** A repository row with the id its listbox addresses it by. */
|
|
190
|
+
interface DrawnRow extends AddRepoRow {
|
|
191
|
+
id: string;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/** A folder row with the id its listbox addresses it by. */
|
|
195
|
+
interface DrawnLocation extends AddRepoLocation {
|
|
196
|
+
id: string;
|
|
197
|
+
}
|
|
198
|
+
|
|
112
199
|
/** What the document discriminates on, and the flags it reads. Derived, never handed in. */
|
|
113
200
|
interface AddRepoFlags {
|
|
114
201
|
/**
|
|
115
|
-
* Which body
|
|
202
|
+
* Which body the repository pane draws.
|
|
116
203
|
*
|
|
117
204
|
* Six, because a spinner, a list and four absences are six different things to say and only one
|
|
118
205
|
* of them is fixed by looking again.
|
|
119
206
|
*/
|
|
120
207
|
listState: "loading" | "listed" | "empty-filter" | "empty-write" | "empty-install" | "empty-none";
|
|
121
|
-
|
|
208
|
+
rows: DrawnRow[];
|
|
209
|
+
locations: DrawnLocation[];
|
|
210
|
+
/** The id each listbox is told is active: one string per list (ui.md §5.3); `""` for none. */
|
|
211
|
+
activeRepoId: string;
|
|
212
|
+
activeLocationId: string;
|
|
213
|
+
/** Whether there is more than one account to narrow by. */
|
|
214
|
+
hasOwners: boolean;
|
|
215
|
+
/** Whether a refusal sits above the panes that survived it. */
|
|
122
216
|
hasFailure: boolean;
|
|
123
217
|
/** Whether there is anything to offer in the access footer. */
|
|
124
218
|
hasAccess: boolean;
|
|
125
|
-
/**
|
|
219
|
+
/** Whether a repository is chosen, so the folder field has something to name a folder of. */
|
|
220
|
+
hasDetail: boolean;
|
|
221
|
+
/** Whether the folder field's text is refused. */
|
|
222
|
+
folderInvalid: boolean;
|
|
223
|
+
/** Refresh cannot re-ask while the answer is still coming, or while an adoption runs. */
|
|
126
224
|
refreshDisabled: boolean;
|
|
225
|
+
/** The confirm button's state, the inverse of `canConfirm`. */
|
|
226
|
+
confirmDisabled: boolean;
|
|
127
227
|
}
|
|
128
228
|
|
|
129
|
-
interface AddRepoScope
|
|
229
|
+
interface AddRepoScope
|
|
230
|
+
extends Record<string, unknown>, Omit<AddRepoView, "rows" | "locations">, AddRepoFlags {
|
|
130
231
|
setFilter: (value: string) => void;
|
|
131
|
-
|
|
232
|
+
setOwner: (value: string) => void;
|
|
233
|
+
select: (fullName: string) => void;
|
|
234
|
+
move: (delta: number) => void;
|
|
235
|
+
setFolder: (value: string) => void;
|
|
236
|
+
pickLocation: (dir: string) => void;
|
|
237
|
+
moveLocation: (delta: number) => void;
|
|
238
|
+
confirm: () => void;
|
|
239
|
+
openRepo: (fullName: string) => void;
|
|
132
240
|
refresh: () => void;
|
|
133
241
|
closed: () => void;
|
|
134
242
|
}
|
|
135
243
|
|
|
244
|
+
/** The id the repository list addresses row `index` by. */
|
|
245
|
+
export function repoRowId(index: number): string {
|
|
246
|
+
return `add-repo-repo-${index}`;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/** The id the folder list addresses row `index` by. */
|
|
250
|
+
export function locationRowId(index: number): string {
|
|
251
|
+
return `add-repo-location-${index}`;
|
|
252
|
+
}
|
|
253
|
+
|
|
136
254
|
/** The view plus the flags the document renders from. */
|
|
137
|
-
function derive(view: AddRepoView): AddRepoView & AddRepoFlags {
|
|
138
|
-
const
|
|
255
|
+
function derive(view: AddRepoView): Omit<AddRepoView, "rows" | "locations"> & AddRepoFlags {
|
|
256
|
+
const rows = view.rows.map((row, index) => ({ ...row, id: repoRowId(index) }));
|
|
257
|
+
const locations = view.locations.map((location, index) => ({
|
|
258
|
+
...location,
|
|
259
|
+
id: locationRowId(index),
|
|
260
|
+
}));
|
|
261
|
+
const activeRow = rows.find((row) => row.fullName === view.selected);
|
|
262
|
+
const activeLocation =
|
|
263
|
+
view.folderError === ""
|
|
264
|
+
? locations.find((location) => location.dir === view.chosenFolder)
|
|
265
|
+
: undefined;
|
|
139
266
|
return {
|
|
140
267
|
...view,
|
|
268
|
+
activeLocationId: activeLocation?.id ?? "",
|
|
269
|
+
activeRepoId: activeRow?.id ?? "",
|
|
270
|
+
confirmDisabled: !view.canConfirm,
|
|
271
|
+
folderInvalid: view.folderError !== "",
|
|
141
272
|
hasAccess: view.access.length > 0,
|
|
273
|
+
hasDetail: view.detailState !== "none",
|
|
142
274
|
hasFailure: view.failure !== "",
|
|
275
|
+
hasOwners: view.owners.length > 0,
|
|
143
276
|
listState: view.loading
|
|
144
277
|
? "loading"
|
|
145
|
-
:
|
|
278
|
+
: rows.length > 0
|
|
146
279
|
? "listed"
|
|
147
280
|
: (`empty-${view.emptyState}` as AddRepoFlags["listState"]),
|
|
148
|
-
|
|
281
|
+
locations,
|
|
282
|
+
refreshDisabled: view.loading || view.busy,
|
|
283
|
+
rows,
|
|
149
284
|
};
|
|
150
285
|
}
|
|
151
286
|
|
|
@@ -177,21 +312,44 @@ export function openAddRepoSurface(options: AddRepoSurfaceOptions): AddRepoSurfa
|
|
|
177
312
|
|
|
178
313
|
const scope = reactive({
|
|
179
314
|
...derive(options.view),
|
|
180
|
-
choose: (fullName: string) => {
|
|
181
|
-
options.onChoose(fullName);
|
|
182
|
-
},
|
|
183
315
|
closed: finish,
|
|
316
|
+
confirm: () => {
|
|
317
|
+
options.onConfirm();
|
|
318
|
+
},
|
|
319
|
+
move: (delta: number) => {
|
|
320
|
+
options.onMove(delta);
|
|
321
|
+
},
|
|
322
|
+
moveLocation: (delta: number) => {
|
|
323
|
+
options.onMoveLocation(delta);
|
|
324
|
+
},
|
|
325
|
+
openRepo: (fullName: string) => {
|
|
326
|
+
options.onOpenRepo(fullName);
|
|
327
|
+
},
|
|
328
|
+
pickLocation: (dir: string) => {
|
|
329
|
+
options.onLocation(dir);
|
|
330
|
+
},
|
|
184
331
|
refresh: () => {
|
|
185
332
|
options.onRefresh();
|
|
186
333
|
},
|
|
334
|
+
select: (fullName: string) => {
|
|
335
|
+
options.onSelect(fullName);
|
|
336
|
+
},
|
|
187
337
|
setFilter: (value: string) => {
|
|
188
338
|
/* What the control holds, first and unconditionally. A binding only writes when the scope
|
|
189
339
|
CHANGES, so a surface that decided a value without announcing the raw one would leave the
|
|
190
|
-
field showing text the scope does not have (`studio-ui-guidelines.md` §9.3).
|
|
191
|
-
refuses a filter today; the echo is what makes a refusal possible at all. */
|
|
340
|
+
field showing text the scope does not have (`studio-ui-guidelines.md` §9.3). */
|
|
192
341
|
scope.filter = value;
|
|
193
342
|
options.onFilter(value);
|
|
194
343
|
},
|
|
344
|
+
setFolder: (value: string) => {
|
|
345
|
+
// The same echo as the filter's, for the same reason.
|
|
346
|
+
scope.folder = value;
|
|
347
|
+
options.onFolder(value);
|
|
348
|
+
},
|
|
349
|
+
setOwner: (value: string) => {
|
|
350
|
+
scope.owner = value;
|
|
351
|
+
options.onOwner(value);
|
|
352
|
+
},
|
|
195
353
|
}) as AddRepoScope;
|
|
196
354
|
|
|
197
355
|
let mounted: SurfaceHandle | null = null;
|
|
@@ -207,6 +365,13 @@ export function openAddRepoSurface(options: AddRepoSurfaceOptions): AddRepoSurfa
|
|
|
207
365
|
return element;
|
|
208
366
|
}
|
|
209
367
|
showModal(element);
|
|
368
|
+
/* The body claims focus (studio-ui-guidelines.md §8.7): the filter is where every way of
|
|
369
|
+
choosing starts, and the confirm button that would otherwise take it is disabled until a
|
|
370
|
+
repository is chosen, which leaves the platform nothing to land on. */
|
|
371
|
+
const filter = element.querySelector<HTMLElement>('[part="filter"]');
|
|
372
|
+
if (filter) {
|
|
373
|
+
focusField(filter);
|
|
374
|
+
}
|
|
210
375
|
return element;
|
|
211
376
|
});
|
|
212
377
|
|
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
|
|
31
31
|
import { reactive } from "../reactivity";
|
|
32
32
|
import { mountSurface, registerSurface } from "../ui/surface";
|
|
33
|
+
import { placeholderBrokenImages } from "../utils/edit-display";
|
|
33
34
|
import libraryDoc from "./library-pane.json";
|
|
34
35
|
import type { JxDocument, JxElement } from "@jxsuite/schema/types";
|
|
35
36
|
import type { JxScope } from "@jxsuite/runtime/types";
|
|
@@ -267,6 +268,10 @@ export function mountLibrarySurface(
|
|
|
267
268
|
actions: LibraryActions,
|
|
268
269
|
): LibrarySurfaceHandle {
|
|
269
270
|
host.replaceChildren();
|
|
271
|
+
// A file that will not load, or a preview image with nothing to bind, draws a placeholder rather
|
|
272
|
+
// Than the browser's broken-image glyph.
|
|
273
|
+
const listening = new AbortController();
|
|
274
|
+
placeholderBrokenImages(host, listening.signal);
|
|
270
275
|
const scope = reactive<LibraryScope>({
|
|
271
276
|
bannerState: "hidden",
|
|
272
277
|
bannerText: "",
|
|
@@ -355,6 +360,7 @@ export function mountLibrarySurface(
|
|
|
355
360
|
},
|
|
356
361
|
dispose() {
|
|
357
362
|
disposed = true;
|
|
363
|
+
listening.abort();
|
|
358
364
|
mounted?.dispose();
|
|
359
365
|
mounted = null;
|
|
360
366
|
},
|
package/src/surfaces/rail.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://jxsuite.com/schema/v1",
|
|
3
3
|
"$id": "StudioRail",
|
|
4
|
-
"$description": "The Navigator rail: a rendering of the panel registry. The adapter hands in `groups`, every rail-able panel grouped by level with its selected state and badge, and `settingsVisible`/`settingsOpen` for the
|
|
4
|
+
"$description": "The Navigator rail: a rendering of the panel registry. The adapter hands in `groups`, every rail-able panel grouped by level with its selected state and badge, and, for the foot, `libraryVisible`/`libraryOpen` for the Library button and `settingsVisible`/`settingsOpen` for the gear. Nothing here is a list of panels. Each button prints its record's short `railLabel` under the glyph, falling back to `title`, and carries the full `title` as its accessible name and tooltip: the visible words lead the name a reader hears (WCAG 2.5.3), and the tooltip restores what a short label leaves out.",
|
|
5
5
|
"tagName": "div",
|
|
6
6
|
"attributes": {
|
|
7
7
|
"part": "rail"
|
|
@@ -162,94 +162,147 @@
|
|
|
162
162
|
"attributes": {
|
|
163
163
|
"part": "foot"
|
|
164
164
|
},
|
|
165
|
-
"
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
"true": {
|
|
170
|
-
"tagName": "jx-action-button",
|
|
171
|
-
"$props": {
|
|
172
|
-
"label": "Settings",
|
|
173
|
-
"hint": "Settings",
|
|
174
|
-
"icon": "gear",
|
|
175
|
-
"stacked": true,
|
|
176
|
-
"haspopup": "menu",
|
|
177
|
-
"expanded": {
|
|
178
|
-
"$ref": "#/state/settingsOpen"
|
|
179
|
-
}
|
|
165
|
+
"children": [
|
|
166
|
+
{
|
|
167
|
+
"$switch": {
|
|
168
|
+
"$ref": "#/state/libraryVisible"
|
|
180
169
|
},
|
|
170
|
+
"tagName": "div",
|
|
181
171
|
"attributes": {
|
|
182
|
-
"
|
|
183
|
-
"data-expanded": "${state.settingsOpen ? '' : null}"
|
|
172
|
+
"part": "foot-library"
|
|
184
173
|
},
|
|
185
|
-
"
|
|
186
|
-
"
|
|
187
|
-
|
|
188
|
-
{
|
|
189
|
-
"
|
|
190
|
-
"
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
"
|
|
174
|
+
"cases": {
|
|
175
|
+
"true": {
|
|
176
|
+
"tagName": "jx-action-button",
|
|
177
|
+
"$props": {
|
|
178
|
+
"label": "Library",
|
|
179
|
+
"hint": "Library",
|
|
180
|
+
"icon": "books",
|
|
181
|
+
"stacked": true,
|
|
182
|
+
"selected": {
|
|
183
|
+
"$ref": "#/state/libraryOpen"
|
|
184
|
+
}
|
|
185
|
+
},
|
|
186
|
+
"attributes": {
|
|
187
|
+
"data-rail-action": "library.open"
|
|
188
|
+
},
|
|
189
|
+
"onclick": {
|
|
190
|
+
"$prototype": "Function",
|
|
191
|
+
"body": [
|
|
194
192
|
{
|
|
195
|
-
"
|
|
193
|
+
"operator": "call",
|
|
194
|
+
"target": {
|
|
195
|
+
"$ref": "#/state/openLibrary"
|
|
196
|
+
},
|
|
197
|
+
"value": []
|
|
196
198
|
}
|
|
197
199
|
]
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
+
},
|
|
201
|
+
"children": [
|
|
202
|
+
{
|
|
203
|
+
"tagName": "span",
|
|
204
|
+
"textContent": "Library"
|
|
205
|
+
}
|
|
206
|
+
]
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
},
|
|
210
|
+
{
|
|
211
|
+
"tagName": "div",
|
|
212
|
+
"attributes": {
|
|
213
|
+
"part": "foot-settings"
|
|
200
214
|
},
|
|
201
|
-
"
|
|
202
|
-
"$
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
"
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
215
|
+
"$switch": {
|
|
216
|
+
"$ref": "#/state/settingsVisible"
|
|
217
|
+
},
|
|
218
|
+
"cases": {
|
|
219
|
+
"true": {
|
|
220
|
+
"tagName": "jx-action-button",
|
|
221
|
+
"$props": {
|
|
222
|
+
"label": "Settings",
|
|
223
|
+
"hint": "Settings",
|
|
224
|
+
"icon": "gear",
|
|
225
|
+
"stacked": true,
|
|
226
|
+
"haspopup": "menu",
|
|
227
|
+
"expanded": {
|
|
228
|
+
"$ref": "#/state/settingsOpen"
|
|
229
|
+
}
|
|
230
|
+
},
|
|
231
|
+
"attributes": {
|
|
232
|
+
"data-rail-menu": "settings",
|
|
233
|
+
"data-expanded": "${state.settingsOpen ? '' : null}"
|
|
234
|
+
},
|
|
235
|
+
"onclick": {
|
|
236
|
+
"$prototype": "Function",
|
|
237
|
+
"body": [
|
|
238
|
+
{
|
|
239
|
+
"operator": "call",
|
|
240
|
+
"target": {
|
|
241
|
+
"$ref": "#/state/openSettings"
|
|
242
|
+
},
|
|
243
|
+
"value": [
|
|
244
|
+
{
|
|
245
|
+
"$ref": "event#/currentTarget"
|
|
246
|
+
}
|
|
247
|
+
]
|
|
248
|
+
}
|
|
249
|
+
]
|
|
250
|
+
},
|
|
251
|
+
"onkeydown": {
|
|
252
|
+
"$prototype": "Function",
|
|
253
|
+
"body": [
|
|
254
|
+
{
|
|
255
|
+
"$switch": {
|
|
256
|
+
"$ref": "event#/key"
|
|
257
|
+
},
|
|
258
|
+
"cases": {
|
|
259
|
+
"ArrowUp": [
|
|
260
|
+
{
|
|
261
|
+
"operator": "call",
|
|
262
|
+
"target": {
|
|
263
|
+
"$ref": "#/state/openSettings"
|
|
264
|
+
},
|
|
265
|
+
"value": [
|
|
266
|
+
{
|
|
267
|
+
"$ref": "event#/currentTarget"
|
|
268
|
+
}
|
|
269
|
+
]
|
|
270
|
+
},
|
|
216
271
|
{
|
|
217
|
-
"
|
|
272
|
+
"preventDefault": true
|
|
218
273
|
}
|
|
219
|
-
]
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
274
|
+
],
|
|
275
|
+
"ArrowDown": [
|
|
276
|
+
{
|
|
277
|
+
"operator": "call",
|
|
278
|
+
"target": {
|
|
279
|
+
"$ref": "#/state/openSettings"
|
|
280
|
+
},
|
|
281
|
+
"value": [
|
|
282
|
+
{
|
|
283
|
+
"$ref": "event#/currentTarget"
|
|
284
|
+
}
|
|
285
|
+
]
|
|
286
|
+
},
|
|
232
287
|
{
|
|
233
|
-
"
|
|
288
|
+
"preventDefault": true
|
|
234
289
|
}
|
|
235
290
|
]
|
|
236
|
-
},
|
|
237
|
-
{
|
|
238
|
-
"preventDefault": true
|
|
239
291
|
}
|
|
240
|
-
|
|
292
|
+
}
|
|
293
|
+
]
|
|
294
|
+
},
|
|
295
|
+
"children": [
|
|
296
|
+
{
|
|
297
|
+
"tagName": "span",
|
|
298
|
+
"textContent": "Settings"
|
|
241
299
|
}
|
|
242
|
-
|
|
243
|
-
]
|
|
244
|
-
},
|
|
245
|
-
"children": [
|
|
246
|
-
{
|
|
247
|
-
"tagName": "span",
|
|
248
|
-
"textContent": "Settings"
|
|
300
|
+
]
|
|
249
301
|
}
|
|
250
|
-
|
|
302
|
+
}
|
|
251
303
|
}
|
|
252
|
-
|
|
304
|
+
],
|
|
305
|
+
"$description": "The two rail buttons that open no Navigator panel: the Library, which is a canvas tab rather than a panel and so sits with the other exception, above the gear."
|
|
253
306
|
}
|
|
254
307
|
]
|
|
255
308
|
}
|