@godxjp/ui 2.1.0 → 5.0.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 (46) hide show
  1. package/BRAND.md +39 -29
  2. package/CHANGELOG.md +554 -10
  3. package/README.md +143 -168
  4. package/config/eslint.js +54 -0
  5. package/config/prettier.cjs +20 -0
  6. package/config/tsconfig.base.json +22 -0
  7. package/config/vitest.base.ts +26 -0
  8. package/dist/MiniMonth-YAmPGEpC.d.ts +143 -0
  9. package/dist/Table.types-BbsxoIYE.d.ts +352 -0
  10. package/dist/color-DO0qqUAb.d.ts +38 -0
  11. package/dist/components/composites.d.ts +963 -0
  12. package/dist/components/composites.js +7340 -0
  13. package/dist/components/composites.js.map +1 -0
  14. package/dist/components/primitives.d.ts +2633 -163
  15. package/dist/components/primitives.js +7264 -165
  16. package/dist/components/primitives.js.map +1 -1
  17. package/dist/components/shell.d.ts +82 -12
  18. package/dist/components/shell.js +168 -162
  19. package/dist/components/shell.js.map +1 -1
  20. package/dist/hooks.d.ts +83 -8
  21. package/dist/hooks.js +497 -83
  22. package/dist/hooks.js.map +1 -1
  23. package/dist/i18n.d.ts +55 -3
  24. package/dist/i18n.js +456 -5
  25. package/dist/i18n.js.map +1 -1
  26. package/dist/index.d.ts +24 -5
  27. package/dist/index.js +12522 -267
  28. package/dist/index.js.map +1 -1
  29. package/dist/padding-DY0JV5Ja.d.ts +16 -0
  30. package/dist/preferences.d.ts +132 -0
  31. package/dist/preferences.js +262 -0
  32. package/dist/preferences.js.map +1 -0
  33. package/dist/props.d.ts +86 -0
  34. package/dist/props.js +16 -0
  35. package/dist/props.js.map +1 -0
  36. package/dist/size-CQwNvOWd.d.ts +19 -0
  37. package/dist/{data.d.ts → types-LTj-2bl-.d.ts} +7 -12
  38. package/dist/useTableViews-D5NIAJ7h.d.ts +154 -0
  39. package/package.json +92 -34
  40. package/src/tokens/tailwind.css +158 -0
  41. package/dist/components/screens.d.ts +0 -51
  42. package/dist/components/screens.js +0 -806
  43. package/dist/components/screens.js.map +0 -1
  44. package/dist/data.js +0 -93
  45. package/dist/data.js.map +0 -1
  46. package/src/tokens/tokens.css +0 -765
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Shared `padding` prop-vocabulary per cardinal rule 23 §B.
3
+ *
4
+ * `PaddingProp` is the canonical 4-step ladder used by surface
5
+ * containers — Card, PageHeader, and equivalent composites — to pick
6
+ * how much breathing room sits between the inner content and the
7
+ * container's edge.
8
+ *
9
+ * Density is a separate axis (see `DensityProp`): density rescales the
10
+ * primitive's internal element heights, padding rescales the outer
11
+ * gutter. Most consumers want padding (visual breathing room) without
12
+ * touching density (row heights).
13
+ */
14
+ type PaddingProp = "tight" | "default" | "cozy" | "none";
15
+
16
+ export type { PaddingProp as P };
@@ -0,0 +1,132 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { ReactNode } from 'react';
3
+ import { AxiosInstance } from 'axios';
4
+
5
+ interface GodxConfig {
6
+ /** BCP 47 language tag, e.g. "ja", "en-US". */
7
+ locale: string;
8
+ /** IANA time zone name, e.g. "Asia/Tokyo". */
9
+ timezone: string;
10
+ /**
11
+ * Currency default for `formatCurrency()` calls that omit a code.
12
+ * ISO 4217 — e.g. "JPY", "USD", "VND", "PHP". Optional; helpers
13
+ * require an explicit currency when this is undefined.
14
+ */
15
+ currency?: string;
16
+ }
17
+ declare function getGodxConfig(): GodxConfig;
18
+ declare function setGodxConfig(next: Partial<GodxConfig>): void;
19
+ declare function resetGodxConfig(defaults?: GodxConfig): void;
20
+ declare function subscribeGodxConfig(fn: (c: GodxConfig) => void): () => void;
21
+
22
+ type GodxConfigStorage = "localStorage" | "cookie" | "both";
23
+ interface CookieOptions {
24
+ /** Cookie domain, e.g. ".godx.jp" (omit for host-only). */
25
+ domain?: string;
26
+ /** Max-Age in seconds; default 365 days. */
27
+ maxAgeSeconds?: number;
28
+ /**
29
+ * SameSite — "Lax" is the spec default and the right choice for
30
+ * preferences; "Strict" breaks cross-portal nav, "None" needs Secure.
31
+ */
32
+ sameSite?: "Lax" | "Strict" | "None";
33
+ }
34
+
35
+ interface GodxConfigContextValue extends GodxConfig {
36
+ setLocale: (locale: string) => void;
37
+ setTimezone: (timezone: string) => void;
38
+ setCurrency: (currency: string) => void;
39
+ setGodxConfig: (partial: Partial<GodxConfig>) => void;
40
+ reset: () => void;
41
+ /** Build the canonical request headers (Accept-Language + X-Timezone). */
42
+ headers: () => Record<string, string>;
43
+ }
44
+ interface GodxConfigProviderProps {
45
+ children: ReactNode;
46
+ /** Where to persist — defaults to localStorage. */
47
+ storage?: GodxConfigStorage;
48
+ /** Cookie options when storage is "cookie" or "both". */
49
+ cookieOptions?: CookieOptions;
50
+ /**
51
+ * Fallback locale when neither storage nor `navigator.language`
52
+ * yields a value. Defaults to "ja".
53
+ */
54
+ defaultLocale?: string;
55
+ /**
56
+ * Fallback timezone when neither storage nor
57
+ * `Intl.DateTimeFormat().resolvedOptions().timeZone` yields a value.
58
+ * Defaults to "Asia/Tokyo".
59
+ */
60
+ defaultTimezone?: string;
61
+ /**
62
+ * Default currency code (ISO 4217) for `formatCurrency()` calls
63
+ * that omit a `currency` option. Optional.
64
+ */
65
+ defaultCurrency?: string;
66
+ /**
67
+ * Open delay applied to every `<Tooltip>` in the tree (ms). The
68
+ * provider mounts a single Radix Tooltip Provider internally so
69
+ * the entire app shares this timing — and so the consumer never
70
+ * has to import a separate `TooltipProvider`. Per-tooltip overrides
71
+ * still flow through the `delayDuration` prop on `<Tooltip>`.
72
+ * Default 200.
73
+ */
74
+ tooltipDelay?: number;
75
+ /**
76
+ * Skip-delay duration applied to every `<Tooltip>` in the tree
77
+ * (ms). When the cursor moves between adjacent tooltipped elements
78
+ * within this window the next tooltip opens immediately. Default
79
+ * follows Radix (300ms).
80
+ */
81
+ tooltipSkipDelay?: number;
82
+ /**
83
+ * Called whenever config changes. Useful for syncing the
84
+ * `<html lang>` attribute or pushing to analytics.
85
+ */
86
+ onChange?: (config: GodxConfig) => void;
87
+ }
88
+ /**
89
+ * Root provider for `@godxjp/ui` consumer apps. Carries locale,
90
+ * timezone, and currency defaults; auto-wires React Aria's
91
+ * `<I18nProvider>` so every date / time / number primitive picks up
92
+ * the locale. Persists to localStorage / cookie / both.
93
+ *
94
+ * Mount once at the top of every service frontend's React tree.
95
+ */
96
+ declare function GodxConfigProvider({ children, storage, cookieOptions, defaultLocale, defaultTimezone, defaultCurrency, tooltipDelay, tooltipSkipDelay, onChange, }: GodxConfigProviderProps): react_jsx_runtime.JSX.Element;
97
+ declare function useGodxConfig(): GodxConfigContextValue;
98
+
99
+ interface ApplyGodxConfigHeadersOptions {
100
+ /**
101
+ * Override the Accept-Language header name. RFC 7231 §5.3.5 names
102
+ * the canonical header; only override if proxying to a backend that
103
+ * requires a different name (very rare).
104
+ */
105
+ acceptLanguageHeader?: string;
106
+ /**
107
+ * Override the timezone header name. There is no international
108
+ * standard header for client time zone; we ship `X-Timezone` by
109
+ * convention. Backend reads as IANA tz name (RFC 6557 / ECMA-402).
110
+ */
111
+ timezoneHeader?: string;
112
+ /** Skip Accept-Language if false (default true). */
113
+ sendLocale?: boolean;
114
+ /** Skip X-Timezone if false (default true). */
115
+ sendTimezone?: boolean;
116
+ }
117
+ /**
118
+ * Wire an axios instance to send the current user's locale + timezone
119
+ * on every request. The interceptor reads from the module-level
120
+ * GodxConfig holder at REQUEST time, so values stay fresh as the user
121
+ * changes them — no need to re-install on every config update.
122
+ *
123
+ * Returns an `eject()` callback that removes the interceptor.
124
+ *
125
+ * Usage in `lib/api.ts`:
126
+ * import { meApi } from "./api"
127
+ * import { applyGodxConfigHeaders } from "@godxjp/ui/preferences"
128
+ * applyGodxConfigHeaders(meApi)
129
+ */
130
+ declare function applyGodxConfigHeaders(client: AxiosInstance, options?: ApplyGodxConfigHeadersOptions): () => void;
131
+
132
+ export { type ApplyGodxConfigHeadersOptions, type CookieOptions, type GodxConfig, type GodxConfigContextValue, GodxConfigProvider, type GodxConfigProviderProps, type GodxConfigStorage, applyGodxConfigHeaders, getGodxConfig, resetGodxConfig, setGodxConfig, subscribeGodxConfig, useGodxConfig };
@@ -0,0 +1,262 @@
1
+ import { createContext, useRef, useState, useEffect, useCallback, useMemo, useContext } from 'react';
2
+ import { I18nProvider } from 'react-aria-components';
3
+ import * as TooltipPrimitive from '@radix-ui/react-tooltip';
4
+ import { jsx } from 'react/jsx-runtime';
5
+
6
+ // src/preferences/GodxConfigProvider.tsx
7
+ var TooltipProviderPresenceContext = createContext(false);
8
+ function InternalTooltipProvider({
9
+ children,
10
+ ...rest
11
+ }) {
12
+ return /* @__PURE__ */ jsx(TooltipPrimitive.Provider, { ...rest, children: /* @__PURE__ */ jsx(TooltipProviderPresenceContext.Provider, { value: true, children }) });
13
+ }
14
+
15
+ // src/preferences/holder.ts
16
+ var DEFAULT = {
17
+ locale: "ja",
18
+ timezone: "Asia/Tokyo"
19
+ };
20
+ var CURRENT = { ...DEFAULT };
21
+ var subscribers = /* @__PURE__ */ new Set();
22
+ function getGodxConfig() {
23
+ return CURRENT;
24
+ }
25
+ function setGodxConfig(next) {
26
+ const merged = { ...CURRENT, ...next };
27
+ if (merged.locale === CURRENT.locale && merged.timezone === CURRENT.timezone && merged.currency === CURRENT.currency) {
28
+ return;
29
+ }
30
+ CURRENT = merged;
31
+ for (const fn of subscribers) {
32
+ try {
33
+ fn(CURRENT);
34
+ } catch {
35
+ }
36
+ }
37
+ }
38
+ function resetGodxConfig(defaults = DEFAULT) {
39
+ setGodxConfig(defaults);
40
+ }
41
+ function subscribeGodxConfig(fn) {
42
+ subscribers.add(fn);
43
+ return () => {
44
+ subscribers.delete(fn);
45
+ };
46
+ }
47
+
48
+ // src/preferences/storage.ts
49
+ var DEFAULT_COOKIE_MAX_AGE = 60 * 60 * 24 * 365;
50
+ function isBrowser() {
51
+ return typeof window !== "undefined" && typeof document !== "undefined";
52
+ }
53
+ function isSecure() {
54
+ return isBrowser() && window.location.protocol === "https:";
55
+ }
56
+ function readLocalStorage(key) {
57
+ if (!isBrowser()) return null;
58
+ try {
59
+ return window.localStorage.getItem(key);
60
+ } catch {
61
+ return null;
62
+ }
63
+ }
64
+ function writeLocalStorage(key, value) {
65
+ if (!isBrowser()) return;
66
+ try {
67
+ if (value === null) window.localStorage.removeItem(key);
68
+ else window.localStorage.setItem(key, value);
69
+ } catch {
70
+ }
71
+ }
72
+ function readCookie(name) {
73
+ if (!isBrowser()) return null;
74
+ const target = `${name}=`;
75
+ for (const raw of document.cookie.split(";")) {
76
+ const c = raw.trim();
77
+ if (c.startsWith(target)) return decodeURIComponent(c.slice(target.length));
78
+ }
79
+ return null;
80
+ }
81
+ function writeCookie(name, value, options = {}) {
82
+ if (!isBrowser()) return;
83
+ const sameSite = options.sameSite ?? "Lax";
84
+ const parts = [];
85
+ if (value === null) {
86
+ parts.push(`${name}=`);
87
+ parts.push("Max-Age=0");
88
+ } else {
89
+ parts.push(`${name}=${encodeURIComponent(value)}`);
90
+ parts.push(`Max-Age=${options.maxAgeSeconds ?? DEFAULT_COOKIE_MAX_AGE}`);
91
+ }
92
+ parts.push("Path=/");
93
+ parts.push(`SameSite=${sameSite}`);
94
+ if (options.domain) parts.push(`Domain=${options.domain}`);
95
+ if (sameSite === "None" || isSecure()) parts.push("Secure");
96
+ document.cookie = parts.join("; ");
97
+ }
98
+ function readStored(storage, key) {
99
+ if (storage === "localStorage") return readLocalStorage(key);
100
+ if (storage === "cookie") return readCookie(key);
101
+ return readCookie(key) ?? readLocalStorage(key);
102
+ }
103
+ function writeStored(storage, key, value, cookieOptions) {
104
+ if (storage === "localStorage" || storage === "both") {
105
+ writeLocalStorage(key, value);
106
+ }
107
+ if (storage === "cookie" || storage === "both") {
108
+ writeCookie(key, value, cookieOptions);
109
+ }
110
+ }
111
+ var LOCALE_KEY = "godx-locale";
112
+ var TIMEZONE_KEY = "godx-timezone";
113
+ var CURRENCY_KEY = "godx-currency";
114
+ var Ctx = createContext(null);
115
+ function detectLocale(fallback) {
116
+ if (typeof navigator === "undefined") return fallback;
117
+ const lang = navigator.language;
118
+ return lang || fallback;
119
+ }
120
+ function detectTimezone(fallback) {
121
+ try {
122
+ const tz = Intl.DateTimeFormat().resolvedOptions().timeZone;
123
+ return tz || fallback;
124
+ } catch {
125
+ return fallback;
126
+ }
127
+ }
128
+ function GodxConfigProvider({
129
+ children,
130
+ storage = "localStorage",
131
+ cookieOptions,
132
+ defaultLocale = "ja",
133
+ defaultTimezone = "Asia/Tokyo",
134
+ defaultCurrency,
135
+ tooltipDelay = 200,
136
+ tooltipSkipDelay,
137
+ onChange
138
+ }) {
139
+ const initial = useRef({
140
+ locale: readStored(storage, LOCALE_KEY) ?? detectLocale(defaultLocale),
141
+ timezone: readStored(storage, TIMEZONE_KEY) ?? detectTimezone(defaultTimezone),
142
+ currency: readStored(storage, CURRENCY_KEY) ?? defaultCurrency
143
+ });
144
+ const [config, setConfigState] = useState(initial.current);
145
+ useEffect(() => {
146
+ setGodxConfig(initial.current);
147
+ }, []);
148
+ useEffect(() => {
149
+ return subscribeGodxConfig((next) => {
150
+ setConfigState(next);
151
+ });
152
+ }, []);
153
+ useEffect(() => {
154
+ if (typeof document !== "undefined") {
155
+ document.documentElement.lang = config.locale;
156
+ }
157
+ }, [config.locale]);
158
+ const writeAndBroadcast = useCallback(
159
+ (partial) => {
160
+ if (partial.locale !== void 0) {
161
+ writeStored(storage, LOCALE_KEY, partial.locale, cookieOptions);
162
+ }
163
+ if (partial.timezone !== void 0) {
164
+ writeStored(storage, TIMEZONE_KEY, partial.timezone, cookieOptions);
165
+ }
166
+ if (partial.currency !== void 0) {
167
+ writeStored(storage, CURRENCY_KEY, partial.currency, cookieOptions);
168
+ }
169
+ setGodxConfig(partial);
170
+ },
171
+ [storage, cookieOptions]
172
+ );
173
+ useEffect(() => {
174
+ onChange?.(config);
175
+ }, [config, onChange]);
176
+ const setLocale = useCallback(
177
+ (locale) => writeAndBroadcast({ locale }),
178
+ [writeAndBroadcast]
179
+ );
180
+ const setTimezone = useCallback(
181
+ (timezone) => writeAndBroadcast({ timezone }),
182
+ [writeAndBroadcast]
183
+ );
184
+ const setCurrency = useCallback(
185
+ (currency) => writeAndBroadcast({ currency }),
186
+ [writeAndBroadcast]
187
+ );
188
+ const setAll = useCallback(
189
+ (partial) => writeAndBroadcast(partial),
190
+ [writeAndBroadcast]
191
+ );
192
+ const reset = useCallback(() => {
193
+ writeStored(storage, LOCALE_KEY, null, cookieOptions);
194
+ writeStored(storage, TIMEZONE_KEY, null, cookieOptions);
195
+ writeStored(storage, CURRENCY_KEY, null, cookieOptions);
196
+ const next = {
197
+ locale: detectLocale(defaultLocale),
198
+ timezone: detectTimezone(defaultTimezone),
199
+ currency: defaultCurrency
200
+ };
201
+ setGodxConfig(next);
202
+ }, [storage, cookieOptions, defaultLocale, defaultTimezone, defaultCurrency]);
203
+ const headers = useCallback(() => {
204
+ const current = getGodxConfig();
205
+ return {
206
+ "Accept-Language": current.locale,
207
+ "X-Timezone": current.timezone
208
+ };
209
+ }, []);
210
+ const value = useMemo(
211
+ () => ({
212
+ ...config,
213
+ setLocale,
214
+ setTimezone,
215
+ setCurrency,
216
+ setGodxConfig: setAll,
217
+ reset,
218
+ headers
219
+ }),
220
+ [config, setLocale, setTimezone, setCurrency, setAll, reset, headers]
221
+ );
222
+ return /* @__PURE__ */ jsx(Ctx.Provider, { value, children: /* @__PURE__ */ jsx(I18nProvider, { locale: config.locale, children: /* @__PURE__ */ jsx(
223
+ InternalTooltipProvider,
224
+ {
225
+ delayDuration: tooltipDelay,
226
+ skipDelayDuration: tooltipSkipDelay,
227
+ children
228
+ }
229
+ ) }) });
230
+ }
231
+ function useGodxConfig() {
232
+ const ctx = useContext(Ctx);
233
+ if (!ctx) {
234
+ throw new Error(
235
+ "useGodxConfig must be used within <GodxConfigProvider>"
236
+ );
237
+ }
238
+ return ctx;
239
+ }
240
+
241
+ // src/preferences/applyGodxConfigHeaders.ts
242
+ function applyGodxConfigHeaders(client, options = {}) {
243
+ const acceptLanguageHeader = options.acceptLanguageHeader ?? "Accept-Language";
244
+ const timezoneHeader = options.timezoneHeader ?? "X-Timezone";
245
+ const sendLocale = options.sendLocale !== false;
246
+ const sendTimezone = options.sendTimezone !== false;
247
+ const id = client.interceptors.request.use((config) => {
248
+ const current = getGodxConfig();
249
+ if (sendLocale && current.locale) {
250
+ config.headers.set(acceptLanguageHeader, current.locale);
251
+ }
252
+ if (sendTimezone && current.timezone) {
253
+ config.headers.set(timezoneHeader, current.timezone);
254
+ }
255
+ return config;
256
+ });
257
+ return () => client.interceptors.request.eject(id);
258
+ }
259
+
260
+ export { GodxConfigProvider, applyGodxConfigHeaders, getGodxConfig, resetGodxConfig, setGodxConfig, subscribeGodxConfig, useGodxConfig };
261
+ //# sourceMappingURL=preferences.js.map
262
+ //# sourceMappingURL=preferences.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/data-display/Tooltip.tsx","../src/preferences/holder.ts","../src/preferences/storage.ts","../src/preferences/GodxConfigProvider.tsx","../src/preferences/applyGodxConfigHeaders.ts"],"names":["createContext","jsx","RACI18nProvider","useContext"],"mappings":"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","file":"preferences.js","sourcesContent":["import * as TooltipPrimitive from \"@radix-ui/react-tooltip\";\nimport {\n createContext,\n useContext,\n type ComponentPropsWithoutRef,\n type ReactNode,\n} from \"react\";\nimport { cn } from \"../cn\";\n\n/**\n * Tooltip — Radix-backed floating label anchored to a trigger.\n *\n * Two equivalent consumption modes per cardinal rule 23 (concept-first\n * API + minimise sub-components) and rule 31 (no nested wrappers):\n *\n * 1. Data-driven (preferred) — pass `content`:\n *\n * <Tooltip content=\"保存します\" placement=\"top\">\n * <Button>保存</Button>\n * </Tooltip>\n *\n * Auto-wires the Radix Root + Trigger(asChild) + Content.\n *\n * Shared timing across the app is configured once on\n * `<GodxConfigProvider>` — the consumer never imports a separate\n * `TooltipProvider`. The provider internally mounts the Radix Provider\n * so every nested `<Tooltip>` picks up the same `delayDuration` /\n * `skipDelayDuration`. Per-tooltip overrides flow through the\n * `delayDuration` prop on `<Tooltip>` itself.\n *\n * Styled via the canonical `.tooltip-content` class from\n * `src/styles/shell/35-badge-tag-misc.css` (cardinal rule 21 — reads\n * tokens, honours every axis).\n */\n\n/**\n * Internal marker context — populated by `InternalTooltipProvider`\n * (mounted by `<GodxConfigProvider>`) so data-driven `<Tooltip>` calls\n * can detect they're already inside a Provider and skip\n * double-wrapping. Not exported.\n */\nconst TooltipProviderPresenceContext = createContext<boolean>(false);\n\ntype InternalTooltipProviderProps = ComponentPropsWithoutRef<\n typeof TooltipPrimitive.Provider\n>;\n\n/**\n * `InternalTooltipProvider` — wraps Radix's `Tooltip.Provider` AND\n * populates the internal presence-marker context so nested data-driven\n * `<Tooltip>` calls dedupe automatically.\n *\n * This is an internal surface (not exported from `@godxjp/ui`).\n * `<GodxConfigProvider>` mounts it once at the root so every consumer\n * gets shared tooltip timing without ever importing this directly.\n */\nexport function InternalTooltipProvider({\n children,\n ...rest\n}: InternalTooltipProviderProps) {\n return (\n <TooltipPrimitive.Provider {...rest}>\n <TooltipProviderPresenceContext.Provider value={true}>\n {children}\n </TooltipProviderPresenceContext.Provider>\n </TooltipPrimitive.Provider>\n );\n}\n\ninterface TooltipFloatingProps\n extends ComponentPropsWithoutRef<typeof TooltipPrimitive.Content> {}\n\nfunction TooltipFloating({ className, sideOffset = 4, ...rest }: TooltipFloatingProps) {\n return (\n <TooltipPrimitive.Portal>\n <TooltipPrimitive.Content\n sideOffset={sideOffset}\n className={cn(\"tooltip-content\", className)}\n {...rest}\n />\n </TooltipPrimitive.Portal>\n );\n}\n\nexport interface TooltipProps {\n /** Tooltip text / content. When set, primitive auto-wires the\n * Radix Root + Trigger(asChild) + Content around the child trigger\n * element — the consumer just provides the trigger node. When\n * omitted, the primitive renders children without tooltip wiring. */\n content?: ReactNode;\n /** Trigger element (data-driven mode) OR Radix Root children\n * (compositional mode). */\n children?: ReactNode;\n /** Anchor side per cardinal rule 23 §B `placement` vocabulary.\n * Default `top`. Honoured only in data-driven mode. */\n placement?: \"top\" | \"right\" | \"bottom\" | \"left\";\n /** Open / close delay in ms. Default 200. Overrides the\n * app-wide default set on `<GodxConfigProvider>` for this single\n * tooltip; without an override the Provider's shared timing wins. */\n delayDuration?: number;\n open?: boolean;\n defaultOpen?: boolean;\n onOpenChange?: (open: boolean) => void;\n}\n\nexport function Tooltip({\n content,\n children,\n placement = \"top\",\n delayDuration = 200,\n open,\n defaultOpen,\n onOpenChange,\n}: TooltipProps) {\n const hasAncestorProvider = useContext(TooltipProviderPresenceContext);\n if (content === undefined) return <>{children}</>;\n // Data-driven — auto-wire Root + Trigger + Content. Wrap with an\n // ad-hoc Provider ONLY when there's no ancestor Provider (e.g.\n // isolated Storybook render without <GodxConfigProvider>); otherwise\n // the outer Provider's timing would be silently overridden\n // (cardinal rule 31 — no double-wrap).\n const root = (\n <TooltipPrimitive.Root\n open={open}\n defaultOpen={defaultOpen}\n onOpenChange={onOpenChange}\n >\n <TooltipPrimitive.Trigger asChild>{children}</TooltipPrimitive.Trigger>\n <TooltipFloating side={placement}>{content}</TooltipFloating>\n </TooltipPrimitive.Root>\n );\n if (hasAncestorProvider) return root;\n return (\n <InternalTooltipProvider delayDuration={delayDuration}>\n {root}\n </InternalTooltipProvider>\n );\n}\n","// Module-level GodxConfig holder.\n//\n// Why a module-level holder, not just React context: axios instances\n// are created at module-load time (before React mounts), and axios\n// interceptors need to read config at REQUEST time — which is after\n// the React tree is up. A mutable holder solves both: the React\n// provider writes into it on mount + on every state change; the\n// interceptor reads via `getGodxConfig()` per request.\n//\n// The holder is the single source of truth at runtime. The React\n// state mirrors it for re-renders; outside of React, anyone (axios,\n// fetch wrapper, error reporter, telemetry, etc.) reads through the\n// `getGodxConfig()` getter.\n\nexport interface GodxConfig {\n /** BCP 47 language tag, e.g. \"ja\", \"en-US\". */\n locale: string;\n /** IANA time zone name, e.g. \"Asia/Tokyo\". */\n timezone: string;\n /**\n * Currency default for `formatCurrency()` calls that omit a code.\n * ISO 4217 — e.g. \"JPY\", \"USD\", \"VND\", \"PHP\". Optional; helpers\n * require an explicit currency when this is undefined.\n */\n currency?: string;\n}\n\nconst DEFAULT: GodxConfig = {\n locale: \"ja\",\n timezone: \"Asia/Tokyo\",\n};\n\nlet CURRENT: GodxConfig = { ...DEFAULT };\nconst subscribers = new Set<(c: GodxConfig) => void>();\n\nexport function getGodxConfig(): GodxConfig {\n return CURRENT;\n}\n\nexport function setGodxConfig(next: Partial<GodxConfig>): void {\n const merged: GodxConfig = { ...CURRENT, ...next };\n if (\n merged.locale === CURRENT.locale &&\n merged.timezone === CURRENT.timezone &&\n merged.currency === CURRENT.currency\n ) {\n return;\n }\n CURRENT = merged;\n for (const fn of subscribers) {\n try {\n fn(CURRENT);\n } catch {\n /* subscriber threw — swallow so other subscribers run */\n }\n }\n}\n\nexport function resetGodxConfig(defaults: GodxConfig = DEFAULT): void {\n setGodxConfig(defaults);\n}\n\nexport function subscribeGodxConfig(\n fn: (c: GodxConfig) => void,\n): () => void {\n subscribers.add(fn);\n return () => {\n subscribers.delete(fn);\n };\n}\n","// Storage primitives — localStorage + cookie.\n//\n// localStorage is the primary surface (browser-only, survives reload,\n// no server round-trip). Cookie is opt-in for deployments that read\n// the prefs server-side (SSR / Edge / RSC). Both can be enabled.\n//\n// Cookie format follows RFC 6265bis-13:\n// <name>=<value>; Max-Age=<s>; Path=/; SameSite=Lax; [Secure]; [Domain=<d>]\n// We DO set `Secure` automatically when the page is https.\n\nexport type GodxConfigStorage = \"localStorage\" | \"cookie\" | \"both\";\n\nexport interface CookieOptions {\n /** Cookie domain, e.g. \".godx.jp\" (omit for host-only). */\n domain?: string;\n /** Max-Age in seconds; default 365 days. */\n maxAgeSeconds?: number;\n /**\n * SameSite — \"Lax\" is the spec default and the right choice for\n * preferences; \"Strict\" breaks cross-portal nav, \"None\" needs Secure.\n */\n sameSite?: \"Lax\" | \"Strict\" | \"None\";\n}\n\nconst DEFAULT_COOKIE_MAX_AGE = 60 * 60 * 24 * 365;\n\nfunction isBrowser(): boolean {\n return typeof window !== \"undefined\" && typeof document !== \"undefined\";\n}\n\nfunction isSecure(): boolean {\n return isBrowser() && window.location.protocol === \"https:\";\n}\n\n// ── localStorage ──────────────────────────────────────────────────\n\nexport function readLocalStorage(key: string): string | null {\n if (!isBrowser()) return null;\n try {\n return window.localStorage.getItem(key);\n } catch {\n return null;\n }\n}\n\nexport function writeLocalStorage(key: string, value: string | null): void {\n if (!isBrowser()) return;\n try {\n if (value === null) window.localStorage.removeItem(key);\n else window.localStorage.setItem(key, value);\n } catch {\n /* private-browsing / quota — ignore */\n }\n}\n\n// ── Cookie ────────────────────────────────────────────────────────\n\nexport function readCookie(name: string): string | null {\n if (!isBrowser()) return null;\n const target = `${name}=`;\n for (const raw of document.cookie.split(\";\")) {\n const c = raw.trim();\n if (c.startsWith(target)) return decodeURIComponent(c.slice(target.length));\n }\n return null;\n}\n\nexport function writeCookie(\n name: string,\n value: string | null,\n options: CookieOptions = {},\n): void {\n if (!isBrowser()) return;\n const sameSite = options.sameSite ?? \"Lax\";\n const parts: string[] = [];\n\n if (value === null) {\n parts.push(`${name}=`);\n parts.push(\"Max-Age=0\");\n } else {\n parts.push(`${name}=${encodeURIComponent(value)}`);\n parts.push(`Max-Age=${options.maxAgeSeconds ?? DEFAULT_COOKIE_MAX_AGE}`);\n }\n parts.push(\"Path=/\");\n parts.push(`SameSite=${sameSite}`);\n if (options.domain) parts.push(`Domain=${options.domain}`);\n if (sameSite === \"None\" || isSecure()) parts.push(\"Secure\");\n\n document.cookie = parts.join(\"; \");\n}\n\n// ── Combined read/write per storage policy ────────────────────────\n\nexport function readStored(\n storage: GodxConfigStorage,\n key: string,\n): string | null {\n if (storage === \"localStorage\") return readLocalStorage(key);\n if (storage === \"cookie\") return readCookie(key);\n // \"both\" — cookie wins (server-readable canonical), fall back to LS\n return readCookie(key) ?? readLocalStorage(key);\n}\n\nexport function writeStored(\n storage: GodxConfigStorage,\n key: string,\n value: string | null,\n cookieOptions?: CookieOptions,\n): void {\n if (storage === \"localStorage\" || storage === \"both\") {\n writeLocalStorage(key, value);\n }\n if (storage === \"cookie\" || storage === \"both\") {\n writeCookie(key, value, cookieOptions);\n }\n}\n","import {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n type ReactNode,\n} from \"react\";\nimport { I18nProvider as RACI18nProvider } from \"react-aria-components\";\nimport { InternalTooltipProvider } from \"../components/data-display/Tooltip\";\nimport {\n getGodxConfig,\n setGodxConfig as setHolder,\n subscribeGodxConfig,\n type GodxConfig,\n} from \"./holder\";\nimport {\n readStored,\n writeStored,\n type CookieOptions,\n type GodxConfigStorage,\n} from \"./storage\";\n\nconst LOCALE_KEY = \"godx-locale\";\nconst TIMEZONE_KEY = \"godx-timezone\";\nconst CURRENCY_KEY = \"godx-currency\";\n\nexport interface GodxConfigContextValue extends GodxConfig {\n setLocale: (locale: string) => void;\n setTimezone: (timezone: string) => void;\n setCurrency: (currency: string) => void;\n setGodxConfig: (partial: Partial<GodxConfig>) => void;\n reset: () => void;\n /** Build the canonical request headers (Accept-Language + X-Timezone). */\n headers: () => Record<string, string>;\n}\n\nconst Ctx = createContext<GodxConfigContextValue | null>(null);\n\nexport interface GodxConfigProviderProps {\n children: ReactNode;\n /** Where to persist — defaults to localStorage. */\n storage?: GodxConfigStorage;\n /** Cookie options when storage is \"cookie\" or \"both\". */\n cookieOptions?: CookieOptions;\n /**\n * Fallback locale when neither storage nor `navigator.language`\n * yields a value. Defaults to \"ja\".\n */\n defaultLocale?: string;\n /**\n * Fallback timezone when neither storage nor\n * `Intl.DateTimeFormat().resolvedOptions().timeZone` yields a value.\n * Defaults to \"Asia/Tokyo\".\n */\n defaultTimezone?: string;\n /**\n * Default currency code (ISO 4217) for `formatCurrency()` calls\n * that omit a `currency` option. Optional.\n */\n defaultCurrency?: string;\n /**\n * Open delay applied to every `<Tooltip>` in the tree (ms). The\n * provider mounts a single Radix Tooltip Provider internally so\n * the entire app shares this timing — and so the consumer never\n * has to import a separate `TooltipProvider`. Per-tooltip overrides\n * still flow through the `delayDuration` prop on `<Tooltip>`.\n * Default 200.\n */\n tooltipDelay?: number;\n /**\n * Skip-delay duration applied to every `<Tooltip>` in the tree\n * (ms). When the cursor moves between adjacent tooltipped elements\n * within this window the next tooltip opens immediately. Default\n * follows Radix (300ms).\n */\n tooltipSkipDelay?: number;\n /**\n * Called whenever config changes. Useful for syncing the\n * `<html lang>` attribute or pushing to analytics.\n */\n onChange?: (config: GodxConfig) => void;\n}\n\nfunction detectLocale(fallback: string): string {\n if (typeof navigator === \"undefined\") return fallback;\n const lang = navigator.language;\n return lang || fallback;\n}\n\nfunction detectTimezone(fallback: string): string {\n try {\n const tz = Intl.DateTimeFormat().resolvedOptions().timeZone;\n return tz || fallback;\n } catch {\n return fallback;\n }\n}\n\n/**\n * Root provider for `@godxjp/ui` consumer apps. Carries locale,\n * timezone, and currency defaults; auto-wires React Aria's\n * `<I18nProvider>` so every date / time / number primitive picks up\n * the locale. Persists to localStorage / cookie / both.\n *\n * Mount once at the top of every service frontend's React tree.\n */\nexport function GodxConfigProvider({\n children,\n storage = \"localStorage\",\n cookieOptions,\n defaultLocale = \"ja\",\n defaultTimezone = \"Asia/Tokyo\",\n defaultCurrency,\n tooltipDelay = 200,\n tooltipSkipDelay,\n onChange,\n}: GodxConfigProviderProps) {\n // Resolve initial config: stored → detected → fallback.\n const initial = useRef<GodxConfig>({\n locale:\n readStored(storage, LOCALE_KEY) ?? detectLocale(defaultLocale),\n timezone:\n readStored(storage, TIMEZONE_KEY) ?? detectTimezone(defaultTimezone),\n currency: readStored(storage, CURRENCY_KEY) ?? defaultCurrency,\n });\n\n const [config, setConfigState] = useState<GodxConfig>(initial.current);\n\n // Sync initial values into the module holder so non-React readers\n // (axios interceptor, etc.) pick them up immediately.\n useEffect(() => {\n setHolder(initial.current);\n }, []);\n\n // Bridge: holder → React state. When something else mutates the\n // holder (e.g. another tab via a future BroadcastChannel listener)\n // we re-render.\n useEffect(() => {\n return subscribeGodxConfig((next) => {\n setConfigState(next);\n });\n }, []);\n\n // Mirror to <html lang> so screen readers + CSS :lang() selectors\n // pick up locale changes.\n useEffect(() => {\n if (typeof document !== \"undefined\") {\n document.documentElement.lang = config.locale;\n }\n }, [config.locale]);\n\n // Persist + notify subscribers via the holder.\n const writeAndBroadcast = useCallback(\n (partial: Partial<GodxConfig>) => {\n if (partial.locale !== undefined) {\n writeStored(storage, LOCALE_KEY, partial.locale, cookieOptions);\n }\n if (partial.timezone !== undefined) {\n writeStored(storage, TIMEZONE_KEY, partial.timezone, cookieOptions);\n }\n if (partial.currency !== undefined) {\n writeStored(storage, CURRENCY_KEY, partial.currency, cookieOptions);\n }\n setHolder(partial); // holder → subscribers → setConfigState\n },\n [storage, cookieOptions],\n );\n\n // onChange callback — fires AFTER state settles.\n useEffect(() => {\n onChange?.(config);\n }, [config, onChange]);\n\n const setLocale = useCallback(\n (locale: string) => writeAndBroadcast({ locale }),\n [writeAndBroadcast],\n );\n const setTimezone = useCallback(\n (timezone: string) => writeAndBroadcast({ timezone }),\n [writeAndBroadcast],\n );\n const setCurrency = useCallback(\n (currency: string) => writeAndBroadcast({ currency }),\n [writeAndBroadcast],\n );\n const setAll = useCallback(\n (partial: Partial<GodxConfig>) => writeAndBroadcast(partial),\n [writeAndBroadcast],\n );\n const reset = useCallback(() => {\n writeStored(storage, LOCALE_KEY, null, cookieOptions);\n writeStored(storage, TIMEZONE_KEY, null, cookieOptions);\n writeStored(storage, CURRENCY_KEY, null, cookieOptions);\n const next: GodxConfig = {\n locale: detectLocale(defaultLocale),\n timezone: detectTimezone(defaultTimezone),\n currency: defaultCurrency,\n };\n setHolder(next);\n }, [storage, cookieOptions, defaultLocale, defaultTimezone, defaultCurrency]);\n\n const headers = useCallback((): Record<string, string> => {\n const current = getGodxConfig();\n return {\n \"Accept-Language\": current.locale,\n \"X-Timezone\": current.timezone,\n };\n }, []);\n\n const value = useMemo<GodxConfigContextValue>(\n () => ({\n ...config,\n setLocale,\n setTimezone,\n setCurrency,\n setGodxConfig: setAll,\n reset,\n headers,\n }),\n [config, setLocale, setTimezone, setCurrency, setAll, reset, headers],\n );\n\n return (\n <Ctx.Provider value={value}>\n <RACI18nProvider locale={config.locale}>\n <InternalTooltipProvider\n delayDuration={tooltipDelay}\n skipDelayDuration={tooltipSkipDelay}\n >\n {children}\n </InternalTooltipProvider>\n </RACI18nProvider>\n </Ctx.Provider>\n );\n}\n\nexport function useGodxConfig(): GodxConfigContextValue {\n const ctx = useContext(Ctx);\n if (!ctx) {\n throw new Error(\n \"useGodxConfig must be used within <GodxConfigProvider>\",\n );\n }\n return ctx;\n}\n","import type { AxiosInstance } from \"axios\";\nimport { getGodxConfig } from \"./holder\";\n\nexport interface ApplyGodxConfigHeadersOptions {\n /**\n * Override the Accept-Language header name. RFC 7231 §5.3.5 names\n * the canonical header; only override if proxying to a backend that\n * requires a different name (very rare).\n */\n acceptLanguageHeader?: string;\n /**\n * Override the timezone header name. There is no international\n * standard header for client time zone; we ship `X-Timezone` by\n * convention. Backend reads as IANA tz name (RFC 6557 / ECMA-402).\n */\n timezoneHeader?: string;\n /** Skip Accept-Language if false (default true). */\n sendLocale?: boolean;\n /** Skip X-Timezone if false (default true). */\n sendTimezone?: boolean;\n}\n\n/**\n * Wire an axios instance to send the current user's locale + timezone\n * on every request. The interceptor reads from the module-level\n * GodxConfig holder at REQUEST time, so values stay fresh as the user\n * changes them — no need to re-install on every config update.\n *\n * Returns an `eject()` callback that removes the interceptor.\n *\n * Usage in `lib/api.ts`:\n * import { meApi } from \"./api\"\n * import { applyGodxConfigHeaders } from \"@godxjp/ui/preferences\"\n * applyGodxConfigHeaders(meApi)\n */\nexport function applyGodxConfigHeaders(\n client: AxiosInstance,\n options: ApplyGodxConfigHeadersOptions = {},\n): () => void {\n const acceptLanguageHeader =\n options.acceptLanguageHeader ?? \"Accept-Language\";\n const timezoneHeader = options.timezoneHeader ?? \"X-Timezone\";\n const sendLocale = options.sendLocale !== false;\n const sendTimezone = options.sendTimezone !== false;\n\n const id = client.interceptors.request.use((config) => {\n const current = getGodxConfig();\n if (sendLocale && current.locale) {\n config.headers.set(acceptLanguageHeader, current.locale);\n }\n if (sendTimezone && current.timezone) {\n config.headers.set(timezoneHeader, current.timezone);\n }\n return config;\n });\n\n return () => client.interceptors.request.eject(id);\n}\n"]}
@@ -0,0 +1,86 @@
1
+ export { I as IconSizeProp, S as SizeProp, a as SizeWithXSProp } from './size-CQwNvOWd.js';
2
+ export { C as ColorProp, D as DensityProp, F as FeedbackColorProp } from './color-DO0qqUAb.js';
3
+ export { P as PaddingProp } from './padding-DY0JV5Ja.js';
4
+
5
+ /**
6
+ * Shared status / tone prop-vocabulary per cardinal rule 23 §B.
7
+ *
8
+ * `StatusProp` is the 4-step ladder used by inputs and inline indicators.
9
+ * `ToneProp` is a strict alias — same values, different verb-form chosen
10
+ * by the consuming primitive (Field uses `tone`, Input uses `status`).
11
+ * `HelpToneProp` is the broader 5-step ladder that adds `info` and `warn`
12
+ * for help-text colouring (Field.help, Alert).
13
+ */
14
+ type StatusProp = "default" | "error" | "warning" | "success";
15
+ /** Same values as `StatusProp` — call it `tone` when the prop describes
16
+ * surface colouring rather than validation state. */
17
+ type ToneProp = StatusProp;
18
+ /** Help-line / Alert colour ladder (5 steps including info + warn). */
19
+ type HelpToneProp = "default" | "info" | "warn" | "error" | "success";
20
+
21
+ /**
22
+ * Shared orientation prop-vocabulary per cardinal rule 23 §B.
23
+ * Used by Tabs, Steps, Radio/Checkbox groups, Anchor, Separator, Menu.
24
+ */
25
+ type OrientationProp = "horizontal" | "vertical";
26
+
27
+ /**
28
+ * Shared edge-side / placement prop-vocabulary per cardinal rule 23 §B.
29
+ *
30
+ * `SideProp` is the 4-edge ladder used by Sheet, Tooltip, Popover,
31
+ * Drawer — the prop that picks WHICH edge the floating panel docks
32
+ * against.
33
+ *
34
+ * `PlacementProp` extends `SideProp` with `"center"` for primitives
35
+ * that ALSO support a centred anchor (Tabs placement, Tour spotlight).
36
+ */
37
+ type SideProp = "top" | "right" | "bottom" | "left";
38
+ type PlacementProp = SideProp | "center";
39
+
40
+ /**
41
+ * Shared cross-axis alignment prop-vocabulary per cardinal rule 23 §B.
42
+ *
43
+ * `AlignProp` is the CSS-flexbox alignment ladder used by Flex and
44
+ * row-shaped composites (PageHeader row, IconRow). Mirrors the values
45
+ * `align-items` accepts, dropped to the five that have meaningful
46
+ * design canon.
47
+ */
48
+ type AlignProp = "start" | "end" | "center" | "stretch" | "baseline";
49
+
50
+ /**
51
+ * Shared `loading` prop-vocabulary for Form / FormField / data-entry
52
+ * primitives.
53
+ *
54
+ * loading={true} → spinner (default kind)
55
+ * loading={false} → off
56
+ * loading={{ kind: "skeleton" }} → skeleton placeholder (use for
57
+ * "data is being fetched the
58
+ * first time" — UX nuance)
59
+ * loading={{ kind: "spinner",
60
+ * label: "保存中..." }} → spinner with aria-label
61
+ *
62
+ * Cascade rules:
63
+ * - <Form loading={…}> sets a default for every <FormField> inside.
64
+ * - <FormField loading={…}> overrides the Form default for that field.
65
+ * - A primitive's own `loading` prop overrides both.
66
+ */
67
+ type LoadingKind = "spinner" | "skeleton";
68
+ interface LoadingOptions {
69
+ /** Default `"spinner"`. */
70
+ kind?: LoadingKind;
71
+ /** Accessible label announced to screen readers. */
72
+ label?: string;
73
+ }
74
+ type LoadingProp = boolean | LoadingOptions;
75
+ interface NormalizedLoading {
76
+ active: boolean;
77
+ kind: LoadingKind;
78
+ label?: string;
79
+ }
80
+ /**
81
+ * Coerce the union shape into `{active, kind, label}` so consumers can
82
+ * branch on a single object instead of repeating `typeof prop === …`.
83
+ */
84
+ declare function normalizeLoading(prop: LoadingProp | undefined, defaultKind?: LoadingKind): NormalizedLoading;
85
+
86
+ export { type AlignProp, type HelpToneProp, type LoadingKind, type LoadingOptions, type LoadingProp, type NormalizedLoading, type OrientationProp, type PlacementProp, type SideProp, type StatusProp, type ToneProp, normalizeLoading };
package/dist/props.js ADDED
@@ -0,0 +1,16 @@
1
+ // src/props/loading.ts
2
+ function normalizeLoading(prop, defaultKind = "spinner") {
3
+ if (prop === void 0 || prop === false) {
4
+ return { active: false, kind: defaultKind };
5
+ }
6
+ if (prop === true) return { active: true, kind: defaultKind };
7
+ return {
8
+ active: true,
9
+ kind: prop.kind ?? defaultKind,
10
+ label: prop.label
11
+ };
12
+ }
13
+
14
+ export { normalizeLoading };
15
+ //# sourceMappingURL=props.js.map
16
+ //# sourceMappingURL=props.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/props/loading.ts"],"names":[],"mappings":";AAuCO,SAAS,gBAAA,CACd,IAAA,EACA,WAAA,GAA2B,SAAA,EACR;AACnB,EAAA,IAAI,IAAA,KAAS,MAAA,IAAa,IAAA,KAAS,KAAA,EAAO;AACxC,IAAA,OAAO,EAAE,MAAA,EAAQ,KAAA,EAAO,IAAA,EAAM,WAAA,EAAY;AAAA,EAC5C;AACA,EAAA,IAAI,SAAS,IAAA,EAAM,OAAO,EAAE,MAAA,EAAQ,IAAA,EAAM,MAAM,WAAA,EAAY;AAC5D,EAAA,OAAO;AAAA,IACL,MAAA,EAAQ,IAAA;AAAA,IACR,IAAA,EAAM,KAAK,IAAA,IAAQ,WAAA;AAAA,IACnB,OAAO,IAAA,CAAK;AAAA,GACd;AACF","file":"props.js","sourcesContent":["/**\n * Shared `loading` prop-vocabulary for Form / FormField / data-entry\n * primitives.\n *\n * loading={true} → spinner (default kind)\n * loading={false} → off\n * loading={{ kind: \"skeleton\" }} → skeleton placeholder (use for\n * \"data is being fetched the\n * first time\" — UX nuance)\n * loading={{ kind: \"spinner\",\n * label: \"保存中...\" }} → spinner with aria-label\n *\n * Cascade rules:\n * - <Form loading={…}> sets a default for every <FormField> inside.\n * - <FormField loading={…}> overrides the Form default for that field.\n * - A primitive's own `loading` prop overrides both.\n */\n\nexport type LoadingKind = \"spinner\" | \"skeleton\";\n\nexport interface LoadingOptions {\n /** Default `\"spinner\"`. */\n kind?: LoadingKind;\n /** Accessible label announced to screen readers. */\n label?: string;\n}\n\nexport type LoadingProp = boolean | LoadingOptions;\n\nexport interface NormalizedLoading {\n active: boolean;\n kind: LoadingKind;\n label?: string;\n}\n\n/**\n * Coerce the union shape into `{active, kind, label}` so consumers can\n * branch on a single object instead of repeating `typeof prop === …`.\n */\nexport function normalizeLoading(\n prop: LoadingProp | undefined,\n defaultKind: LoadingKind = \"spinner\",\n): NormalizedLoading {\n if (prop === undefined || prop === false) {\n return { active: false, kind: defaultKind };\n }\n if (prop === true) return { active: true, kind: defaultKind };\n return {\n active: true,\n kind: prop.kind ?? defaultKind,\n label: prop.label,\n };\n}\n"]}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Shared `size` prop-vocabulary per cardinal rule 23 §B.
3
+ *
4
+ * Most primitives accept `"small" | "default" | "large"`. Button extends
5
+ * this with `"x-small"` for compact icon-bar / table-row contexts.
6
+ * IconButton / Spinner use the `sm | md | lg` shorthand (visual symbol
7
+ * sizing — the height token comes from `--density-element-*` regardless).
8
+ *
9
+ * Naming convention: every shared prop-vocabulary type ends in singular
10
+ * `Prop` so it's unambiguous at import sites (matches `LoadingProp` and
11
+ * disambiguates from third-party `Size` types).
12
+ */
13
+ type SizeProp = "small" | "default" | "large";
14
+ /** Button + composites that need a tighter step below `small`. */
15
+ type SizeWithXSProp = "x-small" | SizeProp;
16
+ /** Icon-only primitives whose visual axis is a glyph, not a height. */
17
+ type IconSizeProp = "sm" | "md" | "lg";
18
+
19
+ export type { IconSizeProp as I, SizeProp as S, SizeWithXSProp as a };
@@ -1,6 +1,6 @@
1
1
  type ProjectKind = "service" | "web" | "desktop" | "workstation" | "mobile" | "library" | "infra";
