@helloimjolopez-pathway/pathway-components 0.7.2 → 0.8.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/components/_shared/focus-ring.js +45 -0
- package/components/module-icon/module-icon.jsx +11 -9
- package/components/nav-shell/nav-shell.jsx +6 -0
- package/components/sidenav/accounting-side-nav.jsx +3 -0
- package/components/sidenav/sidenav.jsx +7 -2
- package/components/top-nav/top-nav.jsx +16 -8
- package/dist/pathway-accounting-sidenav.js +6 -6
- package/dist/pathway-components.js +58 -58
- package/dist/pathway-sidenav.js +6 -6
- package/package.json +1 -1
- package/components/bottom-sheet/bottom-sheet-demo.jsx +0 -145
- package/components/nav-shell/nav-shell-demo.jsx +0 -59
- package/components/radio/radio-demo.jsx +0 -175
- package/components/scroll-to-enable/scroll-to-enable-demo.jsx +0 -125
- package/components/toggle-button/toggle-button-demo.jsx +0 -193
- package/components/tooltip/tooltip-demo.jsx +0 -149
- package/components/top-nav/top-nav-demo.jsx +0 -79
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Keyboard focus ring shared by controls that style themselves inline.
|
|
3
|
+
*
|
|
4
|
+
* One ring for every Pathway control: FocusRing/Width of Stroke/FocusRing/Base,
|
|
5
|
+
* drawn as an outline that hugs the element's edge (no gap), the way Figma draws
|
|
6
|
+
* it: an outside stroke on the control's own container in each Focused variant.
|
|
7
|
+
* Menu rows take the inset variant, as PopoverMenu.Item does in Figma. Where the
|
|
8
|
+
* Figma ring takes the place of the control's border, FOCUS_RING_REPLACE also
|
|
9
|
+
* hides that border while the ring shows.
|
|
10
|
+
*
|
|
11
|
+
* A stylesheet rule rather than inline style, because :focus-visible (keyboard
|
|
12
|
+
* focus only, never a mouse click) can only be matched by a selector.
|
|
13
|
+
*/
|
|
14
|
+
export const FOCUS_RING = "pw-focus-ring";
|
|
15
|
+
export const FOCUS_RING_INSET = "pw-focus-ring pw-focus-ring--inset";
|
|
16
|
+
// The ring replaces the control's border (drawn as an inset box-shadow or a CSS
|
|
17
|
+
// border). A control that also carries an elevation keeps it by setting
|
|
18
|
+
// --pw-ring-keep-shadow in its own style.
|
|
19
|
+
export const FOCUS_RING_REPLACE = "pw-focus-ring pw-focus-ring--replace";
|
|
20
|
+
// For a button that is a bigger touch target than what it draws (an action
|
|
21
|
+
// icon's 44 target around its 28 layer, the 48 profile button around its 32
|
|
22
|
+
// avatar): the ring goes on the drawn layer, marked data-pw-ring-target, as
|
|
23
|
+
// the Figma Focus Ring layer does.
|
|
24
|
+
export const FOCUS_RING_LAYER = "pw-focus-ring pw-focus-ring--layer";
|
|
25
|
+
|
|
26
|
+
// Also exported so a shadow root can carry it: a rule in document.head does not
|
|
27
|
+
// reach inside one, so the custom elements append this to their own styles.
|
|
28
|
+
export const FOCUS_RING_CSS = `
|
|
29
|
+
.pw-focus-ring:focus{outline:none}
|
|
30
|
+
.pw-focus-ring:focus-visible{outline:var(--contextual-layout-units-focusring-width) solid var(--semantic-color-stroke-focusring-base);outline-offset:0}
|
|
31
|
+
.pw-focus-ring--inset:focus-visible{outline-offset:calc(-1 * var(--contextual-layout-units-focusring-width))}
|
|
32
|
+
.pw-focus-ring--replace:focus-visible{border-color:transparent !important;box-shadow:var(--pw-ring-keep-shadow, none) !important}
|
|
33
|
+
.pw-focus-ring--layer:focus-visible{outline:none}
|
|
34
|
+
.pw-focus-ring--layer:focus-visible [data-pw-ring-target]{outline:var(--contextual-layout-units-focusring-width) solid var(--semantic-color-stroke-focusring-base);outline-offset:0}
|
|
35
|
+
`;
|
|
36
|
+
|
|
37
|
+
let injected = false;
|
|
38
|
+
export function injectFocusRing() {
|
|
39
|
+
if (injected || typeof document === "undefined") return;
|
|
40
|
+
injected = true;
|
|
41
|
+
const el = document.createElement("style");
|
|
42
|
+
el.setAttribute("data-pathway", "focus-ring");
|
|
43
|
+
el.textContent = FOCUS_RING_CSS;
|
|
44
|
+
document.head.appendChild(el);
|
|
45
|
+
}
|
|
@@ -385,26 +385,28 @@ export function ModuleIcon({
|
|
|
385
385
|
}
|
|
386
386
|
|
|
387
387
|
/**
|
|
388
|
-
* Module.Mark
|
|
389
|
-
*
|
|
390
|
-
*
|
|
391
|
-
*
|
|
392
|
-
*
|
|
388
|
+
* Module.Mark (Figma): the module's mark in a 22 tinted box beside its name.
|
|
389
|
+
* Used in the SideNav header of a module shell (SideNav.Local.Accounting,
|
|
390
|
+
* read 2026-11-12).
|
|
391
|
+
* box 22, CornerRadius/Small, Fill/Static/Brand/Faint
|
|
392
|
+
* glyph ModuleIcon at 16, mono, Fill/Static/Brand/Strongest
|
|
393
|
+
* label Text/Body/Small/Semibold: 14 / 600 / 20 / 0.3 (the repo's `spacious`),
|
|
394
|
+
* Fill/Static/Brand/Strongest, gap Gap/XXTight
|
|
393
395
|
*/
|
|
394
396
|
export function ModuleMark({ module, label }) {
|
|
395
397
|
const ink = "var(--semantic-color-fill-static-brand-strongest)";
|
|
396
398
|
return (
|
|
397
399
|
<span style={{ display: "inline-flex", alignItems: "center", minWidth: 0,
|
|
398
400
|
gap: "var(--semantic-layout-units-gap-xxtight)", paddingRight: "var(--semantic-layout-units-padding-xxxtight)" }}>
|
|
399
|
-
<span aria-hidden="true" style={{ width:
|
|
401
|
+
<span aria-hidden="true" style={{ width: 22, height: 22, flexShrink: 0, display: "inline-flex",
|
|
400
402
|
alignItems: "center", justifyContent: "center",
|
|
401
403
|
borderRadius: "var(--semantic-layout-units-cornerradius-small)",
|
|
402
404
|
background: "var(--semantic-color-fill-static-brand-faint)", color: ink }}>
|
|
403
|
-
<ModuleIcon module={module} size={
|
|
405
|
+
<ModuleIcon module={module} size={16} color="mono" />
|
|
404
406
|
</span>
|
|
405
407
|
<span style={{ fontFamily: "var(--semantic-type-family-brand)", fontWeight: "var(--semantic-type-weight-semibold)",
|
|
406
|
-
fontSize: "var(--semantic-type-font-size-
|
|
407
|
-
color: ink, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{label}</span>
|
|
408
|
+
fontSize: "var(--semantic-type-font-size-s)", lineHeight: "var(--semantic-type-line-height-s-single)",
|
|
409
|
+
letterSpacing: "var(--semantic-type-letter-spacing-spacious)", color: ink, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{label}</span>
|
|
408
410
|
</span>
|
|
409
411
|
);
|
|
410
412
|
}
|
|
@@ -89,6 +89,10 @@ export function NavShell({
|
|
|
89
89
|
user,
|
|
90
90
|
onModuleSelect,
|
|
91
91
|
onOrgSelect,
|
|
92
|
+
// TopNav Slot.RowEnd. Undefined follows the module: an appSwitcher may carry
|
|
93
|
+
// `topNav: { showSearch, showActions }` (Accounting turns both off).
|
|
94
|
+
showSearch,
|
|
95
|
+
showActions,
|
|
92
96
|
|
|
93
97
|
// SideNav
|
|
94
98
|
navItems,
|
|
@@ -233,6 +237,8 @@ export function NavShell({
|
|
|
233
237
|
onModuleSelect={onModuleSelect}
|
|
234
238
|
onOrgSelect={onOrgSelect}
|
|
235
239
|
onSideNavToggle={handleSideNavToggle}
|
|
240
|
+
showSearch={showSearch ?? (appSwitcher && appSwitcher.topNav && appSwitcher.topNav.showSearch) ?? true}
|
|
241
|
+
showActions={showActions ?? (appSwitcher && appSwitcher.topNav && appSwitcher.topNav.showActions) ?? true}
|
|
236
242
|
/>
|
|
237
243
|
</div>
|
|
238
244
|
|
|
@@ -51,6 +51,9 @@ export const ACCOUNTING_APP_SWITCHER = {
|
|
|
51
51
|
icon: "apps",
|
|
52
52
|
// The module's mark in the SideNav header (Figma Module.Mark, Accounting).
|
|
53
53
|
moduleMark: { module: "accounting", label: "Financials" },
|
|
54
|
+
// The Accounting TopNav is TopNav.Global with Search and Actions hidden
|
|
55
|
+
// (Figma 40007879:18072): module switcher, org switcher and profile only.
|
|
56
|
+
topNav: { showSearch: false, showActions: false },
|
|
54
57
|
// Title of the mobile BottomSheet (Figma "Financial Applications").
|
|
55
58
|
sheetTitle: "Financial Applications",
|
|
56
59
|
home: { id: "financials-home", label: "Financials Home",
|
|
@@ -1137,13 +1137,18 @@ export function SideNav({
|
|
|
1137
1137
|
// inset the thumb by 16px expanded / 12px on the 72px rail, badly off on the rail).
|
|
1138
1138
|
// negated through calc() because these are var() references, and negating
|
|
1139
1139
|
// a string would produce NaN
|
|
1140
|
-
marginRight: neg(collapsed ? L.navColPadH : L.navPadH)
|
|
1140
|
+
marginRight: neg(collapsed ? L.navColPadH : L.navPadH),
|
|
1141
|
+
// The rows' focus ring hugs their edge, outside it, so the scroll view
|
|
1142
|
+
// (which clips) starts one ring-width further left and pads it back:
|
|
1143
|
+
// nothing moves, and the ring's left edge is no longer cut off.
|
|
1144
|
+
marginLeft: neg("var(--contextual-layout-units-focusring-width)") }}
|
|
1141
1145
|
viewStyle={{ display: "flex", flexDirection: "column", gap: L.menuGap,
|
|
1142
1146
|
paddingTop: L.menuPadT,
|
|
1143
1147
|
// Re-add the content's right inset INSIDE the scroll view so items keep their
|
|
1144
1148
|
// position; only the thumb moves to the edge. (Spec rule: padding on viewStyle,
|
|
1145
1149
|
// never on the wrapper, keeps the thumb flush on any scroll surface.)
|
|
1146
|
-
paddingRight: collapsed ? L.navColPadH : L.navPadH
|
|
1150
|
+
paddingRight: collapsed ? L.navColPadH : L.navPadH,
|
|
1151
|
+
paddingLeft: "var(--contextual-layout-units-focusring-width)" }}
|
|
1147
1152
|
>
|
|
1148
1153
|
|
|
1149
1154
|
{appSwitcher && <AppSwitcher {...appSwitcher} collapsed={collapsed} />}
|
|
@@ -1030,6 +1030,10 @@ function TopNavNewCo({ navRef, className, breakpoint, org, user, openPanel, togg
|
|
|
1030
1030
|
* onSideNavToggle, () => void (mobile only)
|
|
1031
1031
|
* actions, Array<{icon, label, onClick}> Figma TopNav.Actions slot (default: two bells).
|
|
1032
1032
|
* On mobile, more than one folds into more_vert.
|
|
1033
|
+
* showSearch, boolean, default true. Figma TopNav.Search in Slot.RowEnd.
|
|
1034
|
+
* showActions, boolean, default true. Figma TopNav.Actions in Slot.RowEnd.
|
|
1035
|
+
* A module whose bar hides them in Figma turns them off
|
|
1036
|
+
* (Accounting, 40007879:18072: module, org and profile only).
|
|
1033
1037
|
* onNotifications, () => void
|
|
1034
1038
|
* onMore, () => void
|
|
1035
1039
|
* className, string
|
|
@@ -1054,6 +1058,8 @@ export function TopNav({
|
|
|
1054
1058
|
onSearchOpen,
|
|
1055
1059
|
onSideNavToggle,
|
|
1056
1060
|
actions,
|
|
1061
|
+
showSearch = true,
|
|
1062
|
+
showActions = true,
|
|
1057
1063
|
onNotifications,
|
|
1058
1064
|
onMore,
|
|
1059
1065
|
className = "",
|
|
@@ -1259,15 +1265,17 @@ export function TopNav({
|
|
|
1259
1265
|
{/* Collapsed search pill. Opening it triggers the full-bar takeover
|
|
1260
1266
|
overlay below (never an inline expand), so it cannot overlap the
|
|
1261
1267
|
OrgSwitcher. */}
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1268
|
+
{showSearch && (
|
|
1269
|
+
<TopNavSearch
|
|
1270
|
+
expanded={false}
|
|
1271
|
+
onExpandChange={setSearchExpanded}
|
|
1272
|
+
breakpoint={breakpoint}
|
|
1273
|
+
onSearchOpen={onSearchOpen}
|
|
1274
|
+
searchProps={{ value: searchQuery, onChange: setSearchQuery, onClear: () => setSearchQuery("") }}
|
|
1275
|
+
/>
|
|
1276
|
+
)}
|
|
1269
1277
|
|
|
1270
|
-
<TopNavActions breakpoint={breakpoint} actions={actions} onNotifications={onNotifications} onMore={onMore} />
|
|
1278
|
+
{showActions && <TopNavActions breakpoint={breakpoint} actions={actions} onNotifications={onNotifications} onMore={onMore} />}
|
|
1271
1279
|
|
|
1272
1280
|
<TopNavProfile
|
|
1273
1281
|
user={user}
|