@visns-studio/visns-components 6.20.1 → 6.21.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.
@@ -0,0 +1,96 @@
1
+ @use 'surface' as *;
2
+
3
+ /**
4
+ * TabStrip — the underline tabs.
5
+ *
6
+ * The visual language GenericIndex draws, as a component that can be used on its
7
+ * own. Three bespoke screens had rebuilt it by hand, each with the note "the
8
+ * library's tabs are wired to their own state machine, so what is shared is the
9
+ * LOOK, not the code" — which was true, and is the reason this exists: the look
10
+ * belongs in the library even when the state machine does not.
11
+ *
12
+ * The full-width rule is on the STRIP rather than on each tab, and the active
13
+ * tab pulls itself down over it by a pixel, so its underline sits ON that line
14
+ * instead of floating above it.
15
+ */
16
+
17
+ .strip {
18
+ @include surface-tokens;
19
+
20
+ display: flex;
21
+ flex-wrap: wrap;
22
+ gap: var(--spacing-md, 1rem);
23
+ border-bottom: 1px solid var(--pr-line);
24
+ }
25
+
26
+ .tab {
27
+ display: inline-flex;
28
+ gap: 0.4rem;
29
+ align-items: center;
30
+ padding: 0.6rem 0.15rem;
31
+ /* Sits over the strip's own rule. */
32
+ margin-bottom: -1px;
33
+ color: var(--pr-muted);
34
+ font-family: inherit;
35
+ font-size: var(--font-size-sm, 0.8125rem);
36
+ font-weight: var(--font-weight-semibold, 600);
37
+ white-space: nowrap;
38
+ background: none;
39
+ border: 0;
40
+ /* Drawn transparent rather than added on activation, so a tab does not shift
41
+ by 2px the moment it is selected. */
42
+ border-bottom: 2px solid transparent;
43
+ cursor: pointer;
44
+ transition:
45
+ color var(--speed, 160ms) var(--ease, ease),
46
+ border-color var(--speed, 160ms) var(--ease, ease);
47
+
48
+ &:hover:not(:disabled) {
49
+ color: var(--pr-heading);
50
+ }
51
+
52
+ &:focus-visible {
53
+ outline: 2px solid var(--pr-accent);
54
+ outline-offset: 2px;
55
+ }
56
+
57
+ &:disabled {
58
+ cursor: default;
59
+ opacity: 0.5;
60
+ }
61
+ }
62
+
63
+ .active {
64
+ color: var(--pr-heading);
65
+ border-bottom-color: var(--pr-accent);
66
+ }
67
+
68
+ /* -------------------------------------------------------------------------- */
69
+ /* the count on a tab */
70
+ /* -------------------------------------------------------------------------- */
71
+
72
+ /* A count rides along where a tab has one worth pre-empting — it is the
73
+ difference between "go and look at Service health" and "go and look at Service
74
+ health, there are 21 things wrong".
75
+ *
76
+ * Two tones, and the distinction matters: `quiet` is HOW MANY (four tickets on
77
+ * this contact), `danger` is HOW MANY ARE WRONG (twenty-one devices offline). A
78
+ * red pill on a plain count is an alarm about nothing. */
79
+ .badge {
80
+ padding: 0.05rem 0.35rem;
81
+ font-size: var(--font-size-xs, 0.75rem);
82
+ font-variant-numeric: tabular-nums;
83
+ border-radius: var(--radius-pill, 999px);
84
+ }
85
+
86
+ .badgeQuiet {
87
+ color: var(--pr-muted);
88
+ background: var(--pr-quiet);
89
+ border: 1px solid var(--pr-line);
90
+ }
91
+
92
+ .badgeDanger {
93
+ color: #fff;
94
+ background: var(--pr-danger);
95
+ border: 1px solid transparent;
96
+ }
@@ -0,0 +1,49 @@
1
+ /**
2
+ * The surface language the page primitives are drawn in.
3
+ *
4
+ * StatStrip, FactsBand, BandedList, PageHeader and TabStrip were extracted from
5
+ * five hand-rolled CRM screens that each declared their own `--sb-*` / `--c-*` /
6
+ * `--zd-*` namespace on the page root and inherited it down. The values were
7
+ * not identical between them — one page's hairline was `--border-color`, the
8
+ * next mixed 12% of the body ink — so a primitive that read the app tokens
9
+ * directly would have redrawn half of those screens the moment it landed.
10
+ *
11
+ * Hence two levels. Each local `--pr-*` alias resolves a PUBLIC `--vs-*`
12
+ * override first and falls back to the app's own token, so:
13
+ *
14
+ * - a project that defines nothing gets a plain, working component;
15
+ * - a page that already has a palette sets `--vs-line`, `--vs-surface` and
16
+ * the rest ONCE on its own root and every primitive inside it follows.
17
+ *
18
+ * The mixin is applied to each component's ROOT class rather than to a shared
19
+ * `:root` block: CSS-module scoping means there is no shared block to write to,
20
+ * and a Stat rendered outside a StatStrip must still resolve its colours.
21
+ *
22
+ * A local alias may not be declared and read on the same element by an override
23
+ * — that is why the override is `var(--vs-*)` inside the alias rather than the
24
+ * alias being redeclared: an inherited custom property loses to a declaration
25
+ * on the element itself.
26
+ */
27
+
28
+ @mixin surface-tokens {
29
+ --pr-surface: var(--vs-surface, var(--background-color, #fff));
30
+ // The ground the page sits on, which is NOT the card surface — needed by
31
+ // anything that has to become opaque against the page itself, a sticky
32
+ // heading with no card behind it being the case that forced it.
33
+ --pr-page: var(--vs-page, var(--bg-color, #fff));
34
+ --pr-ink: var(--vs-ink, var(--paragraph-color, #2b2b2b));
35
+ --pr-heading: var(--vs-heading, var(--primary-color, #1b3933));
36
+ --pr-muted: var(--vs-muted, var(--muted-color, rgba(43 43 43 / 68%)));
37
+ --pr-line: var(--vs-line, var(--border-color, #e1e1e1));
38
+ --pr-quiet: var(
39
+ --vs-quiet,
40
+ color-mix(in srgb, var(--pr-ink) 4%, var(--pr-surface))
41
+ );
42
+ --pr-accent: var(--vs-accent, var(--highlight-color, #3cbf7d));
43
+ // There is no amber in the house palette — it is a green and a red — so the
44
+ // "worth a look, not yet wrong" step is named here once.
45
+ --pr-warn: var(--vs-warn, #c8811b);
46
+ --pr-danger: var(--vs-danger, var(--danger-color, #be3e3e));
47
+ --pr-radius: var(--vs-radius, var(--card-radius, 10px));
48
+ --pr-shadow: var(--vs-shadow, none);
49
+ }
package/src/index.js CHANGED
@@ -178,6 +178,7 @@ import {
178
178
  clientRecipient,
179
179
  conversationCountLabel,
180
180
  describeTransport,
181
+ displayNumber,
181
182
  fillTemplate,
182
183
  groupMessagesByDay,
183
184
  initialsFor,
@@ -189,7 +190,9 @@ import {
189
190
  pickInitialThread,
190
191
  relativeTime,
191
192
  segmentCount,
193
+ smsHref,
192
194
  splitPersonName,
195
+ telHref,
193
196
  threadDisplayName,
194
197
  toE164,
195
198
  } from './components/sms/smsHelpers';
@@ -247,6 +250,30 @@ import SectionTypeSelector from './components/proposal/SectionTypeSelector';
247
250
  import VariableInserter from './components/proposal/VariableInserter';
248
251
  import ProposalTemplatePreview from './components/proposal/ProposalTemplatePreview';
249
252
 
253
+ /**
254
+ * Page primitives.
255
+ *
256
+ * The scaffolding a bespoke screen is built out of — the strip of figures over a
257
+ * list, the underline tabs, the urgency bands, the header row and the band of
258
+ * facts under a record's title. Every one of them was written by hand on three
259
+ * or more CRM screens first, and had already drifted between them; see
260
+ * `styles/_surface.scss` for the two-level token scheme that lets a page with an
261
+ * existing palette adopt them without redrawing itself.
262
+ */
263
+ import AnchoredMenu from './components/layout/AnchoredMenu';
264
+ import BandedList, { Band } from './components/layout/BandedList';
265
+ import FactsBand, { Fact } from './components/layout/FactsBand';
266
+ import PageHeader from './components/layout/PageHeader';
267
+ import ScrollRegion from './components/layout/ScrollRegion';
268
+ import StatStrip, { Stat } from './components/layout/StatStrip';
269
+ import useContainedHeight from './components/layout/useContainedHeight';
270
+ import TabStrip from './components/navigation/TabStrip';
271
+
272
+ // Copying, and what to do when the browser will not. Named for the vault
273
+ // because that is where it was written; nothing in it is vault-specific, and
274
+ // every screen with a copy button was otherwise restating it.
275
+ import { canCopy, copyText } from './components/vault/vaultClipboard';
276
+
250
277
  /** OAuth Components */
251
278
  import OAuthManager from './components/oauth/OAuthManager';
252
279
  import OAuthProviderCard from './components/oauth/OAuthProviderCard';
@@ -339,6 +366,26 @@ export {
339
366
  useSmsLive,
340
367
  UniversalSearch,
341
368
  useSmsThreads,
369
+ // Phone numbers, for display and for dialling. Beside the SMS module's own
370
+ // normaliser because that is the formatter they are built on.
371
+ displayNumber,
372
+ smsHref,
373
+ telHref,
374
+ // Page primitives.
375
+ AnchoredMenu,
376
+ Band,
377
+ BandedList,
378
+ Fact,
379
+ FactsBand,
380
+ PageHeader,
381
+ ScrollRegion,
382
+ Stat,
383
+ StatStrip,
384
+ TabStrip,
385
+ useContainedHeight,
386
+ // Clipboard.
387
+ canCopy,
388
+ copyText,
342
389
  CameraPlacement,
343
390
  CellWithTooltip,
344
391
  clampOtp,