@nano133/nanoterra 0.3.1 → 0.4.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/CHANGELOG.md +25 -0
- package/README.md +23 -6
- package/dist/admin/panel/dom.d.ts +11 -0
- package/dist/admin/panel/dom.js +33 -0
- package/dist/admin/panel/gameCard.d.ts +5 -2
- package/dist/admin/panel/gameCard.js +9 -5
- package/dist/admin/panel/icons.d.ts +121 -0
- package/dist/admin/panel/icons.js +41 -0
- package/dist/admin/panel/index.d.ts +11 -2
- package/dist/admin/panel/index.js +6 -1
- package/dist/admin/panel/mount.js +101 -67
- package/dist/admin/panel/orders.d.ts +23 -0
- package/dist/admin/panel/orders.js +130 -0
- package/dist/admin/panel/overview.d.ts +22 -0
- package/dist/admin/panel/overview.js +75 -0
- package/dist/admin/panel/players.d.ts +7 -0
- package/dist/admin/panel/players.js +61 -0
- package/dist/admin/panel/shelf.d.ts +11 -2
- package/dist/admin/panel/shelf.js +322 -76
- package/dist/admin/panel/tabs.d.ts +23 -0
- package/dist/admin/panel/tabs.js +68 -0
- package/dist/admin/switches.d.ts +1 -1
- package/dist/admin/switches.js +4 -4
- package/package.json +1 -1
- package/src/admin/panel/dom.ts +34 -0
- package/src/admin/panel/gameCard.ts +9 -5
- package/src/admin/panel/icons.ts +46 -0
- package/src/admin/panel/index.ts +11 -2
- package/src/admin/panel/mount.ts +120 -101
- package/src/admin/panel/orders.ts +172 -0
- package/src/admin/panel/overview.ts +134 -0
- package/src/admin/panel/players.ts +79 -0
- package/src/admin/panel/shelf.ts +467 -109
- package/src/admin/panel/style.css +1775 -270
- package/src/admin/panel/tabs.ts +94 -0
- package/src/admin/switches.ts +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,30 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.4.0 (2026-10-30)
|
|
4
|
+
|
|
5
|
+
- **The admin panel (`@nano133/nanoterra/admin/panel`) has a new layout: the game at a glance, in tabs.** A top bar (who is signed in, Live or Test mode, the bot
|
|
6
|
+
check, Sign out), the warnings as one banner, four numbers (Earned, Open now, To send back, Players; each opens its list), then five tabs: Overview (best sellers
|
|
7
|
+
with their share of the money, the last five orders, the switches as On or Off, the shelf in numbers, first steps for a game with no order), Cosmetics (the
|
|
8
|
+
Shelf), Orders (a filter with counts, a search, a table on a wide panel, 20 at a time), Players (with a search) and Settings (the Game card, the sign-ins, the
|
|
9
|
+
facts). Wes asked for less scrolling and reading, clear sections, and a much better cosmetics screen (2026-10-30).
|
|
10
|
+
- **The Shelf**: a grid of cards with a big picture on a tint of the look's colour, the price with its ladder step, the share of the sales, slot chips; filters and a
|
|
11
|
+
sort. Its editor opens one card at a time, with grouped fields (the ladder's seven steps as keys, colour swatches, the hats as pictures), "New", "Changed",
|
|
12
|
+
"Owned" and problem marks on each card, a short change bar at the bottom of the screen, and "Put back" for a removed look. The editor's rules do not change: the
|
|
13
|
+
Shelf still writes nothing, and an owned look's id and slots stay.
|
|
14
|
+
- New exports: `shelfFilter`, `shelfSort`, `BODY_COLOURS`, `ordersView`, `orderMatches`, `overviewView`, `bestSellers`, `playersView`, `playerMatches`, `tabBar`,
|
|
15
|
+
`TABS`, `icon`, and their types. `gameCard` takes an optional fourth argument, `onChange(state)`. `mountAdmin`'s options and the handler's routes do not change.
|
|
16
|
+
- New CSS custom properties: `--n133-ground`, `--n133-warn`, `--n133-bad`, `--n133-shadow`. The stylesheet uses a container query on the panel (`container: n133`).
|
|
17
|
+
Words that changed: the totals are "Earned", "Open now" and "To send back"; "Owned looks" is "Players who own a look"; "Copy a message for my AI" is "Copy for
|
|
18
|
+
my AI" (its tooltip says the rest). A sign-out now takes the drawn data off the page.
|
|
19
|
+
|
|
20
|
+
## 0.3.2 (2026-10-29)
|
|
21
|
+
|
|
22
|
+
- **The Game card's character switch says what On means when a game offers a choice.** Echo Island lets a player choose between the Nanoterra character
|
|
23
|
+
and the game's own character (On), or plays only its own (Off). The card's words now hold for that game and for one with no choice (the starter): the title
|
|
24
|
+
is "Players can play as their Nanoterra character"; On adds "The game may also let a player switch to its own character."; Off says "with no choice";
|
|
25
|
+
`CHARACTER_SIGNIN_OFF` says a player "starts as" the island's first character and adds the same sentence. The switch's values (`"nanoterra"`, `"own"`),
|
|
26
|
+
`CHARACTER_NEEDS_SIGNIN` and the API do not change.
|
|
27
|
+
|
|
3
28
|
## 0.3.1 (2026-10-29)
|
|
4
29
|
|
|
5
30
|
- **`@nano133/nanoterra/input`: a character that does not walk on by itself.** A key that is down when the player leaves the page (another tab, another window, a pop-up,
|
package/README.md
CHANGED
|
@@ -66,7 +66,7 @@ Each path loads only what it needs. A page that reads a ticket loads no Three.js
|
|
|
66
66
|
| `@nano133/nanoterra/input` | let go of held keys and sticks when the player leaves the page, and pause: `releaseWhenLost`, `pauseWhenLeft`, `swallowsKeyup` (since 0.3.1) | no | 0.5 kB |
|
|
67
67
|
| `@nano133/nanoterra/admin` | the admin of a game: the guard, the handler, the store type (server code; since 0.2.0) | no | 3.7 kB |
|
|
68
68
|
| `@nano133/nanoterra/admin/d1`, `/admin/firestore` | the admin store for D1 (the starter's tables) or Firestore (a small interface, no Firebase dependency): `createD1Store`, `createFirestoreStore` (since 0.2.0) | no | 1.5 kB each |
|
|
69
|
-
| `@nano133/nanoterra/admin/panel` | the admin page, drawn in the browser into one element, and its stylesheet `@nano133/nanoterra/admin/style.css` (since 0.2.0) | no | 10.6 kB on 0.3.0
|
|
69
|
+
| `@nano133/nanoterra/admin/panel` | the admin page, drawn in the browser into one element, and its stylesheet `@nano133/nanoterra/admin/style.css` (since 0.2.0) | no | 18.9 kB with the tabbed layout (10.6 kB on 0.3.0, 8.5 kB on 0.2.0) + 5.9 kB of CSS (minified) |
|
|
70
70
|
| `@nano133/nanoterra/admin/conformance` | test cases that a game runs against its own admin (a dev dependency in practice; since 0.2.0) | no | n/a |
|
|
71
71
|
|
|
72
72
|
Measured 2026-10-26: `esbuild --bundle --minify --format=esm --external:three`, then `gzip -9`, without Three.js.
|
|
@@ -585,10 +585,24 @@ mountAdmin(document.getElementById("admin")!, {
|
|
|
585
585
|
});
|
|
586
586
|
```
|
|
587
587
|
|
|
588
|
-
The panel draws
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
588
|
+
The panel draws, from the top: a bar with who is signed in (the short address and a copy key), Live or Test mode, the bot check and Sign out; what is still off (the
|
|
589
|
+
status warnings, one banner); four numbers (Earned, Open now, To send back, Players: a press opens the list behind each); then five tabs. **Overview**: the best-selling
|
|
590
|
+
looks with their share of the money, the last five orders, the two switches as On or Off and the shelf in numbers (a game with no order yet gets three first steps).
|
|
591
|
+
**Cosmetics**: the **Shelf** (below). **Orders**: a filter by status with its count, a search (a look, an address or a block), "Check payments now" when the game gave a
|
|
592
|
+
check, "Paid late" with the address to send the money back to, 20 orders at a time; a card each on a phone, a table on a wide panel. **Players**: each player who owns a
|
|
593
|
+
look, with their looks. **Settings**: the **Game** card (the two switches), the Sign-ins card and the facts (the address, the mode, the nodes, the last price). The tabs are
|
|
594
|
+
real tabs (the arrow keys, Home and End work); every tab is made once, so a draft, a search and a switch keep their state, and the panel never writes the page's address.
|
|
595
|
+
A visitor who is signed out gets the game's sign-in; a wrong wallet, a Nanoterra sign-in and a missing owner address each get their own screen and see no data (a sign-out
|
|
596
|
+
takes the drawn data off the page). Options: `api` (the handler's base path), `noAddressHelp`, `lookPicture(look)` (your own picture of a look for the Shelf), `repoHelp`,
|
|
597
|
+
`loginsNote` and `fetch`.
|
|
598
|
+
|
|
599
|
+
**The Shelf** shows the looks your game sells now in a grid of cards: a big picture on a soft tint of the look's colour, the price with its step on the ladder ("25¢ ·
|
|
600
|
+
statement"), what it has sold and its share of the sales, its slots as chips; filters (All, For sale, Free, Not sold yet) and a sort (Best selling, price, name, the
|
|
601
|
+
file's order). **Edit the shelf** opens the draft: one card at a time opens with its fields in groups (the name; the price, with the ladder's seven steps as keys; the
|
|
602
|
+
look: body colour swatches and any colour, the hat as pictures, the hat colour; the slots), and each card says "New", "Changed", "Owned" or how many problems it has.
|
|
603
|
+
A bar at the bottom of the screen holds the state of the draft and the two copy keys ("Copy looks.json", "Copy for my AI"); under the cards are the full list of
|
|
604
|
+
problems, GitHub, Start over, what changed and the new file. A removed look can be put back. The pure helpers are exported for a game's own page: `shelfFilter`,
|
|
605
|
+
`shelfSort`, `bestSellers`, `orderMatches`, `playerMatches`.
|
|
592
606
|
|
|
593
607
|
**The Shelf's pictures on the real character** (since 0.3.0). The panel has no Three.js, so it takes the pictures from you, lazily:
|
|
594
608
|
|
|
@@ -612,7 +626,10 @@ flat picture stays. `createLookPreview({ size?, body?, record?, background?, key
|
|
|
612
626
|
**The Shelf never writes.** It edits a draft in the owner's browser and gives text to copy (`looks.json`, or a message for an AI assistant): there is no call in the panel that
|
|
613
627
|
changes a look, a price or an order, and a test proves that editing the Shelf sends no request. A change of looks is a change of your repository. The **Game** card writes only the
|
|
614
628
|
two settings. The stylesheet is scoped under `.n133-admin`, so it cannot leak into your page; to theme it set `--n133-ink`, `--n133-ink-soft`, `--n133-paper`, `--n133-line`,
|
|
615
|
-
`--n133-gem`, `--n133-gem-dark`, `--n133-sea`, `--n133-radius
|
|
629
|
+
`--n133-gem`, `--n133-gem-dark`, `--n133-sea`, `--n133-radius`, `--n133-font`, `--n133-ground` (the wells inside a card), `--n133-warn`, `--n133-bad` or `--n133-shadow` on the
|
|
630
|
+
element that holds it. The keys, inputs, switches, swatches and price steps are 44 px or more; the tabs, filter keys and copy keys 40 px. The layout follows the panel's own
|
|
631
|
+
width (a container query), not the window's: on a phone two cards a row and the five tabs as icons over their words; three cards a row and the orders as a table from
|
|
632
|
+
760 px; four cards from 1000 px. The panel can be up to 1200 px wide: the page that holds it sets the width (the starter's admin page is 900 px).
|
|
616
633
|
|
|
617
634
|
The wallet sign-in stays yours (`signIn`), because only your game can start a request in the ADMIN scope with its own cookie. The panel never signs anyone in.
|
|
618
635
|
|
|
@@ -4,4 +4,15 @@ export declare function h<K extends keyof HTMLElementTagNameMap>(tag: K, attrs?:
|
|
|
4
4
|
/** "nano_1abc…wxyz" */
|
|
5
5
|
export declare const shortAddress: (a: string) => string;
|
|
6
6
|
export declare function copy(text: string, button: HTMLButtonElement): Promise<void>;
|
|
7
|
+
/**
|
|
8
|
+
* A small copy key: an icon and the word "Copy". A press puts `value` on the clipboard and says "Copied" for a moment; only the word changes, so the icon
|
|
9
|
+
* stays. `label` names what is copied, for a screen reader and the tooltip ("Copy the player's address").
|
|
10
|
+
*/
|
|
11
|
+
export declare function copyKey(value: string, label: string, icon?: Element): HTMLButtonElement;
|
|
12
|
+
/** "just now", "5 min ago", "3 h ago", "2 days ago". A time in the future reads "just now". */
|
|
13
|
+
export declare function ago(ms: number, now?: number): string;
|
|
14
|
+
/** "3 orders", "1 order" */
|
|
15
|
+
export declare const plural: (n: number, word: string) => string;
|
|
16
|
+
/** "$1.25" from cents. */
|
|
17
|
+
export declare const dollars: (cents: number) => string;
|
|
7
18
|
export {};
|
package/dist/admin/panel/dom.js
CHANGED
|
@@ -31,3 +31,36 @@ export async function copy(text, button) {
|
|
|
31
31
|
}
|
|
32
32
|
setTimeout(() => (button.textContent = was), 1500);
|
|
33
33
|
}
|
|
34
|
+
/**
|
|
35
|
+
* A small copy key: an icon and the word "Copy". A press puts `value` on the clipboard and says "Copied" for a moment; only the word changes, so the icon
|
|
36
|
+
* stays. `label` names what is copied, for a screen reader and the tooltip ("Copy the player's address").
|
|
37
|
+
*/
|
|
38
|
+
export function copyKey(value, label, icon) {
|
|
39
|
+
const word = h("span", { class: "copy-word" }, "Copy");
|
|
40
|
+
const button = h("button", { class: "btn btn-small btn-quiet copy-key", type: "button", "aria-label": label, title: label }, icon ?? null, word);
|
|
41
|
+
button.addEventListener("click", async () => {
|
|
42
|
+
try {
|
|
43
|
+
await navigator.clipboard.writeText(value);
|
|
44
|
+
word.textContent = "Copied";
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
word.textContent = "Copy failed";
|
|
48
|
+
}
|
|
49
|
+
setTimeout(() => (word.textContent = "Copy"), 1500);
|
|
50
|
+
});
|
|
51
|
+
return button;
|
|
52
|
+
}
|
|
53
|
+
/** "just now", "5 min ago", "3 h ago", "2 days ago". A time in the future reads "just now". */
|
|
54
|
+
export function ago(ms, now = Date.now()) {
|
|
55
|
+
const m = Math.max(0, Math.round((now - ms) / 60_000));
|
|
56
|
+
if (m < 1)
|
|
57
|
+
return "just now";
|
|
58
|
+
if (m < 60)
|
|
59
|
+
return `${m} min ago`;
|
|
60
|
+
const hours = Math.round(m / 60);
|
|
61
|
+
return hours < 48 ? `${hours} h ago` : `${Math.round(hours / 24)} days ago`;
|
|
62
|
+
}
|
|
63
|
+
/** "3 orders", "1 order" */
|
|
64
|
+
export const plural = (n, word) => `${n} ${word}${n === 1 ? "" : "s"}`;
|
|
65
|
+
/** "$1.25" from cents. */
|
|
66
|
+
export const dollars = (cents) => `$${(cents / 100).toFixed(2)}`;
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import { type GameState } from "../switches.js";
|
|
2
2
|
import { type Api } from "./api.js";
|
|
3
|
-
/**
|
|
4
|
-
|
|
3
|
+
/**
|
|
4
|
+
* The card for the settings `initial`. It keeps its own state: a press shows "Saving…", then "Saved." or the server's words. `defaultsFile` is where the game's defaults are
|
|
5
|
+
* written ("game.config.json"). `onChange` hears each saved state (the panel's Overview shows the two switches too).
|
|
6
|
+
*/
|
|
7
|
+
export declare function gameCard(api: Api, initial: GameState, defaultsFile?: string, onChange?: (state: GameState) => void): HTMLElement;
|
|
@@ -1,17 +1,20 @@
|
|
|
1
|
-
// The "Game" card of /admin: two switches, "Players
|
|
1
|
+
// The "Game" card of /admin: two switches, "Players can play as their Nanoterra character" and "Players can sign in with Nanoterra". A press saves at once
|
|
2
2
|
// (POST {api}/game, behind the same admin guard as every admin call) and the game reads the setting at run time, so the creator pushes and deploys
|
|
3
3
|
// nothing. The words are in ../switches.ts. The card changes ONLY these two settings: never a look, a price or an order.
|
|
4
4
|
import { CHARACTER_NEEDS_SIGNIN, CHARACTER_SIGNIN_OFF, REVIEW_LINK, REVIEW_NOTE, SWITCHES, pressOf, signinOn, stateOf } from "../switches.js";
|
|
5
5
|
import { ApiError } from "./api.js";
|
|
6
6
|
import { h } from "./dom.js";
|
|
7
|
-
/**
|
|
8
|
-
|
|
7
|
+
/**
|
|
8
|
+
* The card for the settings `initial`. It keeps its own state: a press shows "Saving…", then "Saved." or the server's words. `defaultsFile` is where the game's defaults are
|
|
9
|
+
* written ("game.config.json"). `onChange` hears each saved state (the panel's Overview shows the two switches too).
|
|
10
|
+
*/
|
|
11
|
+
export function gameCard(api, initial, defaultsFile = "the game's own settings", onChange) {
|
|
9
12
|
let state = initial;
|
|
10
13
|
const rows = new Map();
|
|
11
14
|
/** Under the character switch: it works only through sign-in, so it says so, and says what a player gets while sign-in is Off. */
|
|
12
15
|
const needs = h("p", { class: "fine switch-needs" });
|
|
13
|
-
const card = h("section", { class: "admin-card game-card", "aria-labelledby": "game-title" }, h("h2", { id: "game-title" }, "Game"));
|
|
14
|
-
card.append(h("p", { class: "fine" }, `Two choices about your game. A press saves at once: you push nothing and deploy nothing. They start from ${defaultsFile}, and a press here wins.`));
|
|
16
|
+
const card = h("section", { class: "admin-card game-card", "aria-labelledby": "game-title" }, h("div", { class: "card-head" }, h("h2", { id: "game-title" }, "Game")));
|
|
17
|
+
card.append(h("p", { class: "fine card-intro" }, `Two choices about your game. A press saves at once: you push nothing and deploy nothing. They start from ${defaultsFile}, and a press here wins.`));
|
|
15
18
|
const draw = () => {
|
|
16
19
|
for (const [id, row] of rows) {
|
|
17
20
|
const on = stateOf(id, state);
|
|
@@ -31,6 +34,7 @@ export function gameCard(api, initial, defaultsFile = "the game's own settings")
|
|
|
31
34
|
state = await api.post("/game", pressOf(id, state));
|
|
32
35
|
row.status.textContent = "Saved.";
|
|
33
36
|
draw();
|
|
37
|
+
onChange?.(state);
|
|
34
38
|
}
|
|
35
39
|
catch (e) {
|
|
36
40
|
row.status.textContent = e instanceof ApiError ? e.message : "The game's server did not answer. Nothing was changed.";
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
declare const SHAPES: {
|
|
2
|
+
overview: [string, {
|
|
3
|
+
x: number;
|
|
4
|
+
y: number;
|
|
5
|
+
width: number;
|
|
6
|
+
height: number;
|
|
7
|
+
rx: number;
|
|
8
|
+
}][];
|
|
9
|
+
shirt: [string, {
|
|
10
|
+
d: string;
|
|
11
|
+
}][];
|
|
12
|
+
receipt: [string, {
|
|
13
|
+
d: string;
|
|
14
|
+
}][];
|
|
15
|
+
players: ([string, {
|
|
16
|
+
cx: number;
|
|
17
|
+
cy: number;
|
|
18
|
+
r: number;
|
|
19
|
+
}] | [string, {
|
|
20
|
+
d: string;
|
|
21
|
+
}])[];
|
|
22
|
+
settings: ([string, {
|
|
23
|
+
cx: number;
|
|
24
|
+
cy: number;
|
|
25
|
+
r: number;
|
|
26
|
+
}] | [string, {
|
|
27
|
+
d: string;
|
|
28
|
+
}])[];
|
|
29
|
+
coin: ([string, {
|
|
30
|
+
cx: number;
|
|
31
|
+
cy: number;
|
|
32
|
+
r: number;
|
|
33
|
+
}] | [string, {
|
|
34
|
+
d: string;
|
|
35
|
+
}])[];
|
|
36
|
+
clock: ([string, {
|
|
37
|
+
cx: number;
|
|
38
|
+
cy: number;
|
|
39
|
+
r: number;
|
|
40
|
+
}] | [string, {
|
|
41
|
+
d: string;
|
|
42
|
+
}])[];
|
|
43
|
+
back: [string, {
|
|
44
|
+
d: string;
|
|
45
|
+
}][];
|
|
46
|
+
tag: ([string, {
|
|
47
|
+
d: string;
|
|
48
|
+
}] | [string, {
|
|
49
|
+
cx: number;
|
|
50
|
+
cy: number;
|
|
51
|
+
r: number;
|
|
52
|
+
}])[];
|
|
53
|
+
copy: ([string, {
|
|
54
|
+
x: number;
|
|
55
|
+
y: number;
|
|
56
|
+
width: number;
|
|
57
|
+
height: number;
|
|
58
|
+
rx: number;
|
|
59
|
+
}] | [string, {
|
|
60
|
+
d: string;
|
|
61
|
+
}])[];
|
|
62
|
+
lock: ([string, {
|
|
63
|
+
x: number;
|
|
64
|
+
y: number;
|
|
65
|
+
width: number;
|
|
66
|
+
height: number;
|
|
67
|
+
rx: number;
|
|
68
|
+
}] | [string, {
|
|
69
|
+
d: string;
|
|
70
|
+
}])[];
|
|
71
|
+
plus: [string, {
|
|
72
|
+
d: string;
|
|
73
|
+
}][];
|
|
74
|
+
search: ([string, {
|
|
75
|
+
cx: number;
|
|
76
|
+
cy: number;
|
|
77
|
+
r: number;
|
|
78
|
+
}] | [string, {
|
|
79
|
+
d: string;
|
|
80
|
+
}])[];
|
|
81
|
+
alert: [string, {
|
|
82
|
+
d: string;
|
|
83
|
+
}][];
|
|
84
|
+
check: [string, {
|
|
85
|
+
d: string;
|
|
86
|
+
}][];
|
|
87
|
+
external: [string, {
|
|
88
|
+
d: string;
|
|
89
|
+
}][];
|
|
90
|
+
refresh: [string, {
|
|
91
|
+
d: string;
|
|
92
|
+
}][];
|
|
93
|
+
out: [string, {
|
|
94
|
+
d: string;
|
|
95
|
+
}][];
|
|
96
|
+
pencil: [string, {
|
|
97
|
+
d: string;
|
|
98
|
+
}][];
|
|
99
|
+
close: [string, {
|
|
100
|
+
d: string;
|
|
101
|
+
}][];
|
|
102
|
+
dup: ([string, {
|
|
103
|
+
x: number;
|
|
104
|
+
y: number;
|
|
105
|
+
width: number;
|
|
106
|
+
height: number;
|
|
107
|
+
rx: number;
|
|
108
|
+
}] | [string, {
|
|
109
|
+
d: string;
|
|
110
|
+
}])[];
|
|
111
|
+
trash: [string, {
|
|
112
|
+
d: string;
|
|
113
|
+
}][];
|
|
114
|
+
sparkle: [string, {
|
|
115
|
+
d: string;
|
|
116
|
+
}][];
|
|
117
|
+
};
|
|
118
|
+
export type IconName = keyof typeof SHAPES;
|
|
119
|
+
/** An icon of `size` CSS pixels (default 20). */
|
|
120
|
+
export declare function icon(name: IconName, size?: number): SVGSVGElement;
|
|
121
|
+
export {};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// The panel's small line icons, drawn as inline SVG with createElementNS (no icon font, no innerHTML: fine under a strict CSP). Each is 24 units square, drawn
|
|
2
|
+
// with the text colour (currentColor), and hidden from screen readers: the word next to it says what it is.
|
|
3
|
+
const NS = "http://www.w3.org/2000/svg";
|
|
4
|
+
const SHAPES = {
|
|
5
|
+
overview: [["rect", { x: 3, y: 3, width: 7.5, height: 7.5, rx: 2 }], ["rect", { x: 13.5, y: 3, width: 7.5, height: 7.5, rx: 2 }], ["rect", { x: 3, y: 13.5, width: 7.5, height: 7.5, rx: 2 }], ["rect", { x: 13.5, y: 13.5, width: 7.5, height: 7.5, rx: 2 }]],
|
|
6
|
+
shirt: [["path", { d: "M8.5 3.5 4 6 2.5 10.5l3.5 1.5V21h12v-9l3.5-1.5L20 6l-4.5-2.5a3.5 3.5 0 0 1-7 0Z" }]],
|
|
7
|
+
receipt: [["path", { d: "M5 3h14v18l-2.5-1.5L14 21l-2-1.5L10 21l-2.5-1.5L5 21Z" }], ["path", { d: "M9 8h6M9 12h6M9 16h3" }]],
|
|
8
|
+
players: [["circle", { cx: 9, cy: 8, r: 3.5 }], ["path", { d: "M2.5 20a6.5 6.5 0 0 1 13 0" }], ["path", { d: "M16 4.8a3.5 3.5 0 0 1 0 6.4M18 14.2a6.5 6.5 0 0 1 3.5 5.8" }]],
|
|
9
|
+
settings: [["circle", { cx: 12, cy: 12, r: 3 }], ["path", { d: "M12 2.5v3M12 18.5v3M2.5 12h3M18.5 12h3M5.3 5.3l2.1 2.1M16.6 16.6l2.1 2.1M5.3 18.7l2.1-2.1M16.6 7.4l2.1-2.1" }]],
|
|
10
|
+
coin: [["circle", { cx: 12, cy: 12, r: 9 }], ["path", { d: "M12 6.5v11M14.8 9.2a2.6 2.6 0 0 0-2.6-1.7h-.6a2.2 2.2 0 0 0 0 4.4h.8a2.2 2.2 0 0 1 0 4.4h-.7a2.6 2.6 0 0 1-2.6-1.7" }]],
|
|
11
|
+
clock: [["circle", { cx: 12, cy: 12, r: 9 }], ["path", { d: "M12 7v5l3.5 2" }]],
|
|
12
|
+
back: [["path", { d: "M9 6 4 11l5 5" }], ["path", { d: "M4 11h10.5a5.5 5.5 0 0 1 0 11H11" }]],
|
|
13
|
+
tag: [["path", { d: "M3 12.2V4a1 1 0 0 1 1-1h8.2l8.6 8.6a1.5 1.5 0 0 1 0 2.1l-6.9 6.9a1.5 1.5 0 0 1-2.1 0Z" }], ["circle", { cx: 7.5, cy: 7.5, r: 1.5 }]],
|
|
14
|
+
copy: [["rect", { x: 8, y: 8, width: 13, height: 13, rx: 2.5 }], ["path", { d: "M16 8V5.5A2.5 2.5 0 0 0 13.5 3h-8A2.5 2.5 0 0 0 3 5.5v8A2.5 2.5 0 0 0 5.5 16H8" }]],
|
|
15
|
+
lock: [["rect", { x: 4.5, y: 10.5, width: 15, height: 10.5, rx: 2.5 }], ["path", { d: "M8 10.5V7.5a4 4 0 0 1 8 0v3" }]],
|
|
16
|
+
plus: [["path", { d: "M12 5v14M5 12h14" }]],
|
|
17
|
+
search: [["circle", { cx: 10.5, cy: 10.5, r: 6.5 }], ["path", { d: "m15.5 15.5 5 5" }]],
|
|
18
|
+
alert: [["path", { d: "M10.3 3.9 2.4 17.6A2 2 0 0 0 4.1 20.6h15.8a2 2 0 0 0 1.7-3L13.7 3.9a2 2 0 0 0-3.4 0Z" }], ["path", { d: "M12 9v4.5M12 17h.01" }]],
|
|
19
|
+
check: [["path", { d: "m4.5 12.5 5 5 10-11" }]],
|
|
20
|
+
external: [["path", { d: "M14 4h6v6M20 4l-9 9" }], ["path", { d: "M18 14v4.5a1.5 1.5 0 0 1-1.5 1.5h-11A1.5 1.5 0 0 1 4 18.5v-11A1.5 1.5 0 0 1 5.5 6H10" }]],
|
|
21
|
+
refresh: [["path", { d: "M20 11a8 8 0 0 0-14.3-4.9L4 8M4 4v4h4" }], ["path", { d: "M4 13a8 8 0 0 0 14.3 4.9L20 16M20 20v-4h-4" }]],
|
|
22
|
+
out: [["path", { d: "M15 4h3.5A1.5 1.5 0 0 1 20 5.5v13a1.5 1.5 0 0 1-1.5 1.5H15" }], ["path", { d: "M10 8l-4 4 4 4M6 12h10" }]],
|
|
23
|
+
pencil: [["path", { d: "M4 20h4L19 9a2.8 2.8 0 0 0-4-4L4 16Z" }], ["path", { d: "m13.5 6.5 4 4" }]],
|
|
24
|
+
close: [["path", { d: "M6 6l12 12M18 6 6 18" }]],
|
|
25
|
+
dup: [["rect", { x: 8, y: 8, width: 13, height: 13, rx: 2.5 }], ["path", { d: "M14.5 11.5v6M11.5 14.5h6M16 8V5.5A2.5 2.5 0 0 0 13.5 3h-8A2.5 2.5 0 0 0 3 5.5v8A2.5 2.5 0 0 0 5.5 16H8" }]],
|
|
26
|
+
trash: [["path", { d: "M4 7h16M9.5 7V4.5h5V7M6.5 7l1 13h9l1-13" }]],
|
|
27
|
+
sparkle: [["path", { d: "M12 3v4M12 17v4M3 12h4M17 12h4M6 6l2.5 2.5M15.5 15.5 18 18M6 18l2.5-2.5M15.5 8.5 18 6" }]],
|
|
28
|
+
};
|
|
29
|
+
/** An icon of `size` CSS pixels (default 20). */
|
|
30
|
+
export function icon(name, size = 20) {
|
|
31
|
+
const svg = document.createElementNS(NS, "svg");
|
|
32
|
+
for (const [k, v] of Object.entries({ viewBox: "0 0 24 24", width: size, height: size, fill: "none", stroke: "currentColor", "stroke-width": 1.8, "stroke-linecap": "round", "stroke-linejoin": "round", "aria-hidden": "true", focusable: "false", class: `icon icon-${name}` }))
|
|
33
|
+
svg.setAttribute(k, String(v));
|
|
34
|
+
for (const [tag, attrs] of SHAPES[name]) {
|
|
35
|
+
const el = document.createElementNS(NS, tag);
|
|
36
|
+
for (const [k, v] of Object.entries(attrs))
|
|
37
|
+
el.setAttribute(k, String(v));
|
|
38
|
+
svg.append(el);
|
|
39
|
+
}
|
|
40
|
+
return svg;
|
|
41
|
+
}
|
|
@@ -3,8 +3,17 @@ export type { MountedAdmin, MountOptions } from "./mount.js";
|
|
|
3
3
|
export { ApiError, createApi } from "./api.js";
|
|
4
4
|
export type { Api } from "./api.js";
|
|
5
5
|
export { gameCard } from "./gameCard.js";
|
|
6
|
-
export { lookPicture, previewPicture, shelfView } from "./shelf.js";
|
|
6
|
+
export { BODY_COLOURS, lookPicture, previewPicture, shelfFilter, shelfSort, shelfView } from "./shelf.js";
|
|
7
7
|
export { DEFAULT_LOGINS_NOTE, signinsCard } from "./signins.js";
|
|
8
|
-
export type { ShelfData } from "./shelf.js";
|
|
8
|
+
export type { ShelfData, ShelfFilter, ShelfSort } from "./shelf.js";
|
|
9
|
+
export { orderMatches, ordersView } from "./orders.js";
|
|
10
|
+
export type { OrderFilter, OrdersView } from "./orders.js";
|
|
11
|
+
export { bestSellers, overviewView } from "./overview.js";
|
|
12
|
+
export type { BestSeller, OverviewView } from "./overview.js";
|
|
13
|
+
export { playerMatches, playersView } from "./players.js";
|
|
14
|
+
export { TABS, tabBar } from "./tabs.js";
|
|
15
|
+
export type { TabBar, TabId } from "./tabs.js";
|
|
16
|
+
export { icon } from "./icons.js";
|
|
17
|
+
export type { IconName } from "./icons.js";
|
|
9
18
|
export { aiPrompt, centsOf, changeText, ownedIds, shelfStats, soldText } from "./shelfData.js";
|
|
10
19
|
export type { LookStats, ShelfOrder, ShelfPlayer } from "./shelfData.js";
|
|
@@ -8,6 +8,11 @@
|
|
|
8
8
|
export { mountAdmin } from "./mount.js";
|
|
9
9
|
export { ApiError, createApi } from "./api.js";
|
|
10
10
|
export { gameCard } from "./gameCard.js";
|
|
11
|
-
export { lookPicture, previewPicture, shelfView } from "./shelf.js";
|
|
11
|
+
export { BODY_COLOURS, lookPicture, previewPicture, shelfFilter, shelfSort, shelfView } from "./shelf.js";
|
|
12
12
|
export { DEFAULT_LOGINS_NOTE, signinsCard } from "./signins.js";
|
|
13
|
+
export { orderMatches, ordersView } from "./orders.js";
|
|
14
|
+
export { bestSellers, overviewView } from "./overview.js";
|
|
15
|
+
export { playerMatches, playersView } from "./players.js";
|
|
16
|
+
export { TABS, tabBar } from "./tabs.js";
|
|
17
|
+
export { icon } from "./icons.js";
|
|
13
18
|
export { aiPrompt, centsOf, changeText, ownedIds, shelfStats, soldText } from "./shelfData.js";
|