@spacesync/client 0.1.0 → 0.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/README.md +9 -14
- package/dist/analog-clock-picker.d.ts +2 -2
- package/dist/analog-clock-picker.js +12 -406
- package/dist/analog-clock-picker.js.map +1 -1
- package/dist/api.js +7 -87
- package/dist/api.js.map +1 -1
- package/dist/app-modal.d.ts +7 -8
- package/dist/app-modal.js +3 -74
- package/dist/app-modal.js.map +1 -1
- package/dist/app-shell-mobile-nav.d.ts +1 -2
- package/dist/app-shell-mobile-nav.js +2 -66
- package/dist/app-shell-mobile-nav.js.map +1 -1
- package/dist/app-web-shell.d.ts +2 -2
- package/dist/app-web-shell.js +1 -4
- package/dist/app-web-shell.js.map +1 -1
- package/dist/booking-schedule-calendars.d.ts +12 -12
- package/dist/booking-schedule-calendars.js +13 -402
- package/dist/booking-schedule-calendars.js.map +1 -1
- package/dist/chat-stick-to-bottom.d.ts +1 -2
- package/dist/chat-stick-to-bottom.js +12 -48
- package/dist/chat-stick-to-bottom.js.map +1 -1
- package/dist/chunk-4A6RCBB5.js +4925 -0
- package/dist/chunk-4A6RCBB5.js.map +1 -0
- package/dist/chunk-4NNK5IXY.js +79 -0
- package/dist/chunk-4NNK5IXY.js.map +1 -0
- package/dist/chunk-4Y5DBM6Z.js +6 -0
- package/dist/chunk-4Y5DBM6Z.js.map +1 -0
- package/dist/chunk-7YCAAP43.js +107 -0
- package/dist/chunk-7YCAAP43.js.map +1 -0
- package/dist/chunk-DMPSQ22Q.js +11 -0
- package/dist/chunk-DMPSQ22Q.js.map +1 -0
- package/dist/chunk-FCY5FYJB.js +104 -0
- package/dist/chunk-FCY5FYJB.js.map +1 -0
- package/dist/chunk-H7EX6B6N.js +580 -0
- package/dist/chunk-H7EX6B6N.js.map +1 -0
- package/dist/chunk-HCHASUB6.js +13 -0
- package/dist/chunk-HCHASUB6.js.map +1 -0
- package/dist/chunk-L65F4EMK.js +34 -0
- package/dist/chunk-L65F4EMK.js.map +1 -0
- package/dist/chunk-LUB7QWGU.js +301 -0
- package/dist/chunk-LUB7QWGU.js.map +1 -0
- package/dist/chunk-OMSJWTSZ.js +120 -0
- package/dist/chunk-OMSJWTSZ.js.map +1 -0
- package/dist/chunk-OWNUMPUC.js +9 -0
- package/dist/chunk-OWNUMPUC.js.map +1 -0
- package/dist/chunk-P65TMY3D.js +87 -0
- package/dist/chunk-P65TMY3D.js.map +1 -0
- package/dist/chunk-PBNBLCC2.js +84 -0
- package/dist/chunk-PBNBLCC2.js.map +1 -0
- package/dist/chunk-RV5PMZYZ.js +63 -0
- package/dist/chunk-RV5PMZYZ.js.map +1 -0
- package/dist/chunk-VHINHHZQ.js +48 -0
- package/dist/chunk-VHINHHZQ.js.map +1 -0
- package/dist/chunk-VVXGQKN7.js +107 -0
- package/dist/chunk-VVXGQKN7.js.map +1 -0
- package/dist/chunk-XU3EFSQX.js +22 -0
- package/dist/chunk-XU3EFSQX.js.map +1 -0
- package/dist/chunk-YAVEMXXV.js +381 -0
- package/dist/chunk-YAVEMXXV.js.map +1 -0
- package/dist/chunk-ZIJQ7BXU.js +27 -0
- package/dist/chunk-ZIJQ7BXU.js.map +1 -0
- package/dist/detail-page-header.d.ts +3 -3
- package/dist/detail-page-header.js +5 -149
- package/dist/detail-page-header.js.map +1 -1
- package/dist/{dialog-CIdvylPK.d.ts → dialog-CPkbi5N3.d.ts} +10 -11
- package/dist/file-picker-field.d.ts +2 -2
- package/dist/file-picker-field.js +3 -163
- package/dist/file-picker-field.js.map +1 -1
- package/dist/file-viewer-dialog.d.ts +2 -2
- package/dist/file-viewer-dialog.js +12 -223
- package/dist/file-viewer-dialog.js.map +1 -1
- package/dist/floor-switcher.d.ts +2 -2
- package/dist/floor-switcher.js +13 -323
- package/dist/floor-switcher.js.map +1 -1
- package/dist/forms.js +1 -23
- package/dist/forms.js.map +1 -1
- package/dist/image-picker-field.d.ts +2 -2
- package/dist/image-picker-field.js +3 -48
- package/dist/image-picker-field.js.map +1 -1
- package/dist/layout-canvas-view.d.ts +2 -2
- package/dist/layout-canvas-view.js +17 -220
- package/dist/layout-canvas-view.js.map +1 -1
- package/dist/lib/dateDisplay.js +1 -82
- package/dist/lib/dateDisplay.js.map +1 -1
- package/dist/live-resource-meta.js +1 -25
- package/dist/live-resource-meta.js.map +1 -1
- package/dist/live-space-pulse-dot.d.ts +2 -2
- package/dist/live-space-pulse-dot.js +2 -16
- package/dist/live-space-pulse-dot.js.map +1 -1
- package/dist/notification-sidebar-preview.d.ts +3 -3
- package/dist/notification-sidebar-preview.js +3 -61
- package/dist/notification-sidebar-preview.js.map +1 -1
- package/dist/page-list-chrome.d.ts +25 -25
- package/dist/page-list-chrome.js +6 -968
- package/dist/page-list-chrome.js.map +1 -1
- package/dist/platform-support-message-composer.d.ts +4 -4
- package/dist/platform-support-message-composer.js +15 -218
- package/dist/platform-support-message-composer.js.map +1 -1
- package/dist/shell-sidebar-nav.js +8 -13
- package/dist/shell-sidebar-nav.js.map +1 -1
- package/dist/shell-theme.js +1 -7
- package/dist/shell-theme.js.map +1 -1
- package/dist/shell-top-bar-chrome.js +3 -103
- package/dist/shell-top-bar-chrome.js.map +1 -1
- package/dist/shell-top-bar.d.ts +3 -3
- package/dist/shell-top-bar.js +1 -69
- package/dist/shell-top-bar.js.map +1 -1
- package/dist/support-presence-status.d.ts +2 -2
- package/dist/support-presence-status.js +3 -41
- package/dist/support-presence-status.js.map +1 -1
- package/dist/theme-appearance-control.d.ts +2 -2
- package/dist/theme-appearance-control.js +4 -63
- package/dist/theme-appearance-control.js.map +1 -1
- package/dist/theme-boot-script.js +1 -2
- package/dist/theme-boot-script.js.map +1 -1
- package/dist/theme-provider.d.ts +2 -2
- package/dist/theme-provider.js +2 -23
- package/dist/theme-provider.js.map +1 -1
- package/dist/ui.d.ts +261 -262
- package/dist/ui.js +11 -5635
- package/dist/ui.js.map +1 -1
- package/package.json +11 -2
package/dist/api.js
CHANGED
|
@@ -1,86 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
// src/api/envelope.ts
|
|
5
|
-
var paginationMetaSchema = z.object({
|
|
6
|
-
page: z.number().optional(),
|
|
7
|
-
limit: z.number().optional(),
|
|
8
|
-
total: z.number().optional(),
|
|
9
|
-
totalPages: z.number().optional()
|
|
10
|
-
}).passthrough();
|
|
11
|
-
var apiErrorBodySchema = z.object({
|
|
12
|
-
success: z.literal(false),
|
|
13
|
-
data: z.null(),
|
|
14
|
-
error: z.object({
|
|
15
|
-
message: z.string(),
|
|
16
|
-
details: z.unknown().optional()
|
|
17
|
-
}),
|
|
18
|
-
meta: paginationMetaSchema.optional()
|
|
19
|
-
});
|
|
20
|
-
function apiSuccessBodySchema(dataSchema) {
|
|
21
|
-
return z.object({
|
|
22
|
-
success: z.literal(true),
|
|
23
|
-
data: dataSchema,
|
|
24
|
-
error: z.null(),
|
|
25
|
-
meta: paginationMetaSchema.optional()
|
|
26
|
-
});
|
|
27
|
-
}
|
|
28
|
-
var ApiClientError = class _ApiClientError extends Error {
|
|
29
|
-
kind;
|
|
30
|
-
statusCode;
|
|
31
|
-
details;
|
|
32
|
-
fieldErrors;
|
|
33
|
-
constructor(kind, message, options = {}) {
|
|
34
|
-
super(message);
|
|
35
|
-
this.name = "ApiClientError";
|
|
36
|
-
this.kind = kind;
|
|
37
|
-
this.statusCode = options.statusCode;
|
|
38
|
-
this.details = options.details;
|
|
39
|
-
this.fieldErrors = options.fieldErrors;
|
|
40
|
-
Object.setPrototypeOf(this, _ApiClientError.prototype);
|
|
41
|
-
}
|
|
42
|
-
};
|
|
43
|
-
function isApiClientError(err) {
|
|
44
|
-
return err instanceof ApiClientError;
|
|
45
|
-
}
|
|
46
|
-
function isZodIssueArray(details) {
|
|
47
|
-
return Array.isArray(details) && details.length > 0 && details.every(
|
|
48
|
-
(issue) => typeof issue === "object" && issue !== null && Array.isArray(issue.path) && typeof issue.message === "string"
|
|
49
|
-
);
|
|
50
|
-
}
|
|
51
|
-
function toFieldErrors(issues) {
|
|
52
|
-
return issues.map((issue) => ({
|
|
53
|
-
path: issue.path.join("."),
|
|
54
|
-
message: issue.message
|
|
55
|
-
}));
|
|
56
|
-
}
|
|
57
|
-
function normalizeAxiosError(err) {
|
|
58
|
-
if (isApiClientError(err)) return err;
|
|
59
|
-
if (axios2.isAxiosError(err)) {
|
|
60
|
-
if (!err.response) {
|
|
61
|
-
return new ApiClientError("network", "Unable to reach the server. Check your connection and try again.");
|
|
62
|
-
}
|
|
63
|
-
const status = err.response.status;
|
|
64
|
-
const parsedBody = apiErrorBodySchema.safeParse(err.response.data);
|
|
65
|
-
const message2 = parsedBody.success ? parsedBody.data.error.message : `Request failed with status ${status}`;
|
|
66
|
-
const details = parsedBody.success ? parsedBody.data.error.details : void 0;
|
|
67
|
-
if (status === 400 && isZodIssueArray(details)) {
|
|
68
|
-
return new ApiClientError("validation", message2, {
|
|
69
|
-
statusCode: status,
|
|
70
|
-
details,
|
|
71
|
-
fieldErrors: toFieldErrors(details)
|
|
72
|
-
});
|
|
73
|
-
}
|
|
74
|
-
return new ApiClientError("http", message2, { statusCode: status, details });
|
|
75
|
-
}
|
|
76
|
-
if (err instanceof z.ZodError) {
|
|
77
|
-
return new ApiClientError("http", "Unexpected response shape from the server.", {
|
|
78
|
-
details: err.issues
|
|
79
|
-
});
|
|
80
|
-
}
|
|
81
|
-
const message = err instanceof Error ? err.message : "Something went wrong. Please try again.";
|
|
82
|
-
return new ApiClientError("http", message);
|
|
83
|
-
}
|
|
1
|
+
import { apiSuccessBodySchema, normalizeAxiosError } from './chunk-P65TMY3D.js';
|
|
2
|
+
export { ApiClientError, apiErrorBodySchema, apiSuccessBodySchema, isApiClientError, normalizeAxiosError, paginationMetaSchema } from './chunk-P65TMY3D.js';
|
|
3
|
+
import axios from 'axios';
|
|
84
4
|
|
|
85
5
|
// src/api/tokenStorage.ts
|
|
86
6
|
function createLocalStorageTokenStorage(namespace) {
|
|
@@ -105,12 +25,12 @@ function createLocalStorageTokenStorage(namespace) {
|
|
|
105
25
|
}
|
|
106
26
|
function createApiClient(config) {
|
|
107
27
|
const { baseURL, tokenStorage, refreshPath, onAuthExpired } = config;
|
|
108
|
-
const client =
|
|
28
|
+
const client = axios.create({ baseURL });
|
|
109
29
|
let refreshPromise = null;
|
|
110
30
|
async function refreshTokens() {
|
|
111
31
|
const refreshToken = tokenStorage.getRefreshToken();
|
|
112
32
|
if (!refreshToken) throw new Error("No refresh token available");
|
|
113
|
-
const response = await
|
|
33
|
+
const response = await axios.post(`${baseURL}${refreshPath}`, { refreshToken });
|
|
114
34
|
const tokens = response.data.data;
|
|
115
35
|
tokenStorage.setTokens(tokens);
|
|
116
36
|
return tokens;
|
|
@@ -126,7 +46,7 @@ function createApiClient(config) {
|
|
|
126
46
|
client.interceptors.response.use(
|
|
127
47
|
(response) => response,
|
|
128
48
|
async (error) => {
|
|
129
|
-
if (!
|
|
49
|
+
if (!axios.isAxiosError(error) || error.response?.status !== 401) {
|
|
130
50
|
throw error;
|
|
131
51
|
}
|
|
132
52
|
const originalRequest = error.config;
|
|
@@ -162,6 +82,6 @@ async function apiRequest(client, requestConfig, dataSchema) {
|
|
|
162
82
|
}
|
|
163
83
|
}
|
|
164
84
|
|
|
165
|
-
export {
|
|
85
|
+
export { apiRequest, createApiClient, createLocalStorageTokenStorage };
|
|
166
86
|
//# sourceMappingURL=api.js.map
|
|
167
87
|
//# sourceMappingURL=api.js.map
|
package/dist/api.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/api/envelope.ts","../src/api/errors.ts","../src/api/tokenStorage.ts","../src/api/createApiClient.ts","../src/api/request.ts"],"names":["axios","message","z"],"mappings":"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","file":"api.js","sourcesContent":["import { z } from \"zod\";\n\n/**\n * Mirrors `apps/api/src/lib/ApiResponse.ts` exactly. Every route on the\n * backend responds with this envelope (the only exceptions are documented\n * per-service, e.g. `POST /auth/logout` returns 204 with no body).\n */\nexport interface ApiResponseMeta {\n page?: number;\n limit?: number;\n total?: number;\n totalPages?: number;\n [key: string]: unknown;\n}\n\nexport const paginationMetaSchema = z\n .object({\n page: z.number().optional(),\n limit: z.number().optional(),\n total: z.number().optional(),\n totalPages: z.number().optional(),\n })\n .passthrough();\n\nexport const apiErrorBodySchema = z.object({\n success: z.literal(false),\n data: z.null(),\n error: z.object({\n message: z.string(),\n details: z.unknown().optional(),\n }),\n meta: paginationMetaSchema.optional(),\n});\n\nexport type ApiErrorBody = z.infer<typeof apiErrorBodySchema>;\n\n/**\n * Builds a schema for the success envelope around a given data schema, e.g.\n * `apiSuccessBodySchema(bookingSchema)` or `apiSuccessBodySchema(z.array(bookingSchema))`.\n */\nexport function apiSuccessBodySchema<TData extends z.ZodTypeAny>(dataSchema: TData) {\n return z.object({\n success: z.literal(true),\n data: dataSchema,\n error: z.null(),\n meta: paginationMetaSchema.optional(),\n });\n}\n\nexport type ApiSuccessBody<TData> = {\n success: true;\n data: TData;\n error: null;\n meta?: ApiResponseMeta;\n};\n","import axios from \"axios\";\nimport { z } from \"zod\";\nimport { apiErrorBodySchema } from \"./envelope\";\n\n/**\n * - \"network\": the request never got a response at all (server unreachable, timeout, CORS/preflight failure).\n * - \"validation\": HTTP 400 from our API where `error.details` is the Zod-issues shape produced by\n * `validate.middleware.ts` — field-mappable.\n * - \"http\": every other status our API returns (401, 403, 404, 409, 500, ...). Not field-specific by default.\n */\nexport type ApiClientErrorKind = \"network\" | \"validation\" | \"http\";\n\nexport interface ApiFieldError {\n /** Dot-path, e.g. \"email\" — matched against react-hook-form field names. */\n path: string;\n message: string;\n}\n\nexport interface ApiClientErrorOptions {\n statusCode?: number;\n details?: unknown;\n fieldErrors?: ApiFieldError[];\n}\n\nexport class ApiClientError extends Error {\n readonly kind: ApiClientErrorKind;\n readonly statusCode?: number;\n readonly details?: unknown;\n readonly fieldErrors?: ApiFieldError[];\n\n constructor(kind: ApiClientErrorKind, message: string, options: ApiClientErrorOptions = {}) {\n super(message);\n this.name = \"ApiClientError\";\n this.kind = kind;\n this.statusCode = options.statusCode;\n this.details = options.details;\n this.fieldErrors = options.fieldErrors;\n Object.setPrototypeOf(this, ApiClientError.prototype);\n }\n}\n\nexport function isApiClientError(err: unknown): err is ApiClientError {\n return err instanceof ApiClientError;\n}\n\ninterface ZodLikeIssue {\n path: Array<string | number>;\n message: string;\n}\n\nfunction isZodIssueArray(details: unknown): details is ZodLikeIssue[] {\n return (\n Array.isArray(details) &&\n details.length > 0 &&\n details.every(\n (issue) =>\n typeof issue === \"object\" &&\n issue !== null &&\n Array.isArray((issue as ZodLikeIssue).path) &&\n typeof (issue as ZodLikeIssue).message === \"string\",\n )\n );\n}\n\nfunction toFieldErrors(issues: ZodLikeIssue[]): ApiFieldError[] {\n return issues.map((issue) => ({\n path: issue.path.join(\".\"),\n message: issue.message,\n }));\n}\n\n/**\n * Normalizes anything that can be thrown while making a request (axios error,\n * envelope-shape mismatch, or an unexpected exception) into one `ApiClientError`,\n * classified by `kind` so callers like `useApiForm` can apply a sensible default\n * per failure mode instead of treating a dropped connection the same as a 409.\n */\nexport function normalizeAxiosError(err: unknown): ApiClientError {\n if (isApiClientError(err)) return err;\n\n if (axios.isAxiosError(err)) {\n if (!err.response) {\n return new ApiClientError(\"network\", \"Unable to reach the server. Check your connection and try again.\");\n }\n\n const status = err.response.status;\n const parsedBody = apiErrorBodySchema.safeParse(err.response.data);\n const message = parsedBody.success ? parsedBody.data.error.message : `Request failed with status ${status}`;\n const details = parsedBody.success ? parsedBody.data.error.details : undefined;\n\n if (status === 400 && isZodIssueArray(details)) {\n return new ApiClientError(\"validation\", message, {\n statusCode: status,\n details,\n fieldErrors: toFieldErrors(details),\n });\n }\n\n return new ApiClientError(\"http\", message, { statusCode: status, details });\n }\n\n if (err instanceof z.ZodError) {\n // The server responded 2xx but the payload didn't match the schema we expected -\n // a frontend/backend contract mismatch, not a user-facing form error.\n return new ApiClientError(\"http\", \"Unexpected response shape from the server.\", {\n details: err.issues,\n });\n }\n\n const message = err instanceof Error ? err.message : \"Something went wrong. Please try again.\";\n return new ApiClientError(\"http\", message);\n}\n","export interface AuthTokens {\n accessToken: string;\n refreshToken: string;\n}\n\nexport interface TokenStorage {\n getAccessToken(): string | null;\n getRefreshToken(): string | null;\n setTokens(tokens: AuthTokens): void;\n clearTokens(): void;\n}\n\n/**\n * localStorage-backed token storage, namespaced per app/scope (e.g. \"tenant\", \"platform\")\n * so multiple apps on the same origin (or a single app talking to two backends) never\n * collide on the same keys.\n */\nexport function createLocalStorageTokenStorage(namespace: string): TokenStorage {\n const accessKey = `coworking.${namespace}.accessToken`;\n const refreshKey = `coworking.${namespace}.refreshToken`;\n\n return {\n getAccessToken() {\n return localStorage.getItem(accessKey);\n },\n getRefreshToken() {\n return localStorage.getItem(refreshKey);\n },\n setTokens({ accessToken, refreshToken }) {\n localStorage.setItem(accessKey, accessToken);\n localStorage.setItem(refreshKey, refreshToken);\n },\n clearTokens() {\n localStorage.removeItem(accessKey);\n localStorage.removeItem(refreshKey);\n },\n };\n}\n","import axios, { type AxiosInstance } from \"axios\";\nimport type { AuthTokens, TokenStorage } from \"./tokenStorage\";\n\ndeclare module \"axios\" {\n export interface AxiosRequestConfig {\n /** Set on requests (login/register/refresh) that should never get a Bearer header or trigger a refresh-retry. */\n skipAuth?: boolean;\n /** Internal flag set by the response interceptor to avoid retrying the same request twice. */\n _retriedAfterRefresh?: boolean;\n }\n}\n\nexport interface CreateApiClientConfig {\n baseURL: string;\n tokenStorage: TokenStorage;\n /** Path (relative to `baseURL`) used to exchange a refresh token for a new token pair, e.g. \"/auth/refresh\". */\n refreshPath: string;\n /** Called once refreshing the session has failed (bad/expired refresh token) - e.g. navigate to /login. */\n onAuthExpired?: () => void;\n}\n\n/**\n * Creates an axios instance that:\n * - attaches `Authorization: Bearer <accessToken>` to every request (unless `skipAuth` is set on that request's config)\n * - on a 401, refreshes the session exactly once (concurrent 401s share the same in-flight refresh call via\n * `refreshPromise`, so a burst of requests never fires multiple refreshes) and retries the original request\n * - on refresh failure, clears stored tokens and calls `onAuthExpired` so the app can redirect to login\n *\n * Does NOT unwrap the `{ success, data, error }` envelope itself - see `request.ts` for that - so raw responses\n * (e.g. the 204 from `/auth/logout`) are still inspectable when needed.\n */\nexport function createApiClient(config: CreateApiClientConfig): AxiosInstance {\n const { baseURL, tokenStorage, refreshPath, onAuthExpired } = config;\n const client = axios.create({ baseURL });\n\n let refreshPromise: Promise<AuthTokens> | null = null;\n\n async function refreshTokens(): Promise<AuthTokens> {\n const refreshToken = tokenStorage.getRefreshToken();\n if (!refreshToken) throw new Error(\"No refresh token available\");\n\n // Uses the bare `axios` instance (not `client`) so this call never passes through the\n // interceptors below - it can't trigger its own refresh-retry loop.\n const response = await axios.post<{ data: AuthTokens }>(`${baseURL}${refreshPath}`, { refreshToken });\n const tokens = response.data.data;\n tokenStorage.setTokens(tokens);\n return tokens;\n }\n\n client.interceptors.request.use((requestConfig) => {\n if (requestConfig.skipAuth) return requestConfig;\n\n const accessToken = tokenStorage.getAccessToken();\n if (accessToken) {\n requestConfig.headers.set(\"Authorization\", `Bearer ${accessToken}`);\n }\n return requestConfig;\n });\n\n client.interceptors.response.use(\n (response) => response,\n async (error) => {\n if (!axios.isAxiosError(error) || error.response?.status !== 401) {\n throw error;\n }\n\n const originalRequest = error.config;\n if (!originalRequest || originalRequest.skipAuth || originalRequest._retriedAfterRefresh) {\n throw error;\n }\n\n try {\n refreshPromise ??= refreshTokens().finally(() => {\n refreshPromise = null;\n });\n const tokens = await refreshPromise;\n\n originalRequest._retriedAfterRefresh = true;\n originalRequest.headers.set(\"Authorization\", `Bearer ${tokens.accessToken}`);\n return client.request(originalRequest);\n } catch {\n tokenStorage.clearTokens();\n onAuthExpired?.();\n throw error; // surface the original 401, not the refresh failure\n }\n },\n );\n\n return client;\n}\n","import type { AxiosInstance, AxiosRequestConfig } from \"axios\";\nimport type { z } from \"zod\";\nimport { apiSuccessBodySchema, type ApiResponseMeta } from \"./envelope\";\nimport { normalizeAxiosError } from \"./errors\";\n\nexport interface ApiRequestResult<TData> {\n data: TData;\n meta?: ApiResponseMeta;\n}\n\n/**\n * Runs a request through the given client, validates + unwraps the `{ success, data, error, meta }`\n * envelope against `dataSchema`, and normalizes any failure (network, envelope-shape mismatch, or a\n * `{ success: false }` response) into an `ApiClientError`.\n *\n * This is the one place response validation + error normalization happens, so every service call\n * built on top of it gets a runtime-verified response \"for free\" - if the backend's actual response\n * doesn't match `dataSchema`, this throws instead of silently returning malformed data.\n */\nexport async function apiRequest<TData>(\n client: AxiosInstance,\n requestConfig: AxiosRequestConfig,\n dataSchema: z.ZodType<TData>,\n): Promise<ApiRequestResult<TData>> {\n try {\n const response = await client.request(requestConfig);\n const parsed = apiSuccessBodySchema(dataSchema).parse(response.data);\n // `parsed.data` is structurally `TData` by construction (it came from `dataSchema`), but TS\n // can't prove that through a generic zod composition - narrow it explicitly.\n return { data: parsed.data as TData, meta: parsed.meta };\n } catch (err) {\n throw normalizeAxiosError(err);\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/api/tokenStorage.ts","../src/api/createApiClient.ts","../src/api/request.ts"],"names":[],"mappings":"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","file":"api.js","sourcesContent":["export interface AuthTokens {\n accessToken: string;\n refreshToken: string;\n}\n\nexport interface TokenStorage {\n getAccessToken(): string | null;\n getRefreshToken(): string | null;\n setTokens(tokens: AuthTokens): void;\n clearTokens(): void;\n}\n\n/**\n * localStorage-backed token storage, namespaced per app/scope (e.g. \"tenant\", \"platform\")\n * so multiple apps on the same origin (or a single app talking to two backends) never\n * collide on the same keys.\n */\nexport function createLocalStorageTokenStorage(namespace: string): TokenStorage {\n const accessKey = `coworking.${namespace}.accessToken`;\n const refreshKey = `coworking.${namespace}.refreshToken`;\n\n return {\n getAccessToken() {\n return localStorage.getItem(accessKey);\n },\n getRefreshToken() {\n return localStorage.getItem(refreshKey);\n },\n setTokens({ accessToken, refreshToken }) {\n localStorage.setItem(accessKey, accessToken);\n localStorage.setItem(refreshKey, refreshToken);\n },\n clearTokens() {\n localStorage.removeItem(accessKey);\n localStorage.removeItem(refreshKey);\n },\n };\n}\n","import axios, { type AxiosInstance } from \"axios\";\nimport type { AuthTokens, TokenStorage } from \"./tokenStorage\";\n\ndeclare module \"axios\" {\n export interface AxiosRequestConfig {\n /** Set on requests (login/register/refresh) that should never get a Bearer header or trigger a refresh-retry. */\n skipAuth?: boolean;\n /** Internal flag set by the response interceptor to avoid retrying the same request twice. */\n _retriedAfterRefresh?: boolean;\n }\n}\n\nexport interface CreateApiClientConfig {\n baseURL: string;\n tokenStorage: TokenStorage;\n /** Path (relative to `baseURL`) used to exchange a refresh token for a new token pair, e.g. \"/auth/refresh\". */\n refreshPath: string;\n /** Called once refreshing the session has failed (bad/expired refresh token) - e.g. navigate to /login. */\n onAuthExpired?: () => void;\n}\n\n/**\n * Creates an axios instance that:\n * - attaches `Authorization: Bearer <accessToken>` to every request (unless `skipAuth` is set on that request's config)\n * - on a 401, refreshes the session exactly once (concurrent 401s share the same in-flight refresh call via\n * `refreshPromise`, so a burst of requests never fires multiple refreshes) and retries the original request\n * - on refresh failure, clears stored tokens and calls `onAuthExpired` so the app can redirect to login\n *\n * Does NOT unwrap the `{ success, data, error }` envelope itself - see `request.ts` for that - so raw responses\n * (e.g. the 204 from `/auth/logout`) are still inspectable when needed.\n */\nexport function createApiClient(config: CreateApiClientConfig): AxiosInstance {\n const { baseURL, tokenStorage, refreshPath, onAuthExpired } = config;\n const client = axios.create({ baseURL });\n\n let refreshPromise: Promise<AuthTokens> | null = null;\n\n async function refreshTokens(): Promise<AuthTokens> {\n const refreshToken = tokenStorage.getRefreshToken();\n if (!refreshToken) throw new Error(\"No refresh token available\");\n\n // Uses the bare `axios` instance (not `client`) so this call never passes through the\n // interceptors below - it can't trigger its own refresh-retry loop.\n const response = await axios.post<{ data: AuthTokens }>(`${baseURL}${refreshPath}`, { refreshToken });\n const tokens = response.data.data;\n tokenStorage.setTokens(tokens);\n return tokens;\n }\n\n client.interceptors.request.use((requestConfig) => {\n if (requestConfig.skipAuth) return requestConfig;\n\n const accessToken = tokenStorage.getAccessToken();\n if (accessToken) {\n requestConfig.headers.set(\"Authorization\", `Bearer ${accessToken}`);\n }\n return requestConfig;\n });\n\n client.interceptors.response.use(\n (response) => response,\n async (error) => {\n if (!axios.isAxiosError(error) || error.response?.status !== 401) {\n throw error;\n }\n\n const originalRequest = error.config;\n if (!originalRequest || originalRequest.skipAuth || originalRequest._retriedAfterRefresh) {\n throw error;\n }\n\n try {\n refreshPromise ??= refreshTokens().finally(() => {\n refreshPromise = null;\n });\n const tokens = await refreshPromise;\n\n originalRequest._retriedAfterRefresh = true;\n originalRequest.headers.set(\"Authorization\", `Bearer ${tokens.accessToken}`);\n return client.request(originalRequest);\n } catch {\n tokenStorage.clearTokens();\n onAuthExpired?.();\n throw error; // surface the original 401, not the refresh failure\n }\n },\n );\n\n return client;\n}\n","import type { AxiosInstance, AxiosRequestConfig } from \"axios\";\nimport type { z } from \"zod\";\nimport { apiSuccessBodySchema, type ApiResponseMeta } from \"./envelope\";\nimport { normalizeAxiosError } from \"./errors\";\n\nexport interface ApiRequestResult<TData> {\n data: TData;\n meta?: ApiResponseMeta;\n}\n\n/**\n * Runs a request through the given client, validates + unwraps the `{ success, data, error, meta }`\n * envelope against `dataSchema`, and normalizes any failure (network, envelope-shape mismatch, or a\n * `{ success: false }` response) into an `ApiClientError`.\n *\n * This is the one place response validation + error normalization happens, so every service call\n * built on top of it gets a runtime-verified response \"for free\" - if the backend's actual response\n * doesn't match `dataSchema`, this throws instead of silently returning malformed data.\n */\nexport async function apiRequest<TData>(\n client: AxiosInstance,\n requestConfig: AxiosRequestConfig,\n dataSchema: z.ZodType<TData>,\n): Promise<ApiRequestResult<TData>> {\n try {\n const response = await client.request(requestConfig);\n const parsed = apiSuccessBodySchema(dataSchema).parse(response.data);\n // `parsed.data` is structurally `TData` by construction (it came from `dataSchema`), but TS\n // can't prove that through a generic zod composition - narrow it explicitly.\n return { data: parsed.data as TData, meta: parsed.meta };\n } catch (err) {\n throw normalizeAxiosError(err);\n }\n}\n"]}
|
package/dist/app-modal.d.ts
CHANGED
|
@@ -1,25 +1,24 @@
|
|
|
1
|
-
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
1
|
import * as React from 'react';
|
|
3
|
-
import { D as DialogContent, a as DialogFooter, b as DialogHeader } from './dialog-
|
|
2
|
+
import { D as DialogContent, a as DialogFooter, b as DialogHeader } from './dialog-CPkbi5N3.js';
|
|
4
3
|
import '@radix-ui/react-dialog';
|
|
5
4
|
|
|
6
5
|
/** Shared shell for `DialogContent` when header/body/footer supply their own padding (`p-0`). */
|
|
7
6
|
declare const appModalDialogContentChrome = "bg-card border-border rounded-xl text-foreground shadow-xl gap-0 p-0 overflow-hidden";
|
|
8
|
-
declare function AppModalDialogContent({ className, ...props }: React.ComponentProps<typeof DialogContent>):
|
|
9
|
-
declare function AppModalDialogHeader({ className, ...props }: React.ComponentProps<typeof DialogHeader>):
|
|
10
|
-
declare function AppModalDialogBody({ className, ...props }: React.ComponentProps<"div">):
|
|
7
|
+
declare function AppModalDialogContent({ className, ...props }: React.ComponentProps<typeof DialogContent>): React.JSX.Element;
|
|
8
|
+
declare function AppModalDialogHeader({ className, ...props }: React.ComponentProps<typeof DialogHeader>): React.JSX.Element;
|
|
9
|
+
declare function AppModalDialogBody({ className, ...props }: React.ComponentProps<"div">): React.JSX.Element;
|
|
11
10
|
declare function AppModalDialogFooter({ className, muted, ...props }: React.ComponentProps<typeof DialogFooter> & {
|
|
12
11
|
muted?: boolean;
|
|
13
|
-
}):
|
|
12
|
+
}): React.JSX.Element;
|
|
14
13
|
type ChromeHeaderVariant = "row" | "stack";
|
|
15
14
|
/** Header region for Radix `Dialog.Content` layouts (custom close in header). */
|
|
16
15
|
declare function AppModalChromeHeader({ variant, className, ...props }: React.ComponentProps<"div"> & {
|
|
17
16
|
variant?: ChromeHeaderVariant;
|
|
18
|
-
}):
|
|
17
|
+
}): React.JSX.Element;
|
|
19
18
|
type ChromeFooterVariant = "end" | "split";
|
|
20
19
|
/** Footer region for Radix `Dialog.Content` layouts. */
|
|
21
20
|
declare function AppModalChromeFooter({ variant, className, ...props }: React.ComponentProps<"div"> & {
|
|
22
21
|
variant?: ChromeFooterVariant;
|
|
23
|
-
}):
|
|
22
|
+
}): React.JSX.Element;
|
|
24
23
|
|
|
25
24
|
export { AppModalChromeFooter, AppModalChromeHeader, AppModalDialogBody, AppModalDialogContent, AppModalDialogFooter, AppModalDialogHeader, appModalDialogContentChrome };
|
package/dist/app-modal.js
CHANGED
|
@@ -1,78 +1,7 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { twMerge } from 'tailwind-merge';
|
|
5
|
-
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
1
|
+
import { DialogContent, DialogHeader, DialogFooter } from './chunk-7YCAAP43.js';
|
|
2
|
+
import { cn } from './chunk-DMPSQ22Q.js';
|
|
3
|
+
import { jsx } from 'react/jsx-runtime';
|
|
6
4
|
|
|
7
|
-
function cn(...inputs) {
|
|
8
|
-
return twMerge(clsx(inputs));
|
|
9
|
-
}
|
|
10
|
-
function DialogPortal({ ...props }) {
|
|
11
|
-
return /* @__PURE__ */ jsx(DialogPrimitive.Portal, { "data-slot": "dialog-portal", ...props });
|
|
12
|
-
}
|
|
13
|
-
function DialogOverlay({
|
|
14
|
-
className,
|
|
15
|
-
...props
|
|
16
|
-
}) {
|
|
17
|
-
return /* @__PURE__ */ jsx(
|
|
18
|
-
DialogPrimitive.Overlay,
|
|
19
|
-
{
|
|
20
|
-
"data-slot": "dialog-overlay",
|
|
21
|
-
className: cn(
|
|
22
|
-
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",
|
|
23
|
-
className
|
|
24
|
-
),
|
|
25
|
-
...props
|
|
26
|
-
}
|
|
27
|
-
);
|
|
28
|
-
}
|
|
29
|
-
function DialogContent({
|
|
30
|
-
className,
|
|
31
|
-
children,
|
|
32
|
-
...props
|
|
33
|
-
}) {
|
|
34
|
-
return /* @__PURE__ */ jsxs(DialogPortal, { "data-slot": "dialog-portal", children: [
|
|
35
|
-
/* @__PURE__ */ jsx(DialogOverlay, {}),
|
|
36
|
-
/* @__PURE__ */ jsxs(
|
|
37
|
-
DialogPrimitive.Content,
|
|
38
|
-
{
|
|
39
|
-
"data-slot": "dialog-content",
|
|
40
|
-
className: cn(
|
|
41
|
-
"bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 sm:max-w-lg",
|
|
42
|
-
className
|
|
43
|
-
),
|
|
44
|
-
...props,
|
|
45
|
-
children: [
|
|
46
|
-
children,
|
|
47
|
-
/* @__PURE__ */ jsxs(DialogPrimitive.Close, { className: "ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", children: [
|
|
48
|
-
/* @__PURE__ */ jsx(XIcon, {}),
|
|
49
|
-
/* @__PURE__ */ jsx("span", { className: "sr-only", children: "Close" })
|
|
50
|
-
] })
|
|
51
|
-
]
|
|
52
|
-
}
|
|
53
|
-
)
|
|
54
|
-
] });
|
|
55
|
-
}
|
|
56
|
-
function DialogHeader({ className, ...props }) {
|
|
57
|
-
return /* @__PURE__ */ jsx(
|
|
58
|
-
"div",
|
|
59
|
-
{
|
|
60
|
-
"data-slot": "dialog-header",
|
|
61
|
-
className: cn("flex flex-col gap-2 text-center sm:text-left", className),
|
|
62
|
-
...props
|
|
63
|
-
}
|
|
64
|
-
);
|
|
65
|
-
}
|
|
66
|
-
function DialogFooter({ className, ...props }) {
|
|
67
|
-
return /* @__PURE__ */ jsx(
|
|
68
|
-
"div",
|
|
69
|
-
{
|
|
70
|
-
"data-slot": "dialog-footer",
|
|
71
|
-
className: cn("flex flex-col-reverse gap-2 sm:flex-row sm:justify-end", className),
|
|
72
|
-
...props
|
|
73
|
-
}
|
|
74
|
-
);
|
|
75
|
-
}
|
|
76
5
|
var appModalDialogContentChrome = "bg-card border-border rounded-xl text-foreground shadow-xl gap-0 p-0 overflow-hidden";
|
|
77
6
|
function AppModalDialogContent({
|
|
78
7
|
className,
|
package/dist/app-modal.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/
|
|
1
|
+
{"version":3,"sources":["../src/shell/app-modal.tsx"],"names":[],"mappings":";;;;AAOO,IAAM,2BAAA,GACX;AAEK,SAAS,qBAAA,CAAsB;AAAA,EACpC,SAAA;AAAA,EACA,GAAG;AACL,CAAA,EAA+C;AAC7C,EAAA,uBAAO,GAAA,CAAC,iBAAc,SAAA,EAAW,EAAA,CAAG,6BAA6B,SAAS,CAAA,EAAI,GAAG,KAAA,EAAO,CAAA;AAC1F;AAEO,SAAS,oBAAA,CAAqB;AAAA,EACnC,SAAA;AAAA,EACA,GAAG;AACL,CAAA,EAA8C;AAC5C,EAAA,uBAAO,GAAA,CAAC,gBAAa,SAAA,EAAW,EAAA,CAAG,kBAAkB,SAAS,CAAA,EAAI,GAAG,KAAA,EAAO,CAAA;AAC9E;AAEO,SAAS,kBAAA,CAAmB;AAAA,EACjC,SAAA;AAAA,EACA,GAAG;AACL,CAAA,EAAgC;AAC9B,EAAA,uBAAO,GAAA,CAAC,SAAI,SAAA,EAAW,EAAA,CAAG,iCAAiC,SAAS,CAAA,EAAI,GAAG,KAAA,EAAO,CAAA;AACpF;AAEO,SAAS,oBAAA,CAAqB;AAAA,EACnC,SAAA;AAAA,EACA,KAAA,GAAQ,KAAA;AAAA,EACR,GAAG;AACL,CAAA,EAAoE;AAClE,EAAA,uBACE,GAAA;AAAA,IAAC,YAAA;AAAA,IAAA;AAAA,MACC,SAAA,EAAW,EAAA,CAAG,0BAAA,EAA4B,KAAA,IAAS,eAAe,SAAS,CAAA;AAAA,MAC1E,GAAG;AAAA;AAAA,GACN;AAEJ;AAKO,SAAS,oBAAA,CAAqB;AAAA,EACnC,OAAA,GAAU,KAAA;AAAA,EACV,SAAA;AAAA,EACA,GAAG;AACL,CAAA,EAAoE;AAClE,EAAA,uBACE,GAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACC,SAAA,EAAW,EAAA;AAAA,QACT,UAAA;AAAA,QACA,OAAA,KAAY,UACR,kDAAA,GACA,kDAAA;AAAA,QACJ;AAAA,OACF;AAAA,MACC,GAAG;AAAA;AAAA,GACN;AAEJ;AAKO,SAAS,oBAAA,CAAqB;AAAA,EACnC,OAAA,GAAU,KAAA;AAAA,EACV,SAAA;AAAA,EACA,GAAG;AACL,CAAA,EAAoE;AAClE,EAAA,uBACE,GAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACC,SAAA,EAAW,EAAA;AAAA,QACT,UAAA;AAAA,QACA,OAAA,KAAY,UACR,2DAAA,GACA,+CAAA;AAAA,QACJ;AAAA,OACF;AAAA,MACC,GAAG;AAAA;AAAA,GACN;AAEJ","file":"app-modal.js","sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { DialogContent, DialogFooter, DialogHeader } from \"../ui/dialog\";\nimport { cn } from \"../ui/utils\";\n\n/** Shared shell for `DialogContent` when header/body/footer supply their own padding (`p-0`). */\nexport const appModalDialogContentChrome =\n \"bg-card border-border rounded-xl text-foreground shadow-xl gap-0 p-0 overflow-hidden\";\n\nexport function AppModalDialogContent({\n className,\n ...props\n}: React.ComponentProps<typeof DialogContent>) {\n return <DialogContent className={cn(appModalDialogContentChrome, className)} {...props} />;\n}\n\nexport function AppModalDialogHeader({\n className,\n ...props\n}: React.ComponentProps<typeof DialogHeader>) {\n return <DialogHeader className={cn(\"px-6 pt-6 pb-4\", className)} {...props} />;\n}\n\nexport function AppModalDialogBody({\n className,\n ...props\n}: React.ComponentProps<\"div\">) {\n return <div className={cn(\"px-6 pb-4 flex flex-col gap-4\", className)} {...props} />;\n}\n\nexport function AppModalDialogFooter({\n className,\n muted = false,\n ...props\n}: React.ComponentProps<typeof DialogFooter> & { muted?: boolean }) {\n return (\n <DialogFooter\n className={cn(\"px-6 py-4 gap-2 sm:gap-2\", muted && \"bg-muted/80\", className)}\n {...props}\n />\n );\n}\n\ntype ChromeHeaderVariant = \"row\" | \"stack\";\n\n/** Header region for Radix `Dialog.Content` layouts (custom close in header). */\nexport function AppModalChromeHeader({\n variant = \"row\",\n className,\n ...props\n}: React.ComponentProps<\"div\"> & { variant?: ChromeHeaderVariant }) {\n return (\n <div\n className={cn(\n \"shrink-0\",\n variant === \"stack\"\n ? \"flex items-start justify-between gap-4 px-6 py-4\"\n : \"flex items-center justify-between px-6 pt-5 pb-4\",\n className,\n )}\n {...props}\n />\n );\n}\n\ntype ChromeFooterVariant = \"end\" | \"split\";\n\n/** Footer region for Radix `Dialog.Content` layouts. */\nexport function AppModalChromeFooter({\n variant = \"end\",\n className,\n ...props\n}: React.ComponentProps<\"div\"> & { variant?: ChromeFooterVariant }) {\n return (\n <div\n className={cn(\n \"shrink-0\",\n variant === \"split\"\n ? \"flex items-center justify-between gap-2 px-6 py-4 bg-card\"\n : \"flex items-center justify-end gap-2 px-6 py-4\",\n className,\n )}\n {...props}\n />\n );\n}\n"]}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
1
|
import * as React from 'react';
|
|
3
2
|
import { ReactNode } from 'react';
|
|
4
3
|
|
|
@@ -24,6 +23,6 @@ type AppShellMobileNavProps = {
|
|
|
24
23
|
* Bottom drawer + FAB used with {@link AppWebShell} on viewports below `md`
|
|
25
24
|
* (sidebar and top bar hidden; main is full-bleed white).
|
|
26
25
|
*/
|
|
27
|
-
declare function AppShellMobileNav({ open, onOpenChange, drawerHeader, drawerNav, drawerTitle, }: AppShellMobileNavProps):
|
|
26
|
+
declare function AppShellMobileNav({ open, onOpenChange, drawerHeader, drawerNav, drawerTitle, }: AppShellMobileNavProps): React.JSX.Element;
|
|
28
27
|
|
|
29
28
|
export { AppShellMobileNav, type AppShellMobileNavProps, useAppShellMobileNav };
|
|
@@ -1,73 +1,9 @@
|
|
|
1
|
+
import { Drawer, DrawerContent, DrawerTitle } from './chunk-VVXGQKN7.js';
|
|
2
|
+
import './chunk-DMPSQ22Q.js';
|
|
1
3
|
import { Menu } from 'lucide-react';
|
|
2
4
|
import { useState, useEffect } from 'react';
|
|
3
|
-
import { Drawer as Drawer$1 } from 'vaul';
|
|
4
|
-
import { clsx } from 'clsx';
|
|
5
|
-
import { twMerge } from 'tailwind-merge';
|
|
6
5
|
import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
|
|
7
6
|
|
|
8
|
-
function cn(...inputs) {
|
|
9
|
-
return twMerge(clsx(inputs));
|
|
10
|
-
}
|
|
11
|
-
function Drawer({ ...props }) {
|
|
12
|
-
return /* @__PURE__ */ jsx(Drawer$1.Root, { "data-slot": "drawer", ...props });
|
|
13
|
-
}
|
|
14
|
-
function DrawerPortal({ ...props }) {
|
|
15
|
-
return /* @__PURE__ */ jsx(Drawer$1.Portal, { "data-slot": "drawer-portal", ...props });
|
|
16
|
-
}
|
|
17
|
-
function DrawerOverlay({
|
|
18
|
-
className,
|
|
19
|
-
...props
|
|
20
|
-
}) {
|
|
21
|
-
return /* @__PURE__ */ jsx(
|
|
22
|
-
Drawer$1.Overlay,
|
|
23
|
-
{
|
|
24
|
-
"data-slot": "drawer-overlay",
|
|
25
|
-
className: cn(
|
|
26
|
-
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",
|
|
27
|
-
className
|
|
28
|
-
),
|
|
29
|
-
...props
|
|
30
|
-
}
|
|
31
|
-
);
|
|
32
|
-
}
|
|
33
|
-
function DrawerContent({
|
|
34
|
-
className,
|
|
35
|
-
children,
|
|
36
|
-
...props
|
|
37
|
-
}) {
|
|
38
|
-
return /* @__PURE__ */ jsxs(DrawerPortal, { "data-slot": "drawer-portal", children: [
|
|
39
|
-
/* @__PURE__ */ jsx(DrawerOverlay, {}),
|
|
40
|
-
/* @__PURE__ */ jsxs(
|
|
41
|
-
Drawer$1.Content,
|
|
42
|
-
{
|
|
43
|
-
"data-slot": "drawer-content",
|
|
44
|
-
className: cn(
|
|
45
|
-
"group/drawer-content bg-background fixed z-50 flex h-auto flex-col",
|
|
46
|
-
"data-[vaul-drawer-direction=top]:inset-x-0 data-[vaul-drawer-direction=top]:top-0 data-[vaul-drawer-direction=top]:mb-24 data-[vaul-drawer-direction=top]:max-h-[80vh] data-[vaul-drawer-direction=top]:rounded-b-lg data-[vaul-drawer-direction=top]:border-b",
|
|
47
|
-
"data-[vaul-drawer-direction=bottom]:inset-x-0 data-[vaul-drawer-direction=bottom]:bottom-0 data-[vaul-drawer-direction=bottom]:mt-24 data-[vaul-drawer-direction=bottom]:max-h-[80vh] data-[vaul-drawer-direction=bottom]:rounded-t-lg data-[vaul-drawer-direction=bottom]:border-t",
|
|
48
|
-
"data-[vaul-drawer-direction=right]:inset-y-0 data-[vaul-drawer-direction=right]:right-0 data-[vaul-drawer-direction=right]:w-3/4 data-[vaul-drawer-direction=right]:border-l data-[vaul-drawer-direction=right]:sm:max-w-sm",
|
|
49
|
-
"data-[vaul-drawer-direction=left]:inset-y-0 data-[vaul-drawer-direction=left]:left-0 data-[vaul-drawer-direction=left]:w-3/4 data-[vaul-drawer-direction=left]:border-r data-[vaul-drawer-direction=left]:sm:max-w-sm",
|
|
50
|
-
className
|
|
51
|
-
),
|
|
52
|
-
...props,
|
|
53
|
-
children: [
|
|
54
|
-
/* @__PURE__ */ jsx("div", { className: "bg-muted mx-auto mt-4 hidden h-2 w-[100px] shrink-0 rounded-full group-data-[vaul-drawer-direction=bottom]/drawer-content:block" }),
|
|
55
|
-
children
|
|
56
|
-
]
|
|
57
|
-
}
|
|
58
|
-
)
|
|
59
|
-
] });
|
|
60
|
-
}
|
|
61
|
-
function DrawerTitle({ className, ...props }) {
|
|
62
|
-
return /* @__PURE__ */ jsx(
|
|
63
|
-
Drawer$1.Title,
|
|
64
|
-
{
|
|
65
|
-
"data-slot": "drawer-title",
|
|
66
|
-
className: cn("text-foreground font-semibold", className),
|
|
67
|
-
...props
|
|
68
|
-
}
|
|
69
|
-
);
|
|
70
|
-
}
|
|
71
7
|
function useAppShellMobileNav(pathname) {
|
|
72
8
|
const [mobileNavOpen, setMobileNavOpen] = useState(false);
|
|
73
9
|
useEffect(() => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/ui/utils.ts","../src/ui/drawer.tsx","../src/shell/app-shell-mobile-nav.tsx"],"names":["DrawerPrimitive","jsxs","jsx"],"mappings":"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","file":"app-shell-mobile-nav.js","sourcesContent":["import { clsx, type ClassValue } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport { Drawer as DrawerPrimitive } from \"vaul\";\n\nimport { cn } from \"./utils\";\n\nfunction Drawer({ ...props }: React.ComponentProps<typeof DrawerPrimitive.Root>) {\n return <DrawerPrimitive.Root data-slot=\"drawer\" {...props} />;\n}\n\nfunction DrawerTrigger({ ...props }: React.ComponentProps<typeof DrawerPrimitive.Trigger>) {\n return <DrawerPrimitive.Trigger data-slot=\"drawer-trigger\" {...props} />;\n}\n\nfunction DrawerPortal({ ...props }: React.ComponentProps<typeof DrawerPrimitive.Portal>) {\n return <DrawerPrimitive.Portal data-slot=\"drawer-portal\" {...props} />;\n}\n\nfunction DrawerClose({ ...props }: React.ComponentProps<typeof DrawerPrimitive.Close>) {\n return <DrawerPrimitive.Close data-slot=\"drawer-close\" {...props} />;\n}\n\nfunction DrawerOverlay({\n className,\n ...props\n}: React.ComponentProps<typeof DrawerPrimitive.Overlay>) {\n return (\n <DrawerPrimitive.Overlay\n data-slot=\"drawer-overlay\"\n className={cn(\n \"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50\",\n className,\n )}\n {...props}\n />\n );\n}\n\nfunction DrawerContent({\n className,\n children,\n ...props\n}: React.ComponentProps<typeof DrawerPrimitive.Content>) {\n return (\n <DrawerPortal data-slot=\"drawer-portal\">\n <DrawerOverlay />\n <DrawerPrimitive.Content\n data-slot=\"drawer-content\"\n className={cn(\n \"group/drawer-content bg-background fixed z-50 flex h-auto flex-col\",\n \"data-[vaul-drawer-direction=top]:inset-x-0 data-[vaul-drawer-direction=top]:top-0 data-[vaul-drawer-direction=top]:mb-24 data-[vaul-drawer-direction=top]:max-h-[80vh] data-[vaul-drawer-direction=top]:rounded-b-lg data-[vaul-drawer-direction=top]:border-b\",\n \"data-[vaul-drawer-direction=bottom]:inset-x-0 data-[vaul-drawer-direction=bottom]:bottom-0 data-[vaul-drawer-direction=bottom]:mt-24 data-[vaul-drawer-direction=bottom]:max-h-[80vh] data-[vaul-drawer-direction=bottom]:rounded-t-lg data-[vaul-drawer-direction=bottom]:border-t\",\n \"data-[vaul-drawer-direction=right]:inset-y-0 data-[vaul-drawer-direction=right]:right-0 data-[vaul-drawer-direction=right]:w-3/4 data-[vaul-drawer-direction=right]:border-l data-[vaul-drawer-direction=right]:sm:max-w-sm\",\n \"data-[vaul-drawer-direction=left]:inset-y-0 data-[vaul-drawer-direction=left]:left-0 data-[vaul-drawer-direction=left]:w-3/4 data-[vaul-drawer-direction=left]:border-r data-[vaul-drawer-direction=left]:sm:max-w-sm\",\n className,\n )}\n {...props}\n >\n <div className=\"bg-muted mx-auto mt-4 hidden h-2 w-[100px] shrink-0 rounded-full group-data-[vaul-drawer-direction=bottom]/drawer-content:block\" />\n {children}\n </DrawerPrimitive.Content>\n </DrawerPortal>\n );\n}\n\nfunction DrawerHeader({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"drawer-header\"\n className={cn(\"flex flex-col gap-1.5 p-4\", className)}\n {...props}\n />\n );\n}\n\nfunction DrawerFooter({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"drawer-footer\"\n className={cn(\"mt-auto flex flex-col gap-2 p-4\", className)}\n {...props}\n />\n );\n}\n\nfunction DrawerTitle({ className, ...props }: React.ComponentProps<typeof DrawerPrimitive.Title>) {\n return (\n <DrawerPrimitive.Title\n data-slot=\"drawer-title\"\n className={cn(\"text-foreground font-semibold\", className)}\n {...props}\n />\n );\n}\n\nfunction DrawerDescription({\n className,\n ...props\n}: React.ComponentProps<typeof DrawerPrimitive.Description>) {\n return (\n <DrawerPrimitive.Description\n data-slot=\"drawer-description\"\n className={cn(\"text-muted-foreground text-sm\", className)}\n {...props}\n />\n );\n}\n\nexport {\n Drawer,\n DrawerPortal,\n DrawerOverlay,\n DrawerTrigger,\n DrawerClose,\n DrawerContent,\n DrawerHeader,\n DrawerFooter,\n DrawerTitle,\n DrawerDescription,\n};\n","\"use client\";\n\nimport { Menu } from \"lucide-react\";\nimport { useEffect, useState, type ReactNode } from \"react\";\nimport { Drawer, DrawerContent, DrawerTitle } from \"../ui/drawer\";\n\n/**\n * Closes the mobile nav drawer on route change and when crossing the `md` breakpoint upward.\n * Pass `location.pathname` from React Router (or any stable route key).\n */\nexport function useAppShellMobileNav(pathname: string) {\n const [mobileNavOpen, setMobileNavOpen] = useState(false);\n\n useEffect(() => {\n setMobileNavOpen(false);\n }, [pathname]);\n\n useEffect(() => {\n const mq = window.matchMedia(\"(min-width: 768px)\");\n const closeIfDesktop = () => {\n if (mq.matches) setMobileNavOpen(false);\n };\n mq.addEventListener(\"change\", closeIfDesktop);\n closeIfDesktop();\n return () => mq.removeEventListener(\"change\", closeIfDesktop);\n }, []);\n\n return { mobileNavOpen, setMobileNavOpen };\n}\n\nexport type AppShellMobileNavProps = {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n /** Top block inside the drawer (brand, workspace controls, sign out, etc.). */\n drawerHeader: ReactNode;\n /** Scrollable navigation — usually the same content as `AppWebShell`’s `sidebar`. */\n drawerNav: ReactNode;\n /** Accessible name for the drawer (visually hidden). */\n drawerTitle?: string;\n};\n\n/**\n * Bottom drawer + FAB used with {@link AppWebShell} on viewports below `md`\n * (sidebar and top bar hidden; main is full-bleed white).\n */\nexport function AppShellMobileNav({\n open,\n onOpenChange,\n drawerHeader,\n drawerNav,\n drawerTitle = \"Menu\",\n}: AppShellMobileNavProps) {\n return (\n <>\n <Drawer open={open} onOpenChange={onOpenChange}>\n <DrawerContent className=\"gap-0 border-sidebar-border bg-sidebar px-0 pt-0 pb-[max(1rem,env(safe-area-inset-bottom))] text-sidebar-foreground\">\n <DrawerTitle className=\"sr-only\">{drawerTitle}</DrawerTitle>\n <div className=\"flex max-h-[min(85vh,calc(100dvh-3rem))] min-h-0 flex-col\">\n <div className=\"shrink-0 space-y-3 border-b border-sidebar-border bg-sidebar px-4 pt-2 pb-4\">\n {drawerHeader}\n </div>\n <div className=\"min-h-0 flex-1 overflow-y-auto py-2\">{drawerNav}</div>\n </div>\n </DrawerContent>\n </Drawer>\n\n <button\n type=\"button\"\n className=\"fixed z-40 flex size-14 items-center justify-center rounded-full bg-blue-600 text-white shadow-lg ring-1 ring-black/5 transition-colors hover:bg-blue-700 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-600 md:hidden\"\n style={{\n bottom: \"max(1rem, env(safe-area-inset-bottom))\",\n right: \"max(1rem, env(safe-area-inset-right))\",\n }}\n aria-label=\"Open navigation\"\n aria-expanded={open}\n onClick={() => onOpenChange(true)}\n >\n <Menu size={26} strokeWidth={2.25} aria-hidden />\n </button>\n </>\n );\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/shell/app-shell-mobile-nav.tsx"],"names":[],"mappings":";;;;;;AAUO,SAAS,qBAAqB,QAAA,EAAkB;AACrD,EAAA,MAAM,CAAC,aAAA,EAAe,gBAAgB,CAAA,GAAI,SAAS,KAAK,CAAA;AAExD,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,gBAAA,CAAiB,KAAK,CAAA;AAAA,EACxB,CAAA,EAAG,CAAC,QAAQ,CAAC,CAAA;AAEb,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,MAAM,EAAA,GAAK,MAAA,CAAO,UAAA,CAAW,oBAAoB,CAAA;AACjD,IAAA,MAAM,iBAAiB,MAAM;AAC3B,MAAA,IAAI,EAAA,CAAG,OAAA,EAAS,gBAAA,CAAiB,KAAK,CAAA;AAAA,IACxC,CAAA;AACA,IAAA,EAAA,CAAG,gBAAA,CAAiB,UAAU,cAAc,CAAA;AAC5C,IAAA,cAAA,EAAe;AACf,IAAA,OAAO,MAAM,EAAA,CAAG,mBAAA,CAAoB,QAAA,EAAU,cAAc,CAAA;AAAA,EAC9D,CAAA,EAAG,EAAE,CAAA;AAEL,EAAA,OAAO,EAAE,eAAe,gBAAA,EAAiB;AAC3C;AAiBO,SAAS,iBAAA,CAAkB;AAAA,EAChC,IAAA;AAAA,EACA,YAAA;AAAA,EACA,YAAA;AAAA,EACA,SAAA;AAAA,EACA,WAAA,GAAc;AAChB,CAAA,EAA2B;AACzB,EAAA,uBACE,IAAA,CAAA,QAAA,EAAA,EACE,QAAA,EAAA;AAAA,oBAAA,GAAA,CAAC,UAAO,IAAA,EAAY,YAAA,EAClB,QAAA,kBAAA,IAAA,CAAC,aAAA,EAAA,EAAc,WAAU,qHAAA,EACvB,QAAA,EAAA;AAAA,sBAAA,GAAA,CAAC,WAAA,EAAA,EAAY,SAAA,EAAU,SAAA,EAAW,QAAA,EAAA,WAAA,EAAY,CAAA;AAAA,sBAC9C,IAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,2DAAA,EACb,QAAA,EAAA;AAAA,wBAAA,GAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,6EAAA,EACZ,QAAA,EAAA,YAAA,EACH,CAAA;AAAA,wBACA,GAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,qCAAA,EAAuC,QAAA,EAAA,SAAA,EAAU;AAAA,OAAA,EAClE;AAAA,KAAA,EACF,CAAA,EACF,CAAA;AAAA,oBAEA,GAAA;AAAA,MAAC,QAAA;AAAA,MAAA;AAAA,QACC,IAAA,EAAK,QAAA;AAAA,QACL,SAAA,EAAU,iRAAA;AAAA,QACV,KAAA,EAAO;AAAA,UACL,MAAA,EAAQ,wCAAA;AAAA,UACR,KAAA,EAAO;AAAA,SACT;AAAA,QACA,YAAA,EAAW,iBAAA;AAAA,QACX,eAAA,EAAe,IAAA;AAAA,QACf,OAAA,EAAS,MAAM,YAAA,CAAa,IAAI,CAAA;AAAA,QAEhC,8BAAC,IAAA,EAAA,EAAK,IAAA,EAAM,IAAI,WAAA,EAAa,IAAA,EAAM,eAAW,IAAA,EAAC;AAAA;AAAA;AACjD,GAAA,EACF,CAAA;AAEJ","file":"app-shell-mobile-nav.js","sourcesContent":["\"use client\";\n\nimport { Menu } from \"lucide-react\";\nimport { useEffect, useState, type ReactNode } from \"react\";\nimport { Drawer, DrawerContent, DrawerTitle } from \"../ui/drawer\";\n\n/**\n * Closes the mobile nav drawer on route change and when crossing the `md` breakpoint upward.\n * Pass `location.pathname` from React Router (or any stable route key).\n */\nexport function useAppShellMobileNav(pathname: string) {\n const [mobileNavOpen, setMobileNavOpen] = useState(false);\n\n useEffect(() => {\n setMobileNavOpen(false);\n }, [pathname]);\n\n useEffect(() => {\n const mq = window.matchMedia(\"(min-width: 768px)\");\n const closeIfDesktop = () => {\n if (mq.matches) setMobileNavOpen(false);\n };\n mq.addEventListener(\"change\", closeIfDesktop);\n closeIfDesktop();\n return () => mq.removeEventListener(\"change\", closeIfDesktop);\n }, []);\n\n return { mobileNavOpen, setMobileNavOpen };\n}\n\nexport type AppShellMobileNavProps = {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n /** Top block inside the drawer (brand, workspace controls, sign out, etc.). */\n drawerHeader: ReactNode;\n /** Scrollable navigation — usually the same content as `AppWebShell`’s `sidebar`. */\n drawerNav: ReactNode;\n /** Accessible name for the drawer (visually hidden). */\n drawerTitle?: string;\n};\n\n/**\n * Bottom drawer + FAB used with {@link AppWebShell} on viewports below `md`\n * (sidebar and top bar hidden; main is full-bleed white).\n */\nexport function AppShellMobileNav({\n open,\n onOpenChange,\n drawerHeader,\n drawerNav,\n drawerTitle = \"Menu\",\n}: AppShellMobileNavProps) {\n return (\n <>\n <Drawer open={open} onOpenChange={onOpenChange}>\n <DrawerContent className=\"gap-0 border-sidebar-border bg-sidebar px-0 pt-0 pb-[max(1rem,env(safe-area-inset-bottom))] text-sidebar-foreground\">\n <DrawerTitle className=\"sr-only\">{drawerTitle}</DrawerTitle>\n <div className=\"flex max-h-[min(85vh,calc(100dvh-3rem))] min-h-0 flex-col\">\n <div className=\"shrink-0 space-y-3 border-b border-sidebar-border bg-sidebar px-4 pt-2 pb-4\">\n {drawerHeader}\n </div>\n <div className=\"min-h-0 flex-1 overflow-y-auto py-2\">{drawerNav}</div>\n </div>\n </DrawerContent>\n </Drawer>\n\n <button\n type=\"button\"\n className=\"fixed z-40 flex size-14 items-center justify-center rounded-full bg-blue-600 text-white shadow-lg ring-1 ring-black/5 transition-colors hover:bg-blue-700 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-600 md:hidden\"\n style={{\n bottom: \"max(1rem, env(safe-area-inset-bottom))\",\n right: \"max(1rem, env(safe-area-inset-right))\",\n }}\n aria-label=\"Open navigation\"\n aria-expanded={open}\n onClick={() => onOpenChange(true)}\n >\n <Menu size={26} strokeWidth={2.25} aria-hidden />\n </button>\n </>\n );\n}\n"]}
|
package/dist/app-web-shell.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import * as
|
|
1
|
+
import * as React from 'react';
|
|
2
2
|
import { ReactNode } from 'react';
|
|
3
3
|
|
|
4
4
|
type AppWebShellProps = {
|
|
@@ -15,6 +15,6 @@ type AppWebShellProps = {
|
|
|
15
15
|
* Shared app chrome: outer shell + sidebar, optional top bar, and inset main on desktop.
|
|
16
16
|
* Keeps operator / member (and future web apps) visually aligned.
|
|
17
17
|
*/
|
|
18
|
-
declare function AppWebShell({ topBar, sidebar, sidebarCollapsed, children, }: AppWebShellProps):
|
|
18
|
+
declare function AppWebShell({ topBar, sidebar, sidebarCollapsed, children, }: AppWebShellProps): React.JSX.Element;
|
|
19
19
|
|
|
20
20
|
export { AppWebShell, type AppWebShellProps };
|
package/dist/app-web-shell.js
CHANGED
|
@@ -1,10 +1,7 @@
|
|
|
1
|
+
import { getShellTheme } from './chunk-OWNUMPUC.js';
|
|
1
2
|
import { useEffect } from 'react';
|
|
2
3
|
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
3
4
|
|
|
4
|
-
// src/shell/app-web-shell.tsx
|
|
5
|
-
function getShellTheme() {
|
|
6
|
-
return "light";
|
|
7
|
-
}
|
|
8
5
|
function AppWebShell({
|
|
9
6
|
topBar,
|
|
10
7
|
sidebar,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/
|
|
1
|
+
{"version":3,"sources":["../src/shell/app-web-shell.tsx"],"names":[],"mappings":";;;;AAkBO,SAAS,WAAA,CAAY;AAAA,EAC1B,MAAA;AAAA,EACA,OAAA;AAAA,EACA,gBAAA,GAAmB,IAAA;AAAA,EACnB;AACF,CAAA,EAAqB;AACnB,EAAA,MAAM,aAAa,aAAA,EAAc;AAEjC,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,QAAA,CAAS,eAAA,CAAgB,QAAQ,UAAA,GAAa,UAAA;AAC9C,IAAA,OAAO,MAAM;AACX,MAAA,OAAO,QAAA,CAAS,gBAAgB,OAAA,CAAQ,UAAA;AAAA,IAC1C,CAAA;AAAA,EACF,CAAA,EAAG,CAAC,UAAU,CAAC,CAAA;AAEf,EAAA,uBACE,IAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACC,kBAAA,EAAkB,UAAA;AAAA,MAClB,SAAA,EAAU,6EAAA;AAAA,MAET,QAAA,EAAA;AAAA,QAAA,MAAA,IAAU,uBACT,GAAA,CAAC,QAAA,EAAA,EAAO,SAAA,EAAU,wGAAA,EACf,kBACH,CAAA,GACE,IAAA;AAAA,wBAEJ,IAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,6BAAA,EACb,QAAA,EAAA;AAAA,0BAAA,GAAA;AAAA,YAAC,OAAA;AAAA,YAAA;AAAA,cACC,SAAA,EAAW,CAAA,0HAAA,EACT,gBAAA,GAAmB,MAAA,GAAS,aAC9B,CAAA,CAAA;AAAA,cAEA,QAAA,kBAAA,GAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,qCAAA,EAAuC,QAAA,EAAA,OAAA,EAAQ;AAAA;AAAA,WAChE;AAAA,0BAEA,GAAA,CAAC,SAAI,SAAA,EAAU,qDAAA,EACb,8BAAC,MAAA,EAAA,EAAK,SAAA,EAAU,qIAAA,EACb,QAAA,EACH,CAAA,EACF;AAAA,SAAA,EACF;AAAA;AAAA;AAAA,GACF;AAEJ","file":"app-web-shell.js","sourcesContent":["import { useEffect, type ReactNode } from \"react\";\nimport { getShellTheme } from \"../theme/shell-theme\";\n\nexport type AppWebShellProps = {\n /** Full-width bar above the sidebar + main row (e.g. brand, account actions). */\n topBar?: ReactNode;\n /** Left navigation column. */\n sidebar: ReactNode;\n /** Collapses the desktop sidebar to an icon rail. */\n sidebarCollapsed?: boolean;\n /** Routed page content — typically a card area on desktop. */\n children: ReactNode;\n};\n\n/**\n * Shared app chrome: outer shell + sidebar, optional top bar, and inset main on desktop.\n * Keeps operator / member (and future web apps) visually aligned.\n */\nexport function AppWebShell({\n topBar,\n sidebar,\n sidebarCollapsed = true,\n children,\n}: AppWebShellProps) {\n const shellTheme = getShellTheme();\n\n useEffect(() => {\n document.documentElement.dataset.shellTheme = shellTheme;\n return () => {\n delete document.documentElement.dataset.shellTheme;\n };\n }, [shellTheme]);\n\n return (\n <div\n data-shell-theme={shellTheme}\n className=\"flex h-screen w-screen flex-col overflow-hidden bg-background md:bg-sidebar\"\n >\n {topBar != null ? (\n <header className=\"z-10 hidden h-11 shrink-0 items-center justify-between bg-sidebar px-4 text-sidebar-foreground md:flex\">\n {topBar}\n </header>\n ) : null}\n\n <div className=\"flex min-h-0 min-w-0 flex-1\">\n <aside\n className={`hidden h-full min-h-0 shrink-0 flex-col bg-sidebar py-2.5 text-sidebar-foreground transition-[width] duration-200 md:flex ${\n sidebarCollapsed ? \"w-16\" : \"w-[14.4rem]\"\n }`}\n >\n <div className=\"flex min-h-0 w-full flex-1 flex-col\">{sidebar}</div>\n </aside>\n\n <div className=\"flex min-h-0 min-w-0 flex-1 p-0 md:pr-2.5 md:pb-2.5\">\n <main className=\"flex min-h-0 w-full min-w-0 flex-1 flex-col overflow-hidden rounded-none border-0 bg-card md:rounded-2xl md:border md:border-border\">\n {children}\n </main>\n </div>\n </div>\n </div>\n );\n}\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import * as
|
|
1
|
+
import * as React$1 from 'react';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Date / schedule helpers used by booking-schedule calendars.
|
|
@@ -32,55 +32,55 @@ interface BookingState {
|
|
|
32
32
|
|
|
33
33
|
declare function ScheduleFieldLabel({ children }: {
|
|
34
34
|
children: React.ReactNode;
|
|
35
|
-
}):
|
|
35
|
+
}): React$1.JSX.Element;
|
|
36
36
|
declare function ScheduleInfoBanner({ children }: {
|
|
37
37
|
children: React.ReactNode;
|
|
38
|
-
}):
|
|
38
|
+
}): React$1.JSX.Element;
|
|
39
39
|
declare function ScheduleStepper({ value, min, max, onChange, }: {
|
|
40
40
|
value: number;
|
|
41
41
|
min?: number;
|
|
42
42
|
max?: number;
|
|
43
43
|
onChange: (v: number) => void;
|
|
44
|
-
}):
|
|
44
|
+
}): React$1.JSX.Element;
|
|
45
45
|
declare function WeekCalendarPicker({ weekFrom, onChange, flat, }: {
|
|
46
46
|
weekFrom: string;
|
|
47
47
|
onChange: (v: string) => void;
|
|
48
48
|
flat?: boolean;
|
|
49
|
-
}):
|
|
49
|
+
}): React$1.JSX.Element;
|
|
50
50
|
declare function MonthRangeCalendar({ monthFrom, monthTo, onChange, flat, }: {
|
|
51
51
|
monthFrom: string;
|
|
52
52
|
monthTo: string;
|
|
53
53
|
onChange: (from: string, to: string) => void;
|
|
54
54
|
flat?: boolean;
|
|
55
|
-
}):
|
|
55
|
+
}): React$1.JSX.Element;
|
|
56
56
|
declare function MonthPeriodPicker({ booking, onChange, flat, }: {
|
|
57
57
|
booking: BookingState;
|
|
58
58
|
onChange: (patch: Partial<BookingState>) => void;
|
|
59
59
|
flat?: boolean;
|
|
60
|
-
}):
|
|
60
|
+
}): React$1.JSX.Element;
|
|
61
61
|
declare function WeekPeriodPicker({ booking, onChange, flat, }: {
|
|
62
62
|
booking: BookingState;
|
|
63
63
|
onChange: (patch: Partial<BookingState>) => void;
|
|
64
64
|
flat?: boolean;
|
|
65
|
-
}):
|
|
65
|
+
}): React$1.JSX.Element;
|
|
66
66
|
declare function DayCalendarPicker({ selectedDate, onChange, minDate, flat, }: {
|
|
67
67
|
selectedDate: string;
|
|
68
68
|
onChange: (date: string) => void;
|
|
69
69
|
minDate?: string;
|
|
70
70
|
flat?: boolean;
|
|
71
|
-
}):
|
|
71
|
+
}): React$1.JSX.Element;
|
|
72
72
|
declare function DayRangeCalendar({ startDate, endDate, onChange, minDate, flat, }: {
|
|
73
73
|
startDate: string;
|
|
74
74
|
endDate: string;
|
|
75
75
|
onChange: (start: string, end: string) => void;
|
|
76
76
|
minDate?: string;
|
|
77
77
|
flat?: boolean;
|
|
78
|
-
}):
|
|
78
|
+
}): React$1.JSX.Element;
|
|
79
79
|
declare function DayPeriodPicker({ booking, onChange, flat, }: {
|
|
80
80
|
booking: BookingState;
|
|
81
81
|
onChange: (patch: Partial<BookingState>) => void;
|
|
82
82
|
flat?: boolean;
|
|
83
|
-
}):
|
|
83
|
+
}): React$1.JSX.Element;
|
|
84
84
|
declare function HourlySchedulePicker({ scheduleDate, scheduleTime, onChange, timeFormat, flat, }: {
|
|
85
85
|
scheduleDate: string;
|
|
86
86
|
scheduleTime: string;
|
|
@@ -90,6 +90,6 @@ declare function HourlySchedulePicker({ scheduleDate, scheduleTime, onChange, ti
|
|
|
90
90
|
}) => void;
|
|
91
91
|
timeFormat?: "12h" | "24h";
|
|
92
92
|
flat?: boolean;
|
|
93
|
-
}):
|
|
93
|
+
}): React$1.JSX.Element;
|
|
94
94
|
|
|
95
95
|
export { DayCalendarPicker, DayPeriodPicker, DayRangeCalendar, HourlySchedulePicker, MonthPeriodPicker, MonthRangeCalendar, ScheduleFieldLabel, ScheduleInfoBanner, ScheduleStepper, WeekCalendarPicker, WeekPeriodPicker };
|