@viniaraujo68/plinth 0.0.0-stage → 0.2.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/LICENSE +21 -0
- package/README.md +361 -2
- package/dist/anchoring.d.ts +61 -0
- package/dist/anchoring.js +151 -0
- package/dist/async-action.svelte.d.ts +37 -0
- package/dist/async-action.svelte.js +64 -0
- package/dist/attachments/index.d.ts +1 -0
- package/dist/attachments/index.js +1 -0
- package/dist/attachments/tooltip.d.ts +37 -0
- package/dist/attachments/tooltip.js +166 -0
- package/dist/components/AsyncButton.svelte +49 -0
- package/dist/components/AsyncButton.svelte.d.ts +32 -0
- package/dist/components/Combobox.svelte +380 -0
- package/dist/components/Combobox.svelte.d.ts +74 -0
- package/dist/components/Copyable.svelte +116 -0
- package/dist/components/Copyable.svelte.d.ts +37 -0
- package/dist/components/DateRangePicker.svelte +266 -0
- package/dist/components/DateRangePicker.svelte.d.ts +66 -0
- package/dist/components/Dialog.svelte +102 -0
- package/dist/components/Dialog.svelte.d.ts +38 -0
- package/dist/components/Dropdown.svelte +204 -0
- package/dist/components/Dropdown.svelte.d.ts +49 -0
- package/dist/components/ErrorDisplay.svelte +59 -0
- package/dist/components/ErrorDisplay.svelte.d.ts +24 -0
- package/dist/components/ErrorPage.svelte +119 -0
- package/dist/components/ErrorPage.svelte.d.ts +52 -0
- package/dist/components/JsonInput.svelte +106 -0
- package/dist/components/JsonInput.svelte.d.ts +40 -0
- package/dist/components/Listbox.svelte +257 -0
- package/dist/components/Listbox.svelte.d.ts +78 -0
- package/dist/components/LoadingButton.svelte +36 -0
- package/dist/components/LoadingButton.svelte.d.ts +22 -0
- package/dist/components/Logo.svelte +112 -0
- package/dist/components/Logo.svelte.d.ts +29 -0
- package/dist/components/Modal.svelte +187 -0
- package/dist/components/Modal.svelte.d.ts +62 -0
- package/dist/components/MultiSelect.svelte +256 -0
- package/dist/components/MultiSelect.svelte.d.ts +89 -0
- package/dist/components/OptionPanel.svelte +241 -0
- package/dist/components/OptionPanel.svelte.d.ts +51 -0
- package/dist/components/Paginator.svelte +205 -0
- package/dist/components/Paginator.svelte.d.ts +48 -0
- package/dist/components/RefreshButton.svelte +246 -0
- package/dist/components/RefreshButton.svelte.d.ts +46 -0
- package/dist/components/SegmentedControl.svelte +147 -0
- package/dist/components/SegmentedControl.svelte.d.ts +71 -0
- package/dist/components/Select.svelte +312 -0
- package/dist/components/Select.svelte.d.ts +68 -0
- package/dist/components/Skeleton.svelte +97 -0
- package/dist/components/Skeleton.svelte.d.ts +32 -0
- package/dist/components/Tooltip.svelte +60 -0
- package/dist/components/Tooltip.svelte.d.ts +58 -0
- package/dist/components/date-range.d.ts +101 -0
- package/dist/components/date-range.js +144 -0
- package/dist/components/index.d.ts +25 -0
- package/dist/components/index.js +37 -0
- package/dist/components/json-text.d.ts +22 -0
- package/dist/components/json-text.js +22 -0
- package/dist/components/pick.d.ts +18 -0
- package/dist/components/pick.js +18 -0
- package/dist/components/segmented.d.ts +7 -0
- package/dist/components/segmented.js +1 -0
- package/dist/components/select.d.ts +66 -0
- package/dist/components/select.js +90 -0
- package/dist/confirm/Confirmer.svelte +194 -0
- package/dist/confirm/Confirmer.svelte.d.ts +45 -0
- package/dist/confirm/challenge.d.ts +16 -0
- package/dist/confirm/challenge.js +16 -0
- package/dist/confirm/confirm-manager.svelte.d.ts +91 -0
- package/dist/confirm/confirm-manager.svelte.js +149 -0
- package/dist/confirm/index.d.ts +3 -0
- package/dist/confirm/index.js +3 -0
- package/dist/error.d.ts +25 -0
- package/dist/error.js +39 -0
- package/dist/formatters.d.ts +95 -0
- package/dist/formatters.js +146 -0
- package/dist/http.d.ts +68 -0
- package/dist/http.js +138 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +14 -0
- package/dist/routing.svelte.d.ts +169 -0
- package/dist/routing.svelte.js +208 -0
- package/dist/shell/AppShell.svelte +874 -0
- package/dist/shell/AppShell.svelte.d.ts +93 -0
- package/dist/shell/Breadcrumbs.svelte +78 -0
- package/dist/shell/Breadcrumbs.svelte.d.ts +25 -0
- package/dist/shell/crumbs.d.ts +23 -0
- package/dist/shell/crumbs.js +1 -0
- package/dist/shell/index.d.ts +3 -0
- package/dist/shell/index.js +6 -0
- package/dist/table/ColumnMenu.svelte +249 -0
- package/dist/table/ColumnMenu.svelte.d.ts +47 -0
- package/dist/table/DataTable.svelte +643 -0
- package/dist/table/DataTable.svelte.d.ts +153 -0
- package/dist/table/columns.d.ts +55 -0
- package/dist/table/columns.js +153 -0
- package/dist/table/index.d.ts +4 -0
- package/dist/table/index.js +8 -0
- package/dist/table/sort.d.ts +48 -0
- package/dist/table/sort.js +139 -0
- package/dist/table/types.d.ts +93 -0
- package/dist/table/types.js +1 -0
- package/dist/theme/ThemeController.svelte +54 -0
- package/dist/theme/ThemeController.svelte.d.ts +27 -0
- package/dist/theme/ThemeToggle.svelte +119 -0
- package/dist/theme/ThemeToggle.svelte.d.ts +52 -0
- package/dist/theme/css-color.d.ts +23 -0
- package/dist/theme/css-color.js +31 -0
- package/dist/theme/index.d.ts +4 -0
- package/dist/theme/index.js +5 -0
- package/dist/theme/theme.css +410 -0
- package/dist/theme/theme.svelte.d.ts +54 -0
- package/dist/theme/theme.svelte.js +58 -0
- package/dist/toast/Toaster.svelte +257 -0
- package/dist/toast/Toaster.svelte.d.ts +35 -0
- package/dist/toast/index.d.ts +2 -0
- package/dist/toast/index.js +2 -0
- package/dist/toast/toast-manager.svelte.d.ts +99 -0
- package/dist/toast/toast-manager.svelte.js +176 -0
- package/dist/user/ScopedComponent.svelte +47 -0
- package/dist/user/ScopedComponent.svelte.d.ts +29 -0
- package/dist/user/anonymous.d.ts +21 -0
- package/dist/user/anonymous.js +26 -0
- package/dist/user/context.d.ts +55 -0
- package/dist/user/context.js +13 -0
- package/dist/user/index.d.ts +3 -0
- package/dist/user/index.js +6 -0
- package/package.json +133 -4
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import { resolve } from "$app/paths";
|
|
2
|
+
import { DEV } from "esm-env";
|
|
3
|
+
import { createContext } from "svelte";
|
|
4
|
+
/**
|
|
5
|
+
* Recover the route id SvelteKit would report for a glob key:
|
|
6
|
+
* `/src/routes/orders/[id]/+page.svelte` becomes `/orders/[id]`, and `/src/routes/+page.svelte`
|
|
7
|
+
* becomes `/`.
|
|
8
|
+
*
|
|
9
|
+
* Group segments survive the trip. `page.route.id` carries them — a route under `(app)` really
|
|
10
|
+
* is `/(app)/orders` — so stripping them here would leave the two halves of the config
|
|
11
|
+
* disagreeing about what a route id is. They are dropped later, when a pathname is built.
|
|
12
|
+
*/
|
|
13
|
+
const pageFileToRouteId = (file) => /\/routes(?<id>\/.*)?\/\+page\.svelte$/.exec(file)?.groups?.id ?? "/";
|
|
14
|
+
/** `"/"` yields no segments, `"/orders/[id]"` yields `["orders", "[id]"]`. */
|
|
15
|
+
const idSegments = (routeId) => routeId.split("/").filter(Boolean);
|
|
16
|
+
const isGroup = (segment) => segment.startsWith("(") && segment.endsWith(")");
|
|
17
|
+
const isParam = (segment) => segment.startsWith("[") && segment.endsWith("]");
|
|
18
|
+
/**
|
|
19
|
+
* The key a param segment uses in `params`, which is never the segment itself: SvelteKit spells
|
|
20
|
+
* the same `id` as `[id]`, `[id=integer]`, `[...id]` or `[[id]]` depending on how the route
|
|
21
|
+
* captures it.
|
|
22
|
+
*/
|
|
23
|
+
const paramName = (segment) => segment
|
|
24
|
+
.replace(/^\[+/, "")
|
|
25
|
+
.replace(/\]+$/, "")
|
|
26
|
+
.replace(/^\.\.\./, "")
|
|
27
|
+
.split("=")[0];
|
|
28
|
+
/** A segment SvelteKit is willing to match against nothing at all, hence one that may vanish. */
|
|
29
|
+
const isOptionalParam = (segment) => segment.startsWith("[[") || segment.startsWith("[...");
|
|
30
|
+
/**
|
|
31
|
+
* Turn a route id into a pathname against the current params.
|
|
32
|
+
*
|
|
33
|
+
* A required param with no value leaves an empty segment behind rather than disappearing. The
|
|
34
|
+
* resulting `//` is ugly, and that is the point — it only happens when a caller asks for the
|
|
35
|
+
* pathname of a dynamic route nobody is currently on, which is a bug worth seeing.
|
|
36
|
+
*/
|
|
37
|
+
const routeIdToPathname = (routeId, params) => {
|
|
38
|
+
const segments = [];
|
|
39
|
+
for (const segment of idSegments(routeId)) {
|
|
40
|
+
if (isGroup(segment))
|
|
41
|
+
continue;
|
|
42
|
+
if (!isParam(segment)) {
|
|
43
|
+
segments.push(segment);
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
const value = params[paramName(segment)] ?? "";
|
|
47
|
+
if (value === "" && isOptionalParam(segment))
|
|
48
|
+
continue;
|
|
49
|
+
segments.push(value);
|
|
50
|
+
}
|
|
51
|
+
return segments.length === 0 ? "/" : `/${segments.join("/")}`;
|
|
52
|
+
};
|
|
53
|
+
const resolveTitle = (meta, params) => {
|
|
54
|
+
if (meta.title === undefined)
|
|
55
|
+
return "";
|
|
56
|
+
return typeof meta.title === "function" ? meta.title({ params }) : meta.title;
|
|
57
|
+
};
|
|
58
|
+
/**
|
|
59
|
+
* Reactive view over SvelteKit's own match, projected through a flat metadata map.
|
|
60
|
+
*
|
|
61
|
+
* It resolves nothing by itself — no URL parsing, no pattern matching, no ranking. SvelteKit
|
|
62
|
+
* already decided which route the user is on; this class only answers "what does the app want
|
|
63
|
+
* displayed and gated for that decision, and for its ancestors".
|
|
64
|
+
*/
|
|
65
|
+
export class RoutingContext {
|
|
66
|
+
config;
|
|
67
|
+
/** Every id with a real page, computed once — the filesystem cannot change while the tab is
|
|
68
|
+
* open, so this is the one part of the class that has no reason to be reactive. */
|
|
69
|
+
navigableIds;
|
|
70
|
+
/** The current URL, straight off the source. Nav chrome reads it for things the route id does
|
|
71
|
+
* not carry, such as the query string or the host. */
|
|
72
|
+
url;
|
|
73
|
+
/** The matched route id, or `null` when nothing matched — a 404, or the first paint of an
|
|
74
|
+
* error page. */
|
|
75
|
+
routeId;
|
|
76
|
+
/** Decoded params of the current match. */
|
|
77
|
+
params;
|
|
78
|
+
/**
|
|
79
|
+
* The declared prefixes of the current match, root first.
|
|
80
|
+
*
|
|
81
|
+
* Every ancestor that has a meta entry is here, plumbing included, so a gate declared halfway
|
|
82
|
+
* up the tree stays reachable (`matched.findLast((r) => r.meta.requiredRoles)`). Prefixes with
|
|
83
|
+
* no entry contribute nothing and are skipped rather than faked. Empty when `routeId` is null.
|
|
84
|
+
*/
|
|
85
|
+
matched;
|
|
86
|
+
constructor(config, source) {
|
|
87
|
+
this.config = config;
|
|
88
|
+
// A plain Set: nothing ever writes to it, so a reactive one would buy a proxy and no
|
|
89
|
+
// behaviour.
|
|
90
|
+
// eslint-disable-next-line svelte/prefer-svelte-reactivity
|
|
91
|
+
this.navigableIds = new Set(Object.keys(config.pages).map(pageFileToRouteId));
|
|
92
|
+
this.url = $derived(source.url);
|
|
93
|
+
this.routeId = $derived(source.route.id);
|
|
94
|
+
this.params = $derived(source.params);
|
|
95
|
+
this.matched = $derived.by(() => {
|
|
96
|
+
const id = this.routeId;
|
|
97
|
+
if (id === null)
|
|
98
|
+
return [];
|
|
99
|
+
const params = this.params;
|
|
100
|
+
const segments = idSegments(id);
|
|
101
|
+
const prefixes = ["/", ...segments.map((_, i) => `/${segments.slice(0, i + 1).join("/")}`)];
|
|
102
|
+
const trail = [];
|
|
103
|
+
for (const prefix of prefixes) {
|
|
104
|
+
const meta = this.metaFor(prefix);
|
|
105
|
+
if (meta !== undefined)
|
|
106
|
+
trail.push(this.resolve(prefix, meta, params));
|
|
107
|
+
}
|
|
108
|
+
return trail;
|
|
109
|
+
});
|
|
110
|
+
if (DEV)
|
|
111
|
+
this.reportDrift();
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* The direct declared children of a route id, in the order the meta map declares them.
|
|
115
|
+
*
|
|
116
|
+
* Plumbing children come back too — a caller may want to recurse past them or read their
|
|
117
|
+
* gates — so a renderer that only wants visible entries filters on `meta.title`. Labels and
|
|
118
|
+
* pathnames resolve against the current params, which only shows up when a child is itself a
|
|
119
|
+
* capture and the user is standing on it.
|
|
120
|
+
*/
|
|
121
|
+
children(routeId) {
|
|
122
|
+
const prefix = routeId === "/" ? "/" : `${routeId}/`;
|
|
123
|
+
const params = this.params;
|
|
124
|
+
const found = [];
|
|
125
|
+
for (const [id, meta] of Object.entries(this.config.meta)) {
|
|
126
|
+
if (meta === undefined || id === routeId)
|
|
127
|
+
continue;
|
|
128
|
+
if (!id.startsWith(prefix))
|
|
129
|
+
continue;
|
|
130
|
+
// A remaining slash means a grandchild, which belongs to a deeper call.
|
|
131
|
+
if (id.slice(prefix.length).includes("/"))
|
|
132
|
+
continue;
|
|
133
|
+
found.push(this.resolve(id, meta, params));
|
|
134
|
+
}
|
|
135
|
+
return found;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* The title of any declared route id, `""` when it is plumbing or unknown.
|
|
139
|
+
*
|
|
140
|
+
* A dynamic title is evaluated against the params of the *current* match, so asking about a
|
|
141
|
+
* capture route the user is not on hands it empty strings. Reading the label off `matched` or
|
|
142
|
+
* `children` is the normal path; this is for the odd one-off lookup.
|
|
143
|
+
*/
|
|
144
|
+
label(routeId) {
|
|
145
|
+
const meta = this.config.meta[routeId];
|
|
146
|
+
return meta === undefined ? "" : resolveTitle(meta, this.params);
|
|
147
|
+
}
|
|
148
|
+
/** The single place a runtime string is narrowed to the app's route-id union. Every lookup in
|
|
149
|
+
* this class goes through it so the cast exists once instead of at four call sites. */
|
|
150
|
+
metaFor(routeId) {
|
|
151
|
+
return this.config.meta[routeId];
|
|
152
|
+
}
|
|
153
|
+
resolve(routeId, meta, params) {
|
|
154
|
+
return {
|
|
155
|
+
routeId,
|
|
156
|
+
label: resolveTitle(meta, params),
|
|
157
|
+
pathname: routeIdToPathname(routeId, params),
|
|
158
|
+
navigable: this.navigableIds.has(routeId),
|
|
159
|
+
meta,
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Warn once, in dev, about the two ways the map and the filesystem drift apart.
|
|
164
|
+
*
|
|
165
|
+
* Typed keys already catch the common case at compile time. What they cannot catch is a page
|
|
166
|
+
* added without a meta entry, or a plumbing entry left behind after the last route under it
|
|
167
|
+
* was deleted — neither is a type error, and both are invisible until a crumb or a nav item
|
|
168
|
+
* quietly stops appearing.
|
|
169
|
+
*/
|
|
170
|
+
reportDrift() {
|
|
171
|
+
const declared = Object.keys(this.config.meta);
|
|
172
|
+
const undeclared = [...this.navigableIds].filter((id) => !declared.includes(id));
|
|
173
|
+
if (undeclared.length > 0)
|
|
174
|
+
console.warn(`[plinth/routing] pages with no meta entry: ${undeclared.sort().join(", ")}`);
|
|
175
|
+
const orphaned = declared.filter((id) => {
|
|
176
|
+
const prefix = id === "/" ? "/" : `${id}/`;
|
|
177
|
+
return ![...this.navigableIds].some((page) => page === id || page.startsWith(prefix));
|
|
178
|
+
});
|
|
179
|
+
if (orphaned.length > 0)
|
|
180
|
+
console.warn(`[plinth/routing] meta entries with no page beneath them: ${orphaned.sort().join(", ")}`);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Turn a pathname computed at runtime into an href that respects the configured base path.
|
|
185
|
+
*
|
|
186
|
+
* `$app/paths`' `resolve` is typed as one overload per literal route in the app, so no value
|
|
187
|
+
* produced at runtime can ever satisfy it. This is where that gets acknowledged, once, instead
|
|
188
|
+
* of a cast at every nav item and crumb.
|
|
189
|
+
*/
|
|
190
|
+
export const resolvePathname = (pathname) => resolve(pathname);
|
|
191
|
+
export const [getRoutingContext, setRoutingContext] = createContext();
|
|
192
|
+
/**
|
|
193
|
+
* A copy of `url` with some search parameters set and others removed — `null` or `undefined`
|
|
194
|
+
* removes. It never edits the URL it was given, which is usually the page store's own and is read
|
|
195
|
+
* by everything else on the page.
|
|
196
|
+
*/
|
|
197
|
+
export const withSearchParams = (url, updates) => {
|
|
198
|
+
// A value handed to `goto` or an `href`, never read reactively: a `SvelteURL` would only add
|
|
199
|
+
// signals nobody subscribes to.
|
|
200
|
+
// eslint-disable-next-line svelte/prefer-svelte-reactivity
|
|
201
|
+
const next = new URL(url);
|
|
202
|
+
for (const [key, value] of Object.entries(updates))
|
|
203
|
+
if (value === null || value === undefined)
|
|
204
|
+
next.searchParams.delete(key);
|
|
205
|
+
else
|
|
206
|
+
next.searchParams.set(key, value);
|
|
207
|
+
return next;
|
|
208
|
+
};
|