2
2
  type ProjectStatus = "active" | "review" | "planning" | "archived";
3
- type ForgeProject = {
3
+ interface ForgeProject {
4
4
  id: string;
5
5
  name: string;
6
6
  stack: string;
@@ -12,11 +12,12 @@ type ForgeProject = {
12
12
  openIssues: number;
13
13
  prs: number;
14
14
  sandbox: boolean;
15
- };
16
- type ForgeProduct = {
15
+ }
16
+ interface ForgeProduct {
17
17
  id: string;
18
18
  name: string;
19
- tenant: "godx" | "kintai" | "tempo" | "betoya" | "restaurant";
19
+ /** Tenant slug matches `[data-tenant]` attribute. Operator-defined; not a closed enum. */
20
+ tenant: string;
20
21
  role: string;
21
22
  desc: string;
22
23
  /** Brand color in OKLCH — used as the sidebar logo mark + accent. */
@@ -24,12 +25,6 @@ type ForgeProduct = {
24
25
  owner: string;
25
26
  devs: number;
26
27
  projects: ForgeProject[];
27
- };
28
- declare const PRODUCTS: ForgeProduct[];
29
- declare const PROJECT_KIND: Record<ProjectKind, {
30
- color: string;
31
- label: string;
32
- }>;
33
- declare function findProductByTenant(tenant: string): ForgeProduct | undefined;
28
+ }
34
29
 
35
- export { type ForgeProduct, type ForgeProject, PRODUCTS, PROJECT_KIND, type ProjectKind, type ProjectStatus, findProductByTenant };
30
+ export type { ForgeProduct as F, ProjectKind as P, ForgeProject as a, ProjectStatus as b };