@vectramindnpm/unified-design-system 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +255 -0
- package/dist/components.css +387 -0
- package/dist/fonts/Inter-VariableFont_opsz,wght.woff2 +0 -0
- package/dist/fonts/fp-neo.eot +0 -0
- package/dist/fonts/fp-neo.svg +249 -0
- package/dist/fonts/fp-neo.ttf +0 -0
- package/dist/fonts/fp-neo.woff +0 -0
- package/dist/fonts.css +12 -0
- package/dist/icon-font.css +703 -0
- package/dist/index.cjs +3102 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.css +95 -0
- package/dist/index.css.map +1 -0
- package/dist/index.d.cts +775 -0
- package/dist/index.d.ts +775 -0
- package/dist/index.js +2983 -0
- package/dist/index.js.map +1 -0
- package/dist/shadcn-base.css +36 -0
- package/dist/styles.css +11 -0
- package/dist/theme.css +92 -0
- package/dist/tokens.css +685 -0
- package/package.json +89 -0
- package/tailwind-preset.js +84 -0
- package/tailwind-tokens.cjs +87 -0
package/README.md
ADDED
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
# unified-design-system
|
|
2
|
+
|
|
3
|
+
Standalone, publishable UI component library extracted from Firstpass-Neo's
|
|
4
|
+
`src/designSystem` (design tokens) and `src/components/ui` (29 components), with the
|
|
5
|
+
app-specific couplings removed so it can be installed as a plain npm dependency in any
|
|
6
|
+
React app — and, later, browsed in its own Storybook.
|
|
7
|
+
|
|
8
|
+
## What's here
|
|
9
|
+
|
|
10
|
+
```
|
|
11
|
+
src/
|
|
12
|
+
tokens/ ← designTokens.js → semanticTokens.js → componentTokens.js (+ index.js barrel)
|
|
13
|
+
lib/utils.ts ← cn() (clsx + tailwind-merge)
|
|
14
|
+
types/view-mode.ts ← local EViewMode enum (stand-in for the app's models/utilitiesModel)
|
|
15
|
+
components/ui/ ← all 29 components — implementation only (.tsx/.ts/.css), no stories
|
|
16
|
+
stories/ ← one *.stories.tsx per component, kept separate from components/ui/
|
|
17
|
+
styles/
|
|
18
|
+
tokens.css ← design-token CSS custom properties (from the app's variables.css)
|
|
19
|
+
shadcn-base.css ← the shadcn-style base HSL vars a few components use
|
|
20
|
+
theme.css ← Tailwind v4 `@theme inline` bridge (auto-generated, see below)
|
|
21
|
+
components.css ← custom classNames the components render beyond tokens/Tailwind
|
|
22
|
+
icon-font.css ← the `fp-neo` icon font (icon-neo-*) — see below
|
|
23
|
+
fonts.css ← Inter variable-font @font-face
|
|
24
|
+
fonts/ ← the actual font files (fp-neo.*, Inter .woff2)
|
|
25
|
+
index.css ← styles.css entry: imports all of the above
|
|
26
|
+
index.ts ← the package's public barrel export
|
|
27
|
+
tailwind-tokens.cjs ← flat Tailwind v3 color/radius/spacing map (from the app's tailwind.tokens.cjs)
|
|
28
|
+
tailwind-preset.js ← Tailwind v3 preset for consumers still on Tailwind v3
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
The Rsbuild app in the rest of this repo (`src/App.tsx`, `.storybook/`) is a local dev
|
|
32
|
+
harness / smoke test for the same `src/` — it is **not** what gets published.
|
|
33
|
+
|
|
34
|
+
## What changed vs. the original app code
|
|
35
|
+
|
|
36
|
+
Per [docs/design-system-npm-storybook-guide.md](../../../GITHUB/Firstpass-Neo/docs/design-system-npm-storybook-guide.md)'s
|
|
37
|
+
Phase 1 decoupling, plus a few couplings that guide's scan didn't catch:
|
|
38
|
+
|
|
39
|
+
- **`semanticTokens`/`designTokens`/`componentTokens`** — every `ui/*` file that imported
|
|
40
|
+
these through `../../helpers/helpersIndex` now imports directly from `../../tokens`.
|
|
41
|
+
- **i18n removed entirely** (per explicit request, not just made optional) — `icon.tsx`,
|
|
42
|
+
`buttonIcon.tsx`, `delete-action.tsx` no longer import `react-i18next` or
|
|
43
|
+
`getTranslationKey`. Tooltip/aria labels fall back to a **static string** (the icon
|
|
44
|
+
`name`, or a hardcoded label like `"Delete"`/`"Refresh"`) instead of a translation
|
|
45
|
+
lookup. If you need i18n later, wrap the exported components in your own thin
|
|
46
|
+
wrapper that passes a translated `tooltipText` prop — every component that shows a
|
|
47
|
+
label already accepts one.
|
|
48
|
+
- **`view-toggle.tsx`** — dropped `getSessionStateKey`/`getViewType` (app's
|
|
49
|
+
`internaljscontrols.ts`) and `useSelector` (Redux). `ViewToggle` is now a controlled
|
|
50
|
+
component (`viewMode` + `onViewModeChange` props); `useViewMode()` is a plain
|
|
51
|
+
`useState` + `localStorage` hook with no Redux/app dependency.
|
|
52
|
+
- **`truncated-text.tsx`** — same Redux/session coupling removed; takes a `viewMode` prop
|
|
53
|
+
instead of reading it from the app's store.
|
|
54
|
+
- **`toast.tsx`** — the `Toast` component itself was already prop-driven. Its
|
|
55
|
+
`useToast()` hook (which dispatched into the app's own Redux alert store) was
|
|
56
|
+
app-specific state wiring, not a reusable UI primitive, so it's not included — drive
|
|
57
|
+
`<Toast onClose={...} />` from your own state instead.
|
|
58
|
+
- **`loader.tsx`** — dropped the `useSelector` read of the app's `Layout.loading` Redux
|
|
59
|
+
state; `Loader` now just uses its own `isLoading` prop. The loading GIF is bundled
|
|
60
|
+
into the package (inlined as a data URI at build time) instead of importing from the
|
|
61
|
+
app's `src/icons/`.
|
|
62
|
+
- **`time-picker.scss` → `time-picker.css`** — the original Sass file was nested under
|
|
63
|
+
the app's `#fpneo` Tailwind-important selector (would never match elsewhere) and used
|
|
64
|
+
Sass-only nesting. Flattened to plain, framework-agnostic CSS with its `@apply`
|
|
65
|
+
utilities inlined as literal styles, so it needs no Sass or Tailwind build step at all.
|
|
66
|
+
- **`models/utilitiesModel.EViewMode`** → copied locally as `types/view-mode.ts` (it was
|
|
67
|
+
a two-value enum with no other app dependency).
|
|
68
|
+
- A couple of components (`dropdown-menu.tsx`, `popover.tsx`, `tooltip.tsx`,
|
|
69
|
+
`expandable-search.tsx`, `table.tsx`) needed small, behavior-preserving TypeScript
|
|
70
|
+
fixes (a stricter `useRef`/`RefObject` generic, a `type`-only import,
|
|
71
|
+
`JSX.Element` → `React.JSX.Element`) to type-check as an isolated package — same
|
|
72
|
+
runtime behavior, just satisfies a standalone `tsup` build.
|
|
73
|
+
|
|
74
|
+
**Untouched:** all component markup, class names, and visual variants — this was a
|
|
75
|
+
decoupling pass, not a redesign.
|
|
76
|
+
|
|
77
|
+
### Component-level styles (beyond tokens)
|
|
78
|
+
|
|
79
|
+
Several components render classNames that are neither Tailwind utilities nor covered by
|
|
80
|
+
`tokens.css`/`shadcn-base.css` — plain custom classes like `.app-card`, `.switch-pill`,
|
|
81
|
+
`.view-toggle__btn`, `.dropdownHead`, `.item-table__head`, `.fp-loader-overlay`, and the
|
|
82
|
+
`icon-neo-*` icon font every Icon/ButtonIcon/DeleteAction/Toast/TimePicker glyph depends
|
|
83
|
+
on. These lived in the source app's `src/styles/common.scss`, `mainstyles.css`, and
|
|
84
|
+
`font-icons.css` — SCSS nested under that app's `#fpneo` Tailwind-important wrapper, with
|
|
85
|
+
`@apply` directives, so they'd neither match nor build here as-is.
|
|
86
|
+
|
|
87
|
+
- **`src/styles/components.css`** — every one of those classes, flattened to plain CSS
|
|
88
|
+
(`@apply` resolved to literal declarations, `#fpneo`/SCSS nesting stripped). Page- and
|
|
89
|
+
business-specific rules were left out on purpose: Table's per-page `*-cols` grid-column
|
|
90
|
+
layouts (`.flows-cols`, `.appt-cols`, ...) are data-shape-specific, not reusable design
|
|
91
|
+
system material — bring your own to match your own columns. (Two of `table.tsx`'s own
|
|
92
|
+
variant keys, `users` → `.users-cols` and `devices` → `.devices-cols`, turned out to
|
|
93
|
+
have no corresponding CSS anywhere in the source app either — likely dead/mismatched
|
|
94
|
+
keys there already, not something introduced here.)
|
|
95
|
+
- **`src/styles/icon-font.css`** + **`src/styles/fonts.css`** — the `fp-neo` icon font
|
|
96
|
+
(`icon-neo-*`) and Inter variable font `@font-face` rules, copied verbatim (already
|
|
97
|
+
plain CSS) with only their `url()` paths adjusted to this package's own
|
|
98
|
+
`src/styles/fonts/` copy of the four `fp-neo.*` files and the Inter `.woff2`.
|
|
99
|
+
- All of the above are wired into `src/styles/index.css` (and so into `styles.css`),
|
|
100
|
+
alongside `tokens.css`/`shadcn-base.css`/`theme.css` — importing that one file is
|
|
101
|
+
enough. Each is also independently `exports`-mapped (`./components.css`,
|
|
102
|
+
`./icon-font.css`, `./fonts.css`) if you'd rather cherry-pick.
|
|
103
|
+
|
|
104
|
+
Verified via `npm run build-storybook` and `npm run build` (the demo app): Rsbuild/Rspack's
|
|
105
|
+
asset pipeline picks up and correctly hashes all five font files from the `url()`
|
|
106
|
+
references in this CSS — proof the paths resolve. (No headless browser was available in
|
|
107
|
+
this environment to capture an actual rendered screenshot; that's the one thing not
|
|
108
|
+
independently confirmed visually.)
|
|
109
|
+
|
|
110
|
+
### Button variant redesign
|
|
111
|
+
|
|
112
|
+
`Button` originally had **19 `variant` values** conflating four unrelated things: color/
|
|
113
|
+
emphasis, shape (pill vs. rounded), fixed footer sizing, and one-off page-specific skins.
|
|
114
|
+
Redesigned into three independent, composable axes instead:
|
|
115
|
+
|
|
116
|
+
```tsx
|
|
117
|
+
variant: "primary" | "secondary" | "destructive" | "outline" | "ghost" | "link" // 6
|
|
118
|
+
size: "default" | "sm" | "lg" | "icon" | "iconLg" // unchanged
|
|
119
|
+
shape?: "default" | "pill" // NEW
|
|
120
|
+
selected?: boolean // unchanged
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Nothing else in this package used any variant beyond `"ghost"` internally (checked before
|
|
124
|
+
making this change), and the package has no external consumers yet, so this was a clean
|
|
125
|
+
breaking change with nothing to migrate. Mapping from the old 19 to the new API:
|
|
126
|
+
|
|
127
|
+
| Old `variant` | New equivalent | Note |
|
|
128
|
+
|---|---|---|
|
|
129
|
+
| `default` (purple) **and** `primary` (blue) | `variant="primary"` | Consolidated to one color — the token-driven `interactive-brand` (purple), not the hardcoded blue. If you specifically need the old blue, add `className="bg-[var(--color-brand-blue)]"`. |
|
|
130
|
+
| `secondary`, `destructive`, `outline`, `ghost` | same name, unchanged | No visual change. |
|
|
131
|
+
| `pill` | `shape="pill"` (with any `variant`) | Combine, e.g. `variant="outline" shape="pill"`. |
|
|
132
|
+
| `borderPrimary` | `variant="outline"` | Brand-colored border was a one-off tint — add `className="border-interactive-brand text-interactive-brand"` to restore it exactly. |
|
|
133
|
+
| `refreshIcon` | **removed** — use `<ButtonIcon variant="refresh" />` | That's already `ButtonIcon`'s job; `Button` doesn't need to duplicate it. |
|
|
134
|
+
| `selectedIcon` | **removed** — use `<ButtonIcon variant="filled" />` or `<Button variant="primary" size="icon">` | Same reasoning. |
|
|
135
|
+
| `lang` | `variant="ghost"` + `className` | One-off page skin, not a reusable color. |
|
|
136
|
+
| `submenu` | `variant="outline" shape="pill" size="sm"` + `className` | Same. |
|
|
137
|
+
| `deptedit` | `variant="ghost"` + `className="text-interactive-brand hover:bg-[var(--color-ext-eef2ff)]"` | Also drops the legacy `dept-edit-btn` className-sniffing hack entirely. |
|
|
138
|
+
| `cancel` | `variant="outline"` + `className="h-9 px-5 border-[var(--color-button-cancel)] text-[var(--color-button-cancel)]"` | Footer trio: fixed sizing + color both become explicit `className`, since they're page-context, not a semantic color. |
|
|
139
|
+
| `save` | `variant="primary"` + `className="h-9 px-5"` | |
|
|
140
|
+
| `close` | `variant="secondary"` + `className="h-9 px-5"` | |
|
|
141
|
+
| `formButtonSubmit` | `variant="primary" shape="pill"` + `className="h-9 px-6"` | |
|
|
142
|
+
| `formButtonCancel` | `variant="outline" shape="pill"` + `className` for the soft-pink tone | |
|
|
143
|
+
| `formButtonBack` | `variant="outline" shape="pill"` + `className="h-9 px-6"` | |
|
|
144
|
+
|
|
145
|
+
`link` is genuinely new (no old equivalent) — a text-only, underline-on-hover variant with
|
|
146
|
+
no padding/height, the one common pattern the original 19 never actually had.
|
|
147
|
+
|
|
148
|
+
## Using it in this repo right now
|
|
149
|
+
|
|
150
|
+
```tsx
|
|
151
|
+
import { Button, Badge, Card } from './index'; // see src/App.tsx for a working example
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## Building the package
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
npm run build:lib # tsup → dist/{index.cjs,index.mjs,index.d.ts} + dist/*.css
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
This has been verified end-to-end: `npm run build:lib` succeeds, `npm pack` produces a
|
|
161
|
+
clean ~390 KB tarball, and installing that tarball into a scratch project and
|
|
162
|
+
`require()`-ing it resolves all exports correctly (with `react`, `react-dom`, and
|
|
163
|
+
`formik` installed as peers).
|
|
164
|
+
|
|
165
|
+
## Installing it in another app
|
|
166
|
+
|
|
167
|
+
```bash
|
|
168
|
+
npm install /path/to/unified-design-system-0.1.0.tgz # or from your registry once published
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
```tsx
|
|
172
|
+
import { Button, Icon, Table } from 'unified-design-system';
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
Peer dependencies you must have installed: `react`, `react-dom`, and `formik` (4 of the
|
|
176
|
+
29 components — `FormField`, `Input` with `as="field"`, `Textarea` with `as="field"`,
|
|
177
|
+
`ScrollToFieldError` — call real Formik APIs at runtime, so it's a required peer, not
|
|
178
|
+
optional).
|
|
179
|
+
|
|
180
|
+
**Styling** — pick one based on your Tailwind version:
|
|
181
|
+
|
|
182
|
+
- **Tailwind v4**: `@import "unified-design-system/styles.css";` in your global CSS,
|
|
183
|
+
alongside your own `@import "tailwindcss";`. This one import already includes the icon
|
|
184
|
+
font, component classes, and tokens.
|
|
185
|
+
- **Tailwind v3**: add `presets: [require("unified-design-system/tailwind-preset")]` to
|
|
186
|
+
your `tailwind.config.js`, and `@import "unified-design-system/tokens.css";` +
|
|
187
|
+
`@import "unified-design-system/shadcn-base.css";` + `@import "unified-design-system/components.css";`
|
|
188
|
+
+ `@import "unified-design-system/icon-font.css";` + `@import "unified-design-system/fonts.css";`
|
|
189
|
+
in your global CSS.
|
|
190
|
+
- **No Tailwind**: same four `@import`s as above (skip `tokens.css`/`shadcn-base.css`
|
|
191
|
+
only if you don't need the CSS-variable-driven components) — most components read CSS
|
|
192
|
+
custom properties (`var(--color-...)`) rather than Tailwind utility classes.
|
|
193
|
+
|
|
194
|
+
Any icon (`<Icon name="icon-neo-trash" />`, etc.) needs `icon-font.css` loaded — without
|
|
195
|
+
it the glyph classes resolve to nothing and icons render blank.
|
|
196
|
+
|
|
197
|
+
## Publishing (once you're ready)
|
|
198
|
+
|
|
199
|
+
1. `npm run build:lib`
|
|
200
|
+
2. `npm version patch|minor|major`
|
|
201
|
+
3. `npm publish` (add `--registry` for a private/internal registry)
|
|
202
|
+
4. Tag the release in git.
|
|
203
|
+
|
|
204
|
+
This package has no consumers yet, so no versioning discipline is enforced — once
|
|
205
|
+
something depends on it, breaking changes need a major bump.
|
|
206
|
+
|
|
207
|
+
## Storybook
|
|
208
|
+
|
|
209
|
+
Set up via the official installer (`npm create storybook@latest`, framework
|
|
210
|
+
auto-detected as `react-rsbuild`), which added `@storybook/addon-a11y` and
|
|
211
|
+
`@storybook/addon-docs` to `.storybook/main.ts` and to `devDependencies`.
|
|
212
|
+
|
|
213
|
+
The generic Storybook onboarding tutorial (the original `src/stories/` — its demo
|
|
214
|
+
Button/Header/Page components, their `.css`, `Configure.mdx`, and the screenshot/icon
|
|
215
|
+
images under `assets/`) was removed entirely; it wasn't part of this design system. That
|
|
216
|
+
same `src/stories/` path has since been repurposed to hold this design system's own
|
|
217
|
+
stories instead (see below) — a fresh, unrelated folder with the same name.
|
|
218
|
+
|
|
219
|
+
**Every one of the 29 components has its own `*.stories.tsx` in `src/stories/`** — kept
|
|
220
|
+
in a separate folder from `src/components/ui/` (which holds only `.tsx`/`.ts`/`.css`
|
|
221
|
+
implementation files, no stories), and importing from there
|
|
222
|
+
(`import { Button } from "../components/ui/button"`). `.storybook/main.ts`'s
|
|
223
|
+
`../src/**/*.stories.tsx` glob picks them up regardless of where under `src/` they live,
|
|
224
|
+
so this split needed no config change. Each story has a `Default` plus variant/state
|
|
225
|
+
stories and, for the compound components (Dialog, DropdownMenu, Popover, Tooltip, Table,
|
|
226
|
+
AppList, TimePicker, Toast, ViewToggle, ...), a composed working example; components with
|
|
227
|
+
a real `cva` variant/size enum (Button, Badge, Icon, ButtonIcon, Checkbox, RadioButton,
|
|
228
|
+
Tab) also get an `AllVariants`/`AllColors`/`AllSizes`-style story enumerating every value
|
|
229
|
+
from their actual source, cross-checked against it rather than sampled. Every meta also
|
|
230
|
+
carries `tags: ["autodocs"]`, a one-line `parameters.docs.description.component`
|
|
231
|
+
explaining what the component is/isn't (e.g. Icon vs. ButtonIcon, Popover/Tooltip/DropdownMenu
|
|
232
|
+
being hand-built rather than Radix-based), and a `layout` parameter (`"centered"` for
|
|
233
|
+
atomic controls, `"padded"` for larger/compound ones) so each renders sensibly on its own
|
|
234
|
+
Docs page.
|
|
235
|
+
|
|
236
|
+
One correctness fix worth knowing about: `.storybook/preview.tsx` originally imported
|
|
237
|
+
`src/styles/index.css` directly, which never triggers Tailwind itself — same as
|
|
238
|
+
`src/App.css` needing its own `@import 'tailwindcss';` alongside the design-system
|
|
239
|
+
styles. Without it, Storybook's build silently produced zero Tailwind-generated utility
|
|
240
|
+
classes (`bg-interactive-brand`, `rounded-md`, `inline-flex`, ...), so every
|
|
241
|
+
utility-class-driven style rendered as if unstyled even though the underlying CSS
|
|
242
|
+
variables were present. Fixed via `.storybook/preview.css` (`@import 'tailwindcss';`
|
|
243
|
+
+ the design-system styles) — verified by diffing Storybook's compiled CSS against the
|
|
244
|
+
demo app's byte-for-byte, not just a clean build.
|
|
245
|
+
|
|
246
|
+
Verified working: `npm run build-storybook` produces a static build with exactly 29
|
|
247
|
+
autodocs pages (one per component) alongside 106 individual story variants — confirmed by
|
|
248
|
+
inspecting the build's `index.json`, not just a successful exit code. `npm run storybook`
|
|
249
|
+
boots and serves at `http://localhost:6006/`. `npm run lint` reports zero errors across
|
|
250
|
+
the story files.
|
|
251
|
+
|
|
252
|
+
```bash
|
|
253
|
+
npm run storybook # dev server
|
|
254
|
+
npm run build-storybook # static build, e.g. for Chromatic/GitHub Pages
|
|
255
|
+
```
|
|
@@ -0,0 +1,387 @@
|
|
|
1
|
+
/* Component-specific styles these components render classNames for, beyond Tailwind
|
|
2
|
+
utilities and design tokens. Ported from the source app's src/styles/common.scss and
|
|
3
|
+
src/styles/mainstyles.css — originally SCSS nested under an app-specific `#fpneo {}`
|
|
4
|
+
Tailwind-important wrapper (would never match here) with Tailwind `@apply` directives.
|
|
5
|
+
Flattened to plain CSS with `@apply` resolved to literal declarations so this file has
|
|
6
|
+
zero build-step dependency on Sass or Tailwind. Page/business-specific styling (e.g.
|
|
7
|
+
Table's per-page `*-cols` grid-column layouts, and page overrides in clinical.scss) is
|
|
8
|
+
intentionally NOT included — bring your own for those. */
|
|
9
|
+
|
|
10
|
+
/* ── Card (card.tsx) ─────────────────────────────────────────────────────── */
|
|
11
|
+
.app-card {
|
|
12
|
+
background-color: #ffffff;
|
|
13
|
+
border-radius: 1.25rem;
|
|
14
|
+
border: 1px solid #f3f4f6;
|
|
15
|
+
box-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);
|
|
16
|
+
transition-property: box-shadow;
|
|
17
|
+
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
|
18
|
+
transition-duration: 150ms;
|
|
19
|
+
display: flex;
|
|
20
|
+
flex-direction: column;
|
|
21
|
+
}
|
|
22
|
+
.app-card:hover {
|
|
23
|
+
box-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
|
|
24
|
+
}
|
|
25
|
+
.app-card--editing {
|
|
26
|
+
border-color: var(--color-brand-purple);
|
|
27
|
+
box-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/* ── AppList (list.tsx) ──────────────────────────────────────────────────── */
|
|
31
|
+
.app-list-wrapper {
|
|
32
|
+
border: 1px solid #f3f4f6;
|
|
33
|
+
border-radius: 1rem;
|
|
34
|
+
overflow: hidden;
|
|
35
|
+
background-color: #ffffff;
|
|
36
|
+
}
|
|
37
|
+
.app-list-header {
|
|
38
|
+
position: sticky;
|
|
39
|
+
top: 0;
|
|
40
|
+
z-index: 10;
|
|
41
|
+
padding: 0.75rem 1.25rem;
|
|
42
|
+
background-color: var(--color-ext-f8f9fa);
|
|
43
|
+
border-bottom: 1px solid #e5e7eb;
|
|
44
|
+
}
|
|
45
|
+
.app-list-header > div {
|
|
46
|
+
font-size: 0.625rem;
|
|
47
|
+
font-weight: 500;
|
|
48
|
+
color: #9ca3af;
|
|
49
|
+
text-transform: uppercase;
|
|
50
|
+
letter-spacing: 0.05em;
|
|
51
|
+
}
|
|
52
|
+
.app-list-body {
|
|
53
|
+
width: 100%;
|
|
54
|
+
}
|
|
55
|
+
.app-list-row {
|
|
56
|
+
padding: 0.9375rem 1.25rem;
|
|
57
|
+
border-bottom: 1px solid #f3f4f6;
|
|
58
|
+
transition-property: background-color, border-color, color, fill, stroke, opacity, box-shadow, transform, filter;
|
|
59
|
+
transition-duration: 100ms;
|
|
60
|
+
}
|
|
61
|
+
.app-list-row:last-child {
|
|
62
|
+
border-bottom-width: 0;
|
|
63
|
+
}
|
|
64
|
+
.app-list-row:hover {
|
|
65
|
+
background-color: var(--color-ext-fafafa);
|
|
66
|
+
}
|
|
67
|
+
.app-list-row--editing {
|
|
68
|
+
background-color: rgb(238 242 255 / 0.4);
|
|
69
|
+
border-left: 2px solid var(--color-brand-purple);
|
|
70
|
+
}
|
|
71
|
+
.app-list-cell {
|
|
72
|
+
font-size: 0.8125rem;
|
|
73
|
+
color: #374151;
|
|
74
|
+
min-width: 0;
|
|
75
|
+
padding-right: 0.75rem;
|
|
76
|
+
}
|
|
77
|
+
.app-list-cell--muted {
|
|
78
|
+
color: #6b7280;
|
|
79
|
+
}
|
|
80
|
+
.app-list-cell--actions {
|
|
81
|
+
display: flex;
|
|
82
|
+
align-items: center;
|
|
83
|
+
gap: 0.125rem;
|
|
84
|
+
justify-content: flex-end;
|
|
85
|
+
padding-right: 0.25rem;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/* ── Switch (switch.tsx) ─────────────────────────────────────────────────── */
|
|
89
|
+
.switch-pill {
|
|
90
|
+
position: relative;
|
|
91
|
+
display: inline-flex;
|
|
92
|
+
flex-shrink: 0;
|
|
93
|
+
cursor: pointer;
|
|
94
|
+
align-items: center;
|
|
95
|
+
width: 2rem;
|
|
96
|
+
height: 1.125rem;
|
|
97
|
+
border-radius: 9999px;
|
|
98
|
+
padding: 0.125rem;
|
|
99
|
+
transition: background-color 0.25s ease-in-out;
|
|
100
|
+
}
|
|
101
|
+
.switch-pill:focus-within {
|
|
102
|
+
outline: none;
|
|
103
|
+
}
|
|
104
|
+
.switch-pill--on {
|
|
105
|
+
background-color: var(--color-ext-00c48c);
|
|
106
|
+
}
|
|
107
|
+
.switch-pill--off {
|
|
108
|
+
background-color: var(--color-neutral-450);
|
|
109
|
+
}
|
|
110
|
+
.switch-pill--disabled {
|
|
111
|
+
cursor: not-allowed;
|
|
112
|
+
opacity: 0.5;
|
|
113
|
+
}
|
|
114
|
+
.switch-pill__input {
|
|
115
|
+
position: absolute;
|
|
116
|
+
width: 1px;
|
|
117
|
+
height: 1px;
|
|
118
|
+
padding: 0;
|
|
119
|
+
margin: -1px;
|
|
120
|
+
overflow: hidden;
|
|
121
|
+
clip: rect(0, 0, 0, 0);
|
|
122
|
+
white-space: nowrap;
|
|
123
|
+
border-width: 0;
|
|
124
|
+
}
|
|
125
|
+
.switch-pill__thumb {
|
|
126
|
+
pointer-events: none;
|
|
127
|
+
display: block;
|
|
128
|
+
background-color: #ffffff;
|
|
129
|
+
width: 0.875rem;
|
|
130
|
+
height: 0.875rem;
|
|
131
|
+
border-radius: 9999px;
|
|
132
|
+
box-shadow: 0 1px 3px var(--color-ext-0000001f);
|
|
133
|
+
transition: transform 0.25s cubic-bezier(0.4, 0, 0.2, 1);
|
|
134
|
+
}
|
|
135
|
+
.switch-pill__thumb--on {
|
|
136
|
+
transform: translateX(0.875rem);
|
|
137
|
+
}
|
|
138
|
+
[dir="rtl"] .switch-pill__thumb--on {
|
|
139
|
+
transform: translateX(-0.875rem);
|
|
140
|
+
}
|
|
141
|
+
.switch-pill__thumb--off {
|
|
142
|
+
transform: translateX(0);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/* ── ViewToggle (view-toggle.tsx) ────────────────────────────────────────── */
|
|
146
|
+
.view-toggle {
|
|
147
|
+
display: flex;
|
|
148
|
+
align-items: center;
|
|
149
|
+
gap: 0.125rem;
|
|
150
|
+
background-color: #f3f4f6;
|
|
151
|
+
border-radius: 0.5rem;
|
|
152
|
+
padding: 0.25rem;
|
|
153
|
+
}
|
|
154
|
+
.view-toggle__btn {
|
|
155
|
+
display: flex;
|
|
156
|
+
align-items: center;
|
|
157
|
+
justify-content: center;
|
|
158
|
+
width: 2rem;
|
|
159
|
+
height: 2rem;
|
|
160
|
+
border-radius: 0.375rem;
|
|
161
|
+
transition-property: all;
|
|
162
|
+
transition-duration: 150ms;
|
|
163
|
+
color: var(--color-ext-888888);
|
|
164
|
+
opacity: 1 !important;
|
|
165
|
+
}
|
|
166
|
+
.view-toggle__btn:hover {
|
|
167
|
+
color: #4b5563;
|
|
168
|
+
background-color: #f3f4f6;
|
|
169
|
+
}
|
|
170
|
+
.view-toggle__btn:focus,
|
|
171
|
+
.view-toggle__btn:focus-visible {
|
|
172
|
+
outline: none;
|
|
173
|
+
box-shadow: none;
|
|
174
|
+
}
|
|
175
|
+
.view-toggle__btn--active {
|
|
176
|
+
background-color: #ffffff;
|
|
177
|
+
box-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);
|
|
178
|
+
color: var(--color-brand-purple);
|
|
179
|
+
}
|
|
180
|
+
.view-toggle__btn--active:hover {
|
|
181
|
+
color: var(--color-brand-purple);
|
|
182
|
+
background-color: #ffffff;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/* ── DropdownMenu (dropdown-menu.tsx) ────────────────────────────────────── */
|
|
186
|
+
.dropdownHead {
|
|
187
|
+
position: fixed;
|
|
188
|
+
z-index: var(--zindex-overlay);
|
|
189
|
+
min-width: 12rem;
|
|
190
|
+
overflow: hidden;
|
|
191
|
+
border-radius: 0.75rem;
|
|
192
|
+
border: 1px solid #f3f4f6;
|
|
193
|
+
background-color: #ffffff;
|
|
194
|
+
padding-top: 0.5rem;
|
|
195
|
+
padding-bottom: 0.5rem;
|
|
196
|
+
box-shadow: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1);
|
|
197
|
+
transition-property: opacity;
|
|
198
|
+
transition-duration: 150ms;
|
|
199
|
+
}
|
|
200
|
+
.dropdownInner {
|
|
201
|
+
position: relative;
|
|
202
|
+
display: flex;
|
|
203
|
+
cursor: pointer;
|
|
204
|
+
user-select: none;
|
|
205
|
+
align-items: center;
|
|
206
|
+
padding: 0.375rem 0.5rem;
|
|
207
|
+
font-size: 0.875rem;
|
|
208
|
+
line-height: 1.25rem;
|
|
209
|
+
outline: none;
|
|
210
|
+
transition-property: color, background-color, border-color;
|
|
211
|
+
transition-duration: 150ms;
|
|
212
|
+
}
|
|
213
|
+
.dropdownInner[data-disabled] {
|
|
214
|
+
pointer-events: none;
|
|
215
|
+
opacity: 0.5;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/* ── FormField / Input (form-field.tsx, expandable-search.tsx) ──────────── */
|
|
219
|
+
.form__field {
|
|
220
|
+
min-width: 0;
|
|
221
|
+
}
|
|
222
|
+
.form__field > * + * {
|
|
223
|
+
margin-top: 0.5rem;
|
|
224
|
+
}
|
|
225
|
+
.formfield__label {
|
|
226
|
+
font-size: 0.75rem;
|
|
227
|
+
font-weight: 500;
|
|
228
|
+
margin-bottom: 0.5rem;
|
|
229
|
+
display: block;
|
|
230
|
+
color: var(--color-text-primary);
|
|
231
|
+
}
|
|
232
|
+
.form-error {
|
|
233
|
+
font-size: 0.75rem;
|
|
234
|
+
line-height: 1rem;
|
|
235
|
+
color: #ef4444;
|
|
236
|
+
margin-top: 0.25rem;
|
|
237
|
+
}
|
|
238
|
+
.required-field {
|
|
239
|
+
color: #ef4444;
|
|
240
|
+
margin-left: 0.25rem;
|
|
241
|
+
}
|
|
242
|
+
.formfield__search {
|
|
243
|
+
display: flex;
|
|
244
|
+
height: 2.25rem;
|
|
245
|
+
width: 100%;
|
|
246
|
+
border-radius: 0.5rem;
|
|
247
|
+
font-size: 0.8125rem;
|
|
248
|
+
padding: 0.25rem 0.75rem 0.25rem 2.5rem;
|
|
249
|
+
background-color: transparent;
|
|
250
|
+
border: 1px solid var(--color-neutral-300);
|
|
251
|
+
}
|
|
252
|
+
.formfield__search::placeholder {
|
|
253
|
+
color: var(--color-neutral-400);
|
|
254
|
+
font-size: 0.8125rem;
|
|
255
|
+
}
|
|
256
|
+
.formfield__search:disabled {
|
|
257
|
+
cursor: not-allowed;
|
|
258
|
+
opacity: 0.5;
|
|
259
|
+
}
|
|
260
|
+
.formfield__search:focus-visible {
|
|
261
|
+
outline: none;
|
|
262
|
+
border-color: var(--focus-ring-color);
|
|
263
|
+
box-shadow: 0 0 0 1px var(--focus-ring-color-soft);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/* ── ExpandableSearch's collapsed trigger button ─────────────────────────── */
|
|
267
|
+
.header__round-btn {
|
|
268
|
+
width: 2.5625rem;
|
|
269
|
+
height: 2.5625rem;
|
|
270
|
+
padding: 0;
|
|
271
|
+
background-color: var(--color-ext-edeaf7);
|
|
272
|
+
border-radius: 1.25rem;
|
|
273
|
+
display: flex;
|
|
274
|
+
align-items: center;
|
|
275
|
+
justify-content: center;
|
|
276
|
+
font-size: 0.875rem;
|
|
277
|
+
}
|
|
278
|
+
.header__round-btn:hover {
|
|
279
|
+
background-color: var(--color-ext-e0daf0);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/* ── Loader (loader.tsx) — already plain CSS in the source app ──────────── */
|
|
283
|
+
.fp-loader-overlay {
|
|
284
|
+
position: fixed;
|
|
285
|
+
inset: 0;
|
|
286
|
+
z-index: 9999;
|
|
287
|
+
display: flex;
|
|
288
|
+
align-items: center;
|
|
289
|
+
justify-content: center;
|
|
290
|
+
background: rgba(255, 255, 255, 0.45);
|
|
291
|
+
backdrop-filter: blur(2px);
|
|
292
|
+
-webkit-backdrop-filter: blur(2px);
|
|
293
|
+
opacity: 0;
|
|
294
|
+
visibility: hidden;
|
|
295
|
+
transition: opacity 0.25s ease, visibility 0.25s ease;
|
|
296
|
+
pointer-events: none;
|
|
297
|
+
}
|
|
298
|
+
.fp-loader-overlay--visible {
|
|
299
|
+
opacity: 1;
|
|
300
|
+
visibility: visible;
|
|
301
|
+
pointer-events: auto;
|
|
302
|
+
}
|
|
303
|
+
.fp-loader-content {
|
|
304
|
+
display: flex;
|
|
305
|
+
flex-direction: column;
|
|
306
|
+
align-items: center;
|
|
307
|
+
gap: 0.75rem;
|
|
308
|
+
}
|
|
309
|
+
.fp-loader-gif {
|
|
310
|
+
width: 6rem;
|
|
311
|
+
height: 6rem;
|
|
312
|
+
object-fit: contain;
|
|
313
|
+
mix-blend-mode: multiply;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/* ── Table structure (table.tsx) ──────────────────────────────────────────
|
|
317
|
+
Only the classNames Table itself renders. Per-page column layouts
|
|
318
|
+
(`.flows-cols`, `.appt-cols`, etc. — passed as the `variant` prop) are
|
|
319
|
+
business/page-specific grid-template-columns and are NOT included here;
|
|
320
|
+
supply your own to match your own data columns. Row/cell styling for the
|
|
321
|
+
children you render inside Table is likewise left to you. */
|
|
322
|
+
.item-table__wrapper {
|
|
323
|
+
overflow-x: auto;
|
|
324
|
+
border: 1px solid #f3f4f6;
|
|
325
|
+
border-top: 0;
|
|
326
|
+
border-bottom-left-radius: 1rem;
|
|
327
|
+
border-bottom-right-radius: 1rem;
|
|
328
|
+
background-color: #ffffff;
|
|
329
|
+
margin-top: 0 !important;
|
|
330
|
+
}
|
|
331
|
+
.item-table__head {
|
|
332
|
+
position: sticky;
|
|
333
|
+
top: 0;
|
|
334
|
+
z-index: 10;
|
|
335
|
+
display: grid;
|
|
336
|
+
align-items: center;
|
|
337
|
+
gap: 0;
|
|
338
|
+
min-width: 36rem;
|
|
339
|
+
padding: 0.75rem 1.25rem;
|
|
340
|
+
background-color: var(--color-ext-f8f9fa);
|
|
341
|
+
border: 1px solid #f3f4f6;
|
|
342
|
+
border-bottom: 0;
|
|
343
|
+
border-top-left-radius: 1rem;
|
|
344
|
+
border-top-right-radius: 1rem;
|
|
345
|
+
}
|
|
346
|
+
.item-table__head > div {
|
|
347
|
+
font-size: 0.75rem;
|
|
348
|
+
font-weight: 700;
|
|
349
|
+
color: #9ca3af;
|
|
350
|
+
text-transform: uppercase;
|
|
351
|
+
letter-spacing: 0.04em;
|
|
352
|
+
}
|
|
353
|
+
.item-table__body {
|
|
354
|
+
width: 100%;
|
|
355
|
+
}
|
|
356
|
+
.item-table__head--comfortable {
|
|
357
|
+
border: 0;
|
|
358
|
+
border-bottom: 1px solid;
|
|
359
|
+
border-radius: 0;
|
|
360
|
+
min-height: 2.375rem;
|
|
361
|
+
background-color: var(--component-table-header-bg);
|
|
362
|
+
border-color: var(--component-table-border);
|
|
363
|
+
padding: 0.75rem 1.875rem;
|
|
364
|
+
}
|
|
365
|
+
.item-table__head--comfortable > div {
|
|
366
|
+
font-size: 0.6875rem;
|
|
367
|
+
color: var(--component-table-header-text);
|
|
368
|
+
word-break: break-word;
|
|
369
|
+
overflow-wrap: break-word;
|
|
370
|
+
white-space: normal;
|
|
371
|
+
line-height: normal;
|
|
372
|
+
}
|
|
373
|
+
.item-table__wrapper--comfortable {
|
|
374
|
+
border: 0;
|
|
375
|
+
border-radius: 0;
|
|
376
|
+
background-color: transparent;
|
|
377
|
+
}
|
|
378
|
+
.item-table__content--comfortable {
|
|
379
|
+
display: flex;
|
|
380
|
+
flex-direction: column;
|
|
381
|
+
gap: 0.5rem;
|
|
382
|
+
padding: 0.5rem 0;
|
|
383
|
+
}
|
|
384
|
+
.item-table__head--scrollable {
|
|
385
|
+
width: max-content;
|
|
386
|
+
min-width: 100%;
|
|
387
|
+
}
|
|
Binary file
|
|
Binary file
|