wings-plugin-sdk 0.4.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -181
- package/build.d.ts +1 -1
- package/build.mjs +5 -5
- package/package.json +3 -12
- package/shell.d.ts +1 -1
- package/shell.js +1 -14
- package/ui-css.d.ts +0 -4
- package/ui-css.js +0 -709
- package/ui.d.ts +0 -230
- package/ui.js +0 -593
package/README.md
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# wings-plugin-sdk
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The `wings` API types, the page frame, React hooks and the build command for [Wings](https://github.com/josippapez/Wings)
|
|
4
|
+
plugins. The components for plugin pages are in [wings-plugin-ui](../plugin-ui/README.md).
|
|
4
5
|
|
|
5
6
|
Wings itself gives each plugin the `wings` object at runtime. This package doesn't contain it: it types it, and
|
|
6
7
|
bundles your TypeScript and React into the plain scripts Wings loads.
|
|
@@ -9,7 +10,7 @@ bundles your TypeScript and React into the plain scripts Wings loads.
|
|
|
9
10
|
|
|
10
11
|
```sh
|
|
11
12
|
npm install --save-dev wings-plugin-sdk typescript
|
|
12
|
-
npm install --save-dev react react-dom @types/react @types/react-dom # only for React pages
|
|
13
|
+
npm install --save-dev wings-plugin-ui react react-dom @types/react @types/react-dom # only for React pages
|
|
13
14
|
```
|
|
14
15
|
|
|
15
16
|
`tsconfig.json`:
|
|
@@ -33,7 +34,7 @@ npm install --save-dev react react-dom @types/react @types/react-dom # only fo
|
|
|
33
34
|
|
|
34
35
|
Put the code in `src/`, next to `wings-plugin.json`. Every file at the top of `src/` builds to the file of the
|
|
35
36
|
same name, so `src/main.ts` becomes `main.js` and `src/sidebar.tsx` becomes `sidebar.js`. A page that uses
|
|
36
|
-
`wings-plugin-
|
|
37
|
+
`wings-plugin-ui` also gets its `.html` written for it, unless you wrote your own. Folders inside `src/` hold
|
|
37
38
|
the modules they import.
|
|
38
39
|
|
|
39
40
|
```sh
|
|
@@ -47,9 +48,11 @@ as it is, like the plugins in the Wings repo.
|
|
|
47
48
|
|
|
48
49
|
## Pages
|
|
49
50
|
|
|
50
|
-
A sidebar, popover, view, tab or settings page is a file in `src/` that calls one of the `show*` functions
|
|
51
|
-
[
|
|
52
|
-
|
|
51
|
+
A sidebar, popover, view, tab or settings page is a file in `src/` that calls one of the `show*` functions of
|
|
52
|
+
[wings-plugin-ui](../plugin-ui/README.md). The build writes its HTML. A page that builds its DOM itself, without
|
|
53
|
+
React, gets the same frame from `mountShell(place, options)` in `wings-plugin-sdk/shell`, which returns the element
|
|
54
|
+
to fill. Every `wings.on*` call returns a function that removes the listener, so `useBroadcast` from
|
|
55
|
+
`wings-plugin-sdk/react`, or your own `useEffect`, cleans up when a component goes. The API reference is
|
|
53
56
|
[plugins/README.md](https://github.com/josippapez/Wings/blob/main/plugins/README.md) in the Wings repo.
|
|
54
57
|
|
|
55
58
|
## Pack
|
|
@@ -67,178 +70,3 @@ files your plugin ships, like its own `.html`, `.css` and images. It leaves out
|
|
|
67
70
|
`package.json` and lock files, `tsconfig*.json`, `README*`, `HANDOVER*`, dotfiles and other `.wings-plugin` files.
|
|
68
71
|
Your folder gets no built files. From a script, `pack(dir, { out })` in `wings-plugin-sdk/build` does the same and
|
|
69
72
|
resolves the package's path.
|
|
70
|
-
|
|
71
|
-
## UI
|
|
72
|
-
|
|
73
|
-
`wings-plugin-sdk/ui` lets a page say what it shows and where, and Wings decides how it looks. A page built from
|
|
74
|
-
it never touches the DOM, writes HTML or lays itself out with CSS, so when Wings moves or redesigns a place,
|
|
75
|
-
your page follows without a release.
|
|
76
|
-
|
|
77
|
-
```tsx
|
|
78
|
-
import { Card, Gauge, Heading, showSidebar, useMainState } from "wings-plugin-sdk/ui";
|
|
79
|
-
|
|
80
|
-
type Usage = { type: "usage"; percent: number };
|
|
81
|
-
|
|
82
|
-
function Usage() {
|
|
83
|
-
// Says hello to your main script once, then shows the newest `usage` message it broadcasts.
|
|
84
|
-
const usage = useMainState<Usage>("usage");
|
|
85
|
-
if (!usage) return null;
|
|
86
|
-
return (
|
|
87
|
-
<>
|
|
88
|
-
<Heading>Usage</Heading>
|
|
89
|
-
<Card>
|
|
90
|
-
<Gauge label="Today" percent={usage.percent} levels={[75, 90]} />
|
|
91
|
-
</Card>
|
|
92
|
-
</>
|
|
93
|
-
);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
showSidebar(<Usage />);
|
|
97
|
-
```
|
|
98
|
-
|
|
99
|
-
### Places
|
|
100
|
-
|
|
101
|
-
`showSidebar`, `showPopover`, `showView`, `showTab` and `showSettings` take any React element, your own
|
|
102
|
-
components too, and mount it in that place's shell: Wings' padding, spacing and scrolling for a sidebar, a
|
|
103
|
-
popover, a view (in a modal or a window), a tab or the settings page. Call one per page. `{ fill: true }` lays
|
|
104
|
-
the page out edge to edge with no padding, for a page that scrolls on its own, like a canvas.
|
|
105
|
-
|
|
106
|
-
You don't write the page's HTML. For `src/sidebar.tsx` that calls one of these, `wings-plugin build` writes
|
|
107
|
-
`sidebar.html` with a mount point, its CSS and its script, titled like the manifest's page. A `sidebar.html` you
|
|
108
|
-
write yourself wins: the build only writes a page that's missing or that it wrote before (it carries a
|
|
109
|
-
`Generated by wings-plugin build` line). A popover's page takes its sidebar's title, the name Wings gives the
|
|
110
|
-
popover too. A page that builds its DOM itself, without React, gets the same shell
|
|
111
|
-
from `mountShell(place, options)` in `wings-plugin-sdk/shell`, which returns the element to fill.
|
|
112
|
-
|
|
113
|
-
### Talking to main
|
|
114
|
-
|
|
115
|
-
`useMainState<T>(type, hello?)` returns the newest message of `type` your plugin's other pages broadcast, or
|
|
116
|
-
`null` until one comes. It sends `hello` (`{ type: "hello" }` by default, `null` for none) once when the
|
|
117
|
-
component mounts, so main answers with what it has. `useBroadcast(listener)` gets every message.
|
|
118
|
-
|
|
119
|
-
### Components
|
|
120
|
-
|
|
121
|
-
| Component | Props | Renders |
|
|
122
|
-
|---|---|---|
|
|
123
|
-
| `Row`, `Stack` | `gap` (`none`, `xs`, `s`, `m`, `l`), `grow`; `Row` also `align`, `justify`, `wrap`; `as` (`form`, `ul`, `ol` too) | Children side by side or one under the other; as a `ul` or `ol`, a list with no bullets. With `as`, the props and handlers are the element's: `<Row as="form" onSubmit={(e) => e.currentTarget.requestSubmit()}>` has a `HTMLFormElement` |
|
|
124
|
-
| `Text` | `tone` (`muted`, `info`, `success`, `warn`, `danger`), `size` (`small`, `tiny`), `weight` (`medium`, `strong`), `mono`, `numeric`, `truncate`, `clamp`, `grow`, `as` | A span, or the element in `as` |
|
|
125
|
-
| `Title` | `as` (`h1`, `h2`, `h3`), `size` (`large`), `truncate` | A page's or a card's title, like a sprint's name or a setup step's question |
|
|
126
|
-
| `Heading` | `level` (2, 3, 4), `actions` | A small section heading, `h2` by default, with chips or a button on its right |
|
|
127
|
-
| `Card` | `compact`, `as` (`form`, `ul`, `ol` too) | A card, a card that's a form, or a list. Typed for its `as` element like `Row` and `Stack` |
|
|
128
|
-
| `Gauge`, `Meter` | `label`, `percent`, `note`, `tone` or `levels` | A labelled bar with its percentage, or the bar alone, with the meter role |
|
|
129
|
-
| `Stats` | `items: { label, value, tone? }[]` | Small figures side by side, label over value |
|
|
130
|
-
| `Properties` | `items: { label, value }[]` | Label and value pairs |
|
|
131
|
-
| `List`, `ListItem`, `ListButton` | `ordered` and `compact` on `List`, `align` on the rows | A list of rows; a `ListButton` in a `ListItem` makes the row a button |
|
|
132
|
-
| `Dot`, `Spinner` | `tone`, `size`, `label`; `Dot` also `color` | A status dot or a spinner, announced only with a `label`. A dot's `color` comes from your data, like a work item type's `#cc293d` |
|
|
133
|
-
| `Pill`, `Tag` | `tone` | A filled pill, or an outlined tag like a branch |
|
|
134
|
-
| `Loading`, `Empty` | `Empty`: `title`, `detail` | Before the first data, and nothing to show |
|
|
135
|
-
| `Button` | `variant` (`primary`, `ghost`, `danger`), `size` (`small`), `busy` | A button, `type="button"`; `busy` shows a spinner and ignores clicks, keeping focus |
|
|
136
|
-
| `Chip`, `Chips` | `pressed`; `Chips`: `label`, `options`, `value`, `onChange` | A small quiet button, and a group of them that picks one, with `aria-pressed`. An option's other props, like `id`, go to its chip |
|
|
137
|
-
| `Tabs` | `label`, `items`, `value`, `onChange`, `children` | Tabs with arrow keys, Home and End, over their panel |
|
|
138
|
-
| `Field` | `label`, `hint`, `error`, `id`, `inline` | A labelled field that names and describes the input in it. `id` becomes the input's, for a page that puts focus back by id; `inline` puts the label beside it |
|
|
139
|
-
| `FieldGroup` | `label`, `hint`, `error` | Several controls under one label, like checkboxes: a `fieldset` with its `legend`, described by the hint; an error marks each checkbox invalid |
|
|
140
|
-
| `TextInput`, `SearchField`, `Select` | `label`; `Select`: `options`, `value`, `onChange` | Inputs Wings styles; outside a `Field`, `label` names them |
|
|
141
|
-
| `Checkbox` | `checked`, `onChange`, `children` | A checkbox with its label |
|
|
142
|
-
| `Combobox` | `label`, `value`, `onInput`, `options: { value, label, detail? }[]`, `onPick`, `onEnter`, `onKeyDown`, `freeText`, `openOnFocus`, `filter`, `empty` | A text field with suggestions under it: arrows, Enter, Escape and Tab, and a press outside closes it. With `freeText` it keeps what you type; otherwise it picks one of `options`. `openOnFocus={false}` for a page that puts focus back into it after a render. Without `freeText` the picked label is selected when the field gets focus and after a pick, so typing replaces it. `onEnter(text)` runs on Enter when no option is highlighted, with the typed text, and stops the Enter from submitting a form around it. An `onKeyDown` you pass runs first; `e.preventDefault()` in it skips the combobox's own keys. ArrowDown on a closed combobox opens it and highlights the first option, as in the [WAI-ARIA APG combobox pattern](https://www.w3.org/WAI/ARIA/apg/patterns/combobox/) |
|
|
143
|
-
| `Menu` | `label`, `items: { id, label, onSelect, tone? }[]`, `align`, `variant`, `size` | A button that opens a menu: arrows, Home, End, Enter, Escape |
|
|
144
|
-
| `Disclosure` | `summary` | More behind a line that opens it |
|
|
145
|
-
|
|
146
|
-
Every component passes other props, like `id`, `title`, `className`, `onClick` and `aria-*`, to its element.
|
|
147
|
-
`TextInput` and `SearchField` pass `ref` to the input on React 19.
|
|
148
|
-
|
|
149
|
-
A button that's working on something is `busy`: it shows a spinner, ignores clicks and says so with `aria-busy`
|
|
150
|
-
and `aria-disabled`, and unlike `disabled` it keeps the keyboard focus:
|
|
151
|
-
|
|
152
|
-
```tsx
|
|
153
|
-
<Button variant="primary" busy={saving} onClick={save}>
|
|
154
|
-
Save
|
|
155
|
-
</Button>
|
|
156
|
-
```
|
|
157
|
-
|
|
158
|
-
### Ours or your own
|
|
159
|
-
|
|
160
|
-
The components are there for a page that looks like the rest of Wings without effort, not a rule. Write your
|
|
161
|
-
own components next to them, and your own CSS: import it from the page, `import "./sidebar.css"`, and the build
|
|
162
|
-
bundles it into `sidebar.css` and links it from the generated page. Match Wings with its variables, which every
|
|
163
|
-
page has:
|
|
164
|
-
|
|
165
|
-
| Variable | What |
|
|
166
|
-
|---|---|
|
|
167
|
-
| `--wings-fg`, `--wings-muted` | Text, and quieter text |
|
|
168
|
-
| `--wings-surface`, `--wings-hover`, `--wings-pressed`, `--wings-input` | Card, hovered, pressed and input backgrounds |
|
|
169
|
-
| `--wings-hairline`, `--wings-hairline-strong` | Borders |
|
|
170
|
-
| `--wings-ring` | The focus ring |
|
|
171
|
-
| `--wings-primary`, `--wings-primary-fg` | The primary button, and its text |
|
|
172
|
-
| `--wings-info`, `--wings-success`, `--wings-warning`, `--wings-danger` | Tones |
|
|
173
|
-
| `--wings-radius` | A card's corners |
|
|
174
|
-
| `--wings-menu-bg`, `--wings-menu-ring`, `--wings-menu-active`, `--wings-shadow` | A raised layer like a menu or a list of suggestions, as Wings' own menus draw it: its opaque background, its edge, the highlighted option and its shadow |
|
|
175
|
-
| `--wings-font`, `--wings-mono` | Fonts |
|
|
176
|
-
|
|
177
|
-
```tsx
|
|
178
|
-
// src/sidebar.tsx
|
|
179
|
-
import "./sidebar.css";
|
|
180
|
-
|
|
181
|
-
function Banner({ children }: { children: string }) {
|
|
182
|
-
return <div className="banner">{children}</div>;
|
|
183
|
-
}
|
|
184
|
-
```
|
|
185
|
-
|
|
186
|
-
```css
|
|
187
|
-
/* src/sidebar.css */
|
|
188
|
-
.banner {
|
|
189
|
-
padding: 8px 10px;
|
|
190
|
-
border-radius: var(--wings-radius);
|
|
191
|
-
background: color-mix(in srgb, var(--wings-info) 14%, transparent);
|
|
192
|
-
color: var(--wings-info);
|
|
193
|
-
}
|
|
194
|
-
```
|
|
195
|
-
|
|
196
|
-
Wings' base styles cover plain elements (buttons, inputs, selects and the page's font and colours), the `w-*`
|
|
197
|
-
classes and two older helpers, `.muted` and `.card`; nothing else, so your other classes are yours. Your CSS
|
|
198
|
-
loads after Wings', so it wins where you style a component, like `.my-list .w-list-button { height: 24px; }`.
|
|
199
|
-
|
|
200
|
-
### The class contract
|
|
201
|
-
|
|
202
|
-
The components render plain elements with `w-*` class names and data attributes, like
|
|
203
|
-
`<div class="w-card" data-size="compact">`, and no styles of their own. Wings styles them in its
|
|
204
|
-
`plugin-ui.css`, which ships with the app, so a Wings update restyles every plugin. The names are a versioned
|
|
205
|
-
public contract: `--wings-ui` on the page's root is its version, `2` now, and a version only adds classes and
|
|
206
|
-
rules. `Meter` sets `--w-value`, how full it is, and a `Dot` with a `color` sets `--w-color`; both are data
|
|
207
|
-
rather than styles.
|
|
208
|
-
|
|
209
|
-
`w-shell` with `data-place` (`sidebar`, `popover`, `view`, `tab`, `settings`) and `data-fill`; `w-row`,
|
|
210
|
-
`w-stack` with `data-gap`, `data-align`, `data-justify`, `data-wrap`, `data-grow`; `w-text` with `data-tone`,
|
|
211
|
-
`data-size`, `data-weight`, `data-mono`, `data-numeric`, `data-truncate`, `data-clamp`, `data-grow`;
|
|
212
|
-
`w-title` with `data-size`; `w-heading`, `w-heading-row`; `w-card` with `data-size`; `w-gauge`, `w-gauge-label`, `w-gauge-value`,
|
|
213
|
-
`w-gauge-note`, `w-meter` with `data-tone`, `w-meter-fill`; `w-stats`, `w-stat-label`, `w-stat-value`;
|
|
214
|
-
`w-props`, `w-prop`, `w-prop-label`, `w-prop-value`; `w-list` with `data-density`, `w-list-item`,
|
|
215
|
-
`w-list-button` with `data-align`; `w-dot` with `data-tone` or `data-color`; `w-pill`, `w-tag` with `data-tone`; `w-spinner` with `data-size`;
|
|
216
|
-
`w-loading`, `w-empty`, `w-empty-detail`; `w-button` with `data-variant`, `data-size`, `aria-disabled`; `w-chips`, `w-chip`;
|
|
217
|
-
`w-tabs`, `w-tab`; `w-field` with `data-layout`, `w-field-row`, `w-field-label`, `w-field-hint`,
|
|
218
|
-
`w-field-group`, `w-input`, `w-search`, `w-select`, `w-check`; `w-combobox`, `w-listbox`, `w-option`,
|
|
219
|
-
`w-option-label`, `w-option-detail`, `w-option-empty`; `w-menu-root`, `w-menu` with `data-align`, `w-menu-item`
|
|
220
|
-
with `data-tone`; `w-disclosure`. A `ul` or `ol` with `w-row`, `w-stack` or `w-card` loses its bullets.
|
|
221
|
-
|
|
222
|
-
Version 2 added `w-title`, `Heading`'s levels, `w-field-group`, `w-field-row`, the combobox and menu classes,
|
|
223
|
-
`data-color` on `w-dot`, busy buttons (`aria-disabled` on `w-button`), lists as rows, stacks and cards, and the four raised-layer variables.
|
|
224
|
-
|
|
225
|
-
### Older Wings
|
|
226
|
-
|
|
227
|
-
Wings before 0.1.15 has no `w-*` styles, and a Wings with `--wings-ui: 1` lacks version 2's. A page built with
|
|
228
|
-
`wings-plugin-sdk/ui` checks `--wings-ui` when it mounts, and on a Wings without it, or with an older version,
|
|
229
|
-
adds the package's own copy of the styles, before Wings' and your CSS. Since a version only adds rules, the old
|
|
230
|
-
ones Wings has and the new ones from the package fit together. So a plugin works on older Wings without a `minWingsVersion`, and on a current Wings it always gets
|
|
231
|
-
Wings' own styles.
|
|
232
|
-
|
|
233
|
-
### A linked SDK
|
|
234
|
-
|
|
235
|
-
With the SDK linked from a local folder (`npm link`, or `link:` in `package.json`) rather than installed,
|
|
236
|
-
TypeScript looks for React next to the SDK's folder. Point it at yours in `tsconfig.json`; the build already
|
|
237
|
-
does:
|
|
238
|
-
|
|
239
|
-
```json
|
|
240
|
-
"paths": {
|
|
241
|
-
"react": ["./node_modules/@types/react"],
|
|
242
|
-
"react/*": ["./node_modules/@types/react/*"]
|
|
243
|
-
}
|
|
244
|
-
```
|
package/build.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ export function entries(dir: string): { src: string; out: string }[];
|
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Builds the plugin in `dir`: `src/main.ts` to `main.js`, `src/sidebar.tsx` to `sidebar.js`, and so on. CSS a page
|
|
6
|
-
* imports becomes `<name>.css`, and a page that mounts with `wings-plugin-
|
|
6
|
+
* imports becomes `<name>.css`, and a page that mounts with `wings-plugin-ui` or `wings-plugin-sdk/shell` gets a generated
|
|
7
7
|
* `<name>.html` unless the plugin has its own. With `check`, writes nothing and lists in `stale` the outputs that
|
|
8
8
|
* differ from what's on disk. With `outDir`, writes there instead of `dir`.
|
|
9
9
|
*/
|
package/build.mjs
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// becomes main.js and src/sidebar.tsx becomes sidebar.js, next to wings-plugin.json. Folders inside src/ hold
|
|
3
3
|
// the modules they import. Wings runs plain scripts, so each file is one bundle with its imports, React too.
|
|
4
4
|
// The CSS a page imports, like `import "./sidebar.css"`, becomes <name>.css, and a page that mounts with
|
|
5
|
-
// wings-plugin-
|
|
5
|
+
// wings-plugin-ui or wings-plugin-sdk/shell gets a <name>.html written for it unless the plugin has its own.
|
|
6
6
|
import * as esbuild from "esbuild";
|
|
7
7
|
import { zipSync } from "fflate";
|
|
8
8
|
import { copyFileSync, existsSync, mkdirSync, mkdtempSync, readdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
@@ -54,12 +54,12 @@ ${hasCss ? ` <link rel="stylesheet" href="${name}.css" />\n` : ""} </head>
|
|
|
54
54
|
/** Whether `file` is missing or was written by this build, so the build may write it. */
|
|
55
55
|
const ours = (file) => !existsSync(file) || readFileSync(file, "utf8").includes(GENERATED);
|
|
56
56
|
|
|
57
|
-
/** Notes whether the bundle imports wings-plugin-
|
|
57
|
+
/** Notes whether the bundle imports wings-plugin-ui or wings-plugin-sdk/shell, which mount the page themselves. */
|
|
58
58
|
function shellPlugin(seen) {
|
|
59
59
|
return {
|
|
60
60
|
name: "wings-shell",
|
|
61
61
|
setup(build) {
|
|
62
|
-
build.onResolve({ filter: /^wings-plugin-sdk\/
|
|
62
|
+
build.onResolve({ filter: /^(wings-plugin-ui|wings-plugin-sdk\/shell)$/ }, () => {
|
|
63
63
|
seen.shell = true;
|
|
64
64
|
// Resolve as usual.
|
|
65
65
|
return undefined;
|
|
@@ -69,8 +69,8 @@ function shellPlugin(seen) {
|
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
/**
|
|
72
|
-
* Resolves React from the plugin's folder wherever it's imported, so a page and a linked wings-plugin-
|
|
73
|
-
* link, or link: in package.json) share one React instead of the
|
|
72
|
+
* Resolves React from the plugin's folder wherever it's imported, so a page and a linked wings-plugin-ui (npm
|
|
73
|
+
* link, or link: in package.json) share one React instead of the package finding its own, or none.
|
|
74
74
|
*/
|
|
75
75
|
function oneReact(dir) {
|
|
76
76
|
return {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wings-plugin-sdk",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.6.0",
|
|
4
|
+
"description": "The wings API types, the page frame, React hooks and the build command for Wings plugins.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"types": "./wings.d.ts",
|
|
@@ -13,10 +13,6 @@
|
|
|
13
13
|
"types": "./react.d.ts",
|
|
14
14
|
"default": "./react.js"
|
|
15
15
|
},
|
|
16
|
-
"./ui": {
|
|
17
|
-
"types": "./ui.d.ts",
|
|
18
|
-
"default": "./ui.js"
|
|
19
|
-
},
|
|
20
16
|
"./shell": {
|
|
21
17
|
"types": "./shell.d.ts",
|
|
22
18
|
"default": "./shell.js"
|
|
@@ -27,7 +23,7 @@
|
|
|
27
23
|
}
|
|
28
24
|
},
|
|
29
25
|
"scripts": {
|
|
30
|
-
"test": "
|
|
26
|
+
"test": "node --test"
|
|
31
27
|
},
|
|
32
28
|
"bin": {
|
|
33
29
|
"wings-plugin": "bin.mjs"
|
|
@@ -36,10 +32,6 @@
|
|
|
36
32
|
"wings.d.ts",
|
|
37
33
|
"react.js",
|
|
38
34
|
"react.d.ts",
|
|
39
|
-
"ui.js",
|
|
40
|
-
"ui.d.ts",
|
|
41
|
-
"ui-css.js",
|
|
42
|
-
"ui-css.d.ts",
|
|
43
35
|
"shell.js",
|
|
44
36
|
"shell.d.ts",
|
|
45
37
|
"build.mjs",
|
|
@@ -71,7 +63,6 @@
|
|
|
71
63
|
},
|
|
72
64
|
"devDependencies": {
|
|
73
65
|
"@types/react": "^19.1.8",
|
|
74
|
-
"jsdom": "^30.1.2",
|
|
75
66
|
"react": "^19.1.0",
|
|
76
67
|
"react-dom": "^19.1.0",
|
|
77
68
|
"typescript": "~6.0.3"
|
package/shell.d.ts
CHANGED
|
@@ -9,6 +9,6 @@ export interface ShellOptions {
|
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* Makes the page's `#app` element (or a new `<main>`) the shell for `place`, and returns it, for a page that
|
|
12
|
-
* builds its DOM itself. React pages use `showSidebar` and the others from `wings-plugin-
|
|
12
|
+
* builds its DOM itself. React pages use `showSidebar` and the others from `wings-plugin-ui` instead.
|
|
13
13
|
*/
|
|
14
14
|
export function mountShell(place: Place, options?: ShellOptions): HTMLElement;
|
package/shell.js
CHANGED
|
@@ -1,28 +1,15 @@
|
|
|
1
1
|
// The frame a plugin page lives in, without React: where it is, its padding and how it scrolls come from Wings'
|
|
2
|
-
// plugin-ui.css, so a page only says which place it is. wings-plugin-
|
|
3
|
-
import { fallbackCss, UI_VERSION } from "./ui-css.js";
|
|
4
|
-
|
|
2
|
+
// plugin-ui.css, so a page only says which place it is. wings-plugin-ui mounts React into it.
|
|
5
3
|
const PLACES = ["sidebar", "popover", "view", "tab", "settings"];
|
|
6
4
|
// Sidebars and popovers change while you look at them, so a screen reader hears what changed.
|
|
7
5
|
const LIVE = new Set(["sidebar", "popover"]);
|
|
8
6
|
|
|
9
|
-
/** Wings older than the classes this package renders have none, or older ones: bring them, before the page's own CSS. */
|
|
10
|
-
function ensureStyles() {
|
|
11
|
-
const host = Number(getComputedStyle(document.documentElement).getPropertyValue("--wings-ui")) || 0;
|
|
12
|
-
if (host >= UI_VERSION || document.querySelector("style[data-wings-ui]")) return;
|
|
13
|
-
const style = document.createElement("style");
|
|
14
|
-
style.dataset.wingsUi = String(UI_VERSION);
|
|
15
|
-
style.textContent = fallbackCss;
|
|
16
|
-
document.head.prepend(style);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
7
|
/**
|
|
20
8
|
* Makes the page's `#app` element (or a new `<main>`) the shell for `place`, and returns it. Calling it again
|
|
21
9
|
* returns the same element. `fill` lays the page out edge to edge, for a page that scrolls on its own.
|
|
22
10
|
*/
|
|
23
11
|
export function mountShell(place, { fill = false } = {}) {
|
|
24
12
|
if (!PLACES.includes(place)) throw new Error(`Unknown place ${place}: use one of ${PLACES.join(", ")}`);
|
|
25
|
-
ensureStyles();
|
|
26
13
|
let shell = document.getElementById("app");
|
|
27
14
|
if (!shell) {
|
|
28
15
|
shell = document.createElement("main");
|
package/ui-css.d.ts
DELETED
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
/** The version of the classes wings-plugin-sdk/ui renders. Wings' plugin-ui.css sets it as `--wings-ui`. */
|
|
2
|
-
export const UI_VERSION: number;
|
|
3
|
-
/** The wings-plugin-sdk/ui styles, for Wings versions whose plugin-ui.css doesn't have them yet. */
|
|
4
|
-
export const fallbackCss: string;
|