@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.
Files changed (128) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +361 -2
  3. package/dist/anchoring.d.ts +61 -0
  4. package/dist/anchoring.js +151 -0
  5. package/dist/async-action.svelte.d.ts +37 -0
  6. package/dist/async-action.svelte.js +64 -0
  7. package/dist/attachments/index.d.ts +1 -0
  8. package/dist/attachments/index.js +1 -0
  9. package/dist/attachments/tooltip.d.ts +37 -0
  10. package/dist/attachments/tooltip.js +166 -0
  11. package/dist/components/AsyncButton.svelte +49 -0
  12. package/dist/components/AsyncButton.svelte.d.ts +32 -0
  13. package/dist/components/Combobox.svelte +380 -0
  14. package/dist/components/Combobox.svelte.d.ts +74 -0
  15. package/dist/components/Copyable.svelte +116 -0
  16. package/dist/components/Copyable.svelte.d.ts +37 -0
  17. package/dist/components/DateRangePicker.svelte +266 -0
  18. package/dist/components/DateRangePicker.svelte.d.ts +66 -0
  19. package/dist/components/Dialog.svelte +102 -0
  20. package/dist/components/Dialog.svelte.d.ts +38 -0
  21. package/dist/components/Dropdown.svelte +204 -0
  22. package/dist/components/Dropdown.svelte.d.ts +49 -0
  23. package/dist/components/ErrorDisplay.svelte +59 -0
  24. package/dist/components/ErrorDisplay.svelte.d.ts +24 -0
  25. package/dist/components/ErrorPage.svelte +119 -0
  26. package/dist/components/ErrorPage.svelte.d.ts +52 -0
  27. package/dist/components/JsonInput.svelte +106 -0
  28. package/dist/components/JsonInput.svelte.d.ts +40 -0
  29. package/dist/components/Listbox.svelte +257 -0
  30. package/dist/components/Listbox.svelte.d.ts +78 -0
  31. package/dist/components/LoadingButton.svelte +36 -0
  32. package/dist/components/LoadingButton.svelte.d.ts +22 -0
  33. package/dist/components/Logo.svelte +112 -0
  34. package/dist/components/Logo.svelte.d.ts +29 -0
  35. package/dist/components/Modal.svelte +187 -0
  36. package/dist/components/Modal.svelte.d.ts +62 -0
  37. package/dist/components/MultiSelect.svelte +256 -0
  38. package/dist/components/MultiSelect.svelte.d.ts +89 -0
  39. package/dist/components/OptionPanel.svelte +241 -0
  40. package/dist/components/OptionPanel.svelte.d.ts +51 -0
  41. package/dist/components/Paginator.svelte +205 -0
  42. package/dist/components/Paginator.svelte.d.ts +48 -0
  43. package/dist/components/RefreshButton.svelte +246 -0
  44. package/dist/components/RefreshButton.svelte.d.ts +46 -0
  45. package/dist/components/SegmentedControl.svelte +147 -0
  46. package/dist/components/SegmentedControl.svelte.d.ts +71 -0
  47. package/dist/components/Select.svelte +312 -0
  48. package/dist/components/Select.svelte.d.ts +68 -0
  49. package/dist/components/Skeleton.svelte +97 -0
  50. package/dist/components/Skeleton.svelte.d.ts +32 -0
  51. package/dist/components/Tooltip.svelte +60 -0
  52. package/dist/components/Tooltip.svelte.d.ts +58 -0
  53. package/dist/components/date-range.d.ts +101 -0
  54. package/dist/components/date-range.js +144 -0
  55. package/dist/components/index.d.ts +25 -0
  56. package/dist/components/index.js +37 -0
  57. package/dist/components/json-text.d.ts +22 -0
  58. package/dist/components/json-text.js +22 -0
  59. package/dist/components/pick.d.ts +18 -0
  60. package/dist/components/pick.js +18 -0
  61. package/dist/components/segmented.d.ts +7 -0
  62. package/dist/components/segmented.js +1 -0
  63. package/dist/components/select.d.ts +66 -0
  64. package/dist/components/select.js +90 -0
  65. package/dist/confirm/Confirmer.svelte +194 -0
  66. package/dist/confirm/Confirmer.svelte.d.ts +45 -0
  67. package/dist/confirm/challenge.d.ts +16 -0
  68. package/dist/confirm/challenge.js +16 -0
  69. package/dist/confirm/confirm-manager.svelte.d.ts +91 -0
  70. package/dist/confirm/confirm-manager.svelte.js +149 -0
  71. package/dist/confirm/index.d.ts +3 -0
  72. package/dist/confirm/index.js +3 -0
  73. package/dist/error.d.ts +25 -0
  74. package/dist/error.js +39 -0
  75. package/dist/formatters.d.ts +95 -0
  76. package/dist/formatters.js +146 -0
  77. package/dist/http.d.ts +68 -0
  78. package/dist/http.js +138 -0
  79. package/dist/index.d.ts +10 -0
  80. package/dist/index.js +14 -0
  81. package/dist/routing.svelte.d.ts +169 -0
  82. package/dist/routing.svelte.js +208 -0
  83. package/dist/shell/AppShell.svelte +874 -0
  84. package/dist/shell/AppShell.svelte.d.ts +93 -0
  85. package/dist/shell/Breadcrumbs.svelte +78 -0
  86. package/dist/shell/Breadcrumbs.svelte.d.ts +25 -0
  87. package/dist/shell/crumbs.d.ts +23 -0
  88. package/dist/shell/crumbs.js +1 -0
  89. package/dist/shell/index.d.ts +3 -0
  90. package/dist/shell/index.js +6 -0
  91. package/dist/table/ColumnMenu.svelte +249 -0
  92. package/dist/table/ColumnMenu.svelte.d.ts +47 -0
  93. package/dist/table/DataTable.svelte +643 -0
  94. package/dist/table/DataTable.svelte.d.ts +153 -0
  95. package/dist/table/columns.d.ts +55 -0
  96. package/dist/table/columns.js +153 -0
  97. package/dist/table/index.d.ts +4 -0
  98. package/dist/table/index.js +8 -0
  99. package/dist/table/sort.d.ts +48 -0
  100. package/dist/table/sort.js +139 -0
  101. package/dist/table/types.d.ts +93 -0
  102. package/dist/table/types.js +1 -0
  103. package/dist/theme/ThemeController.svelte +54 -0
  104. package/dist/theme/ThemeController.svelte.d.ts +27 -0
  105. package/dist/theme/ThemeToggle.svelte +119 -0
  106. package/dist/theme/ThemeToggle.svelte.d.ts +52 -0
  107. package/dist/theme/css-color.d.ts +23 -0
  108. package/dist/theme/css-color.js +31 -0
  109. package/dist/theme/index.d.ts +4 -0
  110. package/dist/theme/index.js +5 -0
  111. package/dist/theme/theme.css +410 -0
  112. package/dist/theme/theme.svelte.d.ts +54 -0
  113. package/dist/theme/theme.svelte.js +58 -0
  114. package/dist/toast/Toaster.svelte +257 -0
  115. package/dist/toast/Toaster.svelte.d.ts +35 -0
  116. package/dist/toast/index.d.ts +2 -0
  117. package/dist/toast/index.js +2 -0
  118. package/dist/toast/toast-manager.svelte.d.ts +99 -0
  119. package/dist/toast/toast-manager.svelte.js +176 -0
  120. package/dist/user/ScopedComponent.svelte +47 -0
  121. package/dist/user/ScopedComponent.svelte.d.ts +29 -0
  122. package/dist/user/anonymous.d.ts +21 -0
  123. package/dist/user/anonymous.js +26 -0
  124. package/dist/user/context.d.ts +55 -0
  125. package/dist/user/context.js +13 -0
  126. package/dist/user/index.d.ts +3 -0
  127. package/dist/user/index.js +6 -0
  128. 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
+ };