@deadragdoll/reactnu 0.1.24 → 0.1.49

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/dist/styles.css CHANGED
@@ -8,10 +8,12 @@
8
8
  --nu-color-panel: #6262e3;
9
9
  --nu-color-panel-inset: #6262e3;
10
10
  --nu-color-title-bg: #ffffff;
11
+ --nu-color-menu: #ffffff;
11
12
  --nu-color-title-text: #1f36bc;
12
13
  --nu-text-primary: #ffffff;
13
14
  --nu-text-muted: #dfe3ff;
14
15
  --nu-text-inverse: #000000;
16
+ --nu-main-menu-text: #000000;
15
17
  --nu-text-accent: #ffff55;
16
18
  --nu-text-hotkey: #ff5555;
17
19
  --nu-color-button-face: #ffffff;
@@ -48,6 +50,9 @@
48
50
  --nu-window-shadow-bg: rgba(0, 0, 0, 0.38);
49
51
  --nu-window-shadow-offset-x: 1ch;
50
52
  --nu-window-shadow-offset-y: 1em;
53
+ --nu-window-title-controls-width: 7.8ch;
54
+ --nu-window-title-height: calc(1em + 3px);
55
+ --nu-window-status-height: 1.35rem;
51
56
  --nu-window-inactive-overlay: rgba(0, 0, 0, 0.22);
52
57
  --nu-window-inactive-border: color-mix(
53
58
  in srgb,
@@ -55,10 +60,32 @@
55
60
  var(--nu-border-dark)
56
61
  );
57
62
  --nu-window-modal-backdrop: rgba(0, 0, 0, 0.32);
63
+ --nu-app-bar-height: 2.1rem;
64
+ --nu-app-bar-padding: 0.2rem 0.35rem;
58
65
  --nu-scrollbar-track: transparent;
59
66
  --nu-scrollbar-thumb: var(--nu-color-button-face);
60
67
  --nu-scrollbar-button-bg: var(--nu-color-button-face);
61
68
  --nu-scrollbar-button-size: 0.875rem;
69
+ --nu-icon-grid-icon-background: transparent;
70
+ --nu-icon-grid-icon-border-color: transparent;
71
+ --nu-icon-grid-icon-border-style: solid;
72
+ --nu-icon-grid-icon-opacity: 1;
73
+ --nu-icon-grid-icon-selected-background: var(--nu-color-title-bg);
74
+ --nu-icon-grid-icon-inactive-selected-background: color-mix(
75
+ in srgb,
76
+ var(--nu-icon-grid-icon-selected-background) 65%,
77
+ var(--nu-color-app-bg-alt)
78
+ );
79
+ --nu-icon-grid-icon-selected-border-color: var(--nu-focus-color);
80
+ --nu-icon-grid-icon-selected-border-style: solid;
81
+ --nu-icon-grid-icon-selected-opacity: 1;
82
+ --nu-icon-grid-icon-focus-background: var(--nu-color-title-bg);
83
+ --nu-icon-grid-icon-focus-border-color: var(--nu-focus-color);
84
+ --nu-icon-grid-icon-focus-border-style: solid;
85
+ --nu-icon-grid-icon-focus-opacity: 1;
86
+ --nu-icon-grid-icon-disabled-opacity: 0.6;
87
+ --nu-icon-grid-icon-drag-opacity: 0.35;
88
+ --nu-icon-grid-icon-drag-preview-opacity: 0.85;
62
89
  --nu-scrollbar-arrow-up: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpolygon points='8,2 1,12 15,12' fill='black'/%3E%3C/svg%3E");
63
90
  --nu-scrollbar-arrow-down: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpolygon points='1,4 15,4 8,14' fill='black'/%3E%3C/svg%3E");
64
91
  --nu-scrollbar-arrow-left: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpolygon points='2,8 12,1 12,15' fill='black'/%3E%3C/svg%3E");
@@ -559,7 +586,13 @@
559
586
  width: 6rem;
560
587
  height: 6rem;
561
588
  padding: 0.25rem;
562
- border: 1px solid transparent;
589
+ border: 1px var(--nu-icon-grid-icon-border-style)
590
+ var(--nu-icon-grid-icon-border-color);
591
+ --nu-icon-grid-icon-current-background: var(--nu-icon-grid-icon-background);
592
+ --nu-icon-grid-icon-current-background-opacity: var(
593
+ --nu-icon-grid-icon-opacity
594
+ );
595
+ isolation: isolate;
563
596
  background: transparent;
564
597
  color: var(--nu-text-primary);
565
598
  font: inherit;
@@ -568,19 +601,43 @@
568
601
  cursor: default;
569
602
  touch-action: none;
570
603
  }
571
- .nu-icon-grid__icon:focus-visible,
604
+ .nu-icon-grid__icon::before {
605
+ content: "";
606
+ position: absolute;
607
+ z-index: -1;
608
+ inset: 0;
609
+ background: var(--nu-icon-grid-icon-current-background);
610
+ opacity: var(--nu-icon-grid-icon-current-background-opacity);
611
+ pointer-events: none;
612
+ }
572
613
  .nu-icon-grid__icon[data-selected] {
573
- border-color: var(--nu-border-light);
574
- outline: 1px solid var(--nu-border-dark);
575
- outline-offset: -3px;
576
- background: var(--nu-color-title-bg);
614
+ --nu-icon-grid-icon-current-background: var(
615
+ --nu-icon-grid-icon-selected-background
616
+ );
617
+ --nu-icon-grid-icon-current-background-opacity: var(
618
+ --nu-icon-grid-icon-selected-opacity
619
+ );
620
+ border-color: var(--nu-icon-grid-icon-selected-border-color);
621
+ border-style: var(--nu-icon-grid-icon-selected-border-style);
622
+ color: var(--nu-text-inverse);
623
+ }
624
+ .nu-icon-grid__icon:focus {
625
+ --nu-icon-grid-icon-current-background: var(
626
+ --nu-icon-grid-icon-focus-background
627
+ );
628
+ --nu-icon-grid-icon-current-background-opacity: var(
629
+ --nu-icon-grid-icon-focus-opacity
630
+ );
631
+ border-color: var(--nu-icon-grid-icon-focus-border-color);
632
+ border-style: var(--nu-icon-grid-icon-focus-border-style);
577
633
  color: var(--nu-text-inverse);
578
634
  }
579
635
  .nu-icon-grid__icon[data-dragging] {
580
636
  z-index: 1;
637
+ opacity: var(--nu-icon-grid-icon-drag-opacity);
581
638
  }
582
639
  .nu-icon-grid__icon:disabled {
583
- opacity: 0.6;
640
+ opacity: var(--nu-icon-grid-icon-disabled-opacity);
584
641
  }
585
642
  .nu-icon-grid__glyph {
586
643
  display: inline-flex;
@@ -609,6 +666,18 @@
609
666
  -webkit-line-clamp: 2;
610
667
  line-clamp: 2;
611
668
  }
669
+ .nu-window:not([data-active]) .nu-icon-grid__icon[data-selected],
670
+ .nu-window:not([data-active]) .nu-icon-grid__icon:focus {
671
+ --nu-icon-grid-icon-current-background: var(
672
+ --nu-icon-grid-icon-inactive-selected-background
673
+ );
674
+ --nu-icon-grid-icon-current-background-opacity: var(
675
+ --nu-icon-grid-icon-selected-opacity
676
+ );
677
+ border-color: var(--nu-icon-grid-icon-selected-border-color);
678
+ border-style: var(--nu-icon-grid-icon-selected-border-style);
679
+ color: var(--nu-text-inverse);
680
+ }
612
681
  .nu-listbox {
613
682
  flex: 1 1 auto;
614
683
  align-self: stretch;
@@ -973,8 +1042,8 @@
973
1042
  position: relative;
974
1043
  z-index: 1100;
975
1044
  min-width: 0;
976
- background: var(--nu-color-title-bg);
977
- color: var(--nu-text-inverse);
1045
+ background: var(--nu-color-menu);
1046
+ color: var(--nu-main-menu-text);
978
1047
  font-family: var(--nu-font-body);
979
1048
  }
980
1049
  .nu-main-menu__bar {
@@ -1048,8 +1117,8 @@
1048
1117
  min-width: 13rem;
1049
1118
  padding: 0.2rem 0;
1050
1119
  border: 1px solid var(--nu-border-dark);
1051
- background: var(--nu-color-button-face);
1052
- color: var(--nu-text-inverse);
1120
+ background: var(--nu-color-menu);
1121
+ color: var(--nu-main-menu-text);
1053
1122
  }
1054
1123
  .nu-main-menu__popup--submenu {
1055
1124
  position: relative;
@@ -1100,10 +1169,11 @@
1100
1169
  justify-content: center;
1101
1170
  inline-size: var(--nu-glyph-cell-size);
1102
1171
  block-size: var(--nu-glyph-cell-size);
1103
- background: var(--nu-color-button-face);
1172
+ border: 1px solid var(--nu-border-dark);
1173
+ background: transparent;
1104
1174
  }
1105
1175
  .nu-main-menu__check-box[data-unchecked-shape="none"] {
1106
- background: transparent;
1176
+ border-color: transparent;
1107
1177
  }
1108
1178
  .nu-main-menu__label {
1109
1179
  min-width: 0;
@@ -1113,7 +1183,7 @@
1113
1183
  }
1114
1184
  .nu-main-menu__shortcut {
1115
1185
  margin-left: 1rem;
1116
- color: var(--nu-text-inverse);
1186
+ color: var(--nu-main-menu-text);
1117
1187
  }
1118
1188
  .nu-main-menu__item--open .nu-main-menu__shortcut,
1119
1189
  .nu-main-menu__item:not(.nu-main-menu__item--disabled):hover
@@ -3384,8 +3454,6 @@
3384
3454
  padding: var(--nu-space-lg);
3385
3455
  }
3386
3456
  .nu-window {
3387
- --nu-window-title-controls-width: 7.8ch;
3388
- --nu-window-title-height: calc(1em + 3px);
3389
3457
  position: relative;
3390
3458
  display: flex;
3391
3459
  flex-direction: column;
@@ -3497,11 +3565,6 @@
3497
3565
  outline: none;
3498
3566
  box-shadow: none;
3499
3567
  }
3500
- .nu-window__title-button--close {
3501
- background: var(--nu-color-title-bg);
3502
- border: 1px solid var(--nu-border-light);
3503
- color: var(--nu-text-inverse);
3504
- }
3505
3568
  .nu-window__body {
3506
3569
  min-width: 0;
3507
3570
  min-height: 0;
@@ -3575,7 +3638,7 @@
3575
3638
  align-items: center;
3576
3639
  justify-content: space-between;
3577
3640
  gap: var(--nu-space-md);
3578
- min-height: 1.35rem;
3641
+ min-height: var(--nu-window-status-height);
3579
3642
  padding: 0 0.45rem;
3580
3643
  background: var(--nu-color-title-bg);
3581
3644
  color: var(--nu-color-title-text);
@@ -3672,8 +3735,8 @@ button.nu-app-bar-item:focus-visible {
3672
3735
  display: flex;
3673
3736
  align-items: center;
3674
3737
  gap: 0.35rem;
3675
- min-height: 2.1rem;
3676
- padding: 0.2rem 0.35rem;
3738
+ min-height: var(--nu-app-bar-height);
3739
+ padding: var(--nu-app-bar-padding);
3677
3740
  background: var(--nu-color-shell);
3678
3741
  box-shadow: inset 0 1px 0 var(--nu-border-light);
3679
3742
  pointer-events: auto;
@@ -1,4 +1,4 @@
1
- import { PropsWithChildren } from "react";
1
+ import { CSSProperties, PropsWithChildren } from "react";
2
2
  import { NuCrtGlitchProps } from "../components/CrtGlitch";
3
3
  import { NuDesktopPatternMode, NuThemeDefinition, NuThemeName } from "./themes";
4
4
  type NuThemeProviderProps = PropsWithChildren<{
@@ -20,7 +20,8 @@ type NuThemeProviderProps = PropsWithChildren<{
20
20
  onDesktopPatternModeChange?: (desktopPatternMode: NuDesktopPatternMode) => void;
21
21
  onFontFamilyChange?: (fontFamily: string) => void;
22
22
  onFontSizeChange?: (fontSize: number) => void;
23
+ style?: CSSProperties;
23
24
  theme?: NuThemeName | NuThemeDefinition;
24
25
  }>;
25
- export declare function NuThemeProvider({ children, className, crtGlitch, defaultDesktopPatternMode, defaultFontFamily, defaultFontSize, defaultTheme, desktopPatternMode, fontFamily, fontSize, onDesktopPatternModeChange, onFontFamilyChange, onFontSizeChange, onThemeChange, theme }: NuThemeProviderProps): import("react/jsx-runtime").JSX.Element;
26
+ export declare function NuThemeProvider({ children, className, crtGlitch, defaultDesktopPatternMode, defaultFontFamily, defaultFontSize, defaultTheme, desktopPatternMode, fontFamily, fontSize, onDesktopPatternModeChange, onFontFamilyChange, onFontSizeChange, onThemeChange, style, theme }: NuThemeProviderProps): import("react/jsx-runtime").JSX.Element;
26
27
  export {};
@@ -10,10 +10,12 @@ export type NuThemeTokens = {
10
10
  panelBackground: string;
11
11
  panelInsetBackground: string;
12
12
  titleBackground: string;
13
+ menuBackground: string;
13
14
  titleText: string;
14
15
  textPrimary: string;
15
16
  textMuted: string;
16
17
  textInverse: string;
18
+ mainMenuText: string;
17
19
  textAccent: string;
18
20
  textHotkey: string;
19
21
  buttonFace: string;
@@ -42,6 +44,7 @@ export declare const nuThemes: {
42
44
  readonly amber: NuThemeDefinition;
43
45
  readonly phosphor: NuThemeDefinition;
44
46
  readonly midnight: NuThemeDefinition;
47
+ readonly grayscale: NuThemeDefinition;
45
48
  };
46
49
  export type NuThemeName = keyof typeof nuThemes;
47
50
  export declare function isNuThemeName(value: string): value is NuThemeName;
package/docs/IconGrid.md CHANGED
@@ -61,6 +61,16 @@ function Applications() {
61
61
  - `defaultArrangeMode="columns" | "rows" | "name"` arranges seeded icons
62
62
  once, after the grid measures a non-zero available area. This prevents icons
63
63
  from initially landing outside a bounded `Window`.
64
+ - `dropTarget` makes a grid eligible as a non-interactive drop surface when it
65
+ sits behind other content. Pair it with `accepts(icon)` to filter incoming
66
+ icons before `onIconDrop` runs. This is useful for a full desktop target that
67
+ uses `pointer-events: none` and therefore does not interfere with the
68
+ desktop's own controls.
69
+ - Drag an icon to another `NuIconGrid`, including one in a different managed
70
+ window, to transfer it between providers. `onIconDrop(icon, context)` runs on
71
+ the target first and may return `false` to reject the transfer;
72
+ `onIconMoveOut(icon, context)` runs on the source after a successful transfer.
73
+ `context` exposes both managers, grids, and the target `position`.
64
74
 
65
75
  `useNuIconManager()`
66
76
 
@@ -70,12 +80,24 @@ function Applications() {
70
80
  - `arrangeIcons("columns" | "rows" | "name")` lays out the current icons.
71
81
 
72
82
  Each icon can have `onClick`, `onDoubleClick`, `onContextMenu`,
73
- `onPositionChange`, and its own `contextMenuItems`.
83
+ `onPositionChange`, its own `contextMenuItems`, and an application-defined
84
+ `payload` that is preserved during a cross-grid transfer.
85
+
86
+ ## Theme tokens
87
+
88
+ The default, selected, and focus IconGrid states each expose `background`,
89
+ `border-color`, `border-style`, and `opacity` values under the
90
+ `--nu-icon-grid-icon-*` prefix. The resting background defaults to
91
+ `transparent`; those opacity values affect only the background fill. Drag,
92
+ drag-preview, and disabled opacity also have dedicated
93
+ tokens. In an inactive managed window, the selected border remains visible
94
+ while `--nu-icon-grid-icon-inactive-selected-background` supplies a calmer
95
+ fill.
74
96
 
75
97
  ## Interaction and accessibility
76
98
 
77
- - Drag icons to move them inside the grid; their resulting positions remain in
78
- the provider for the lifetime of that provider.
99
+ - Drag icons to move them inside the grid, or onto another icon grid to transfer
100
+ them. A duplicate ID or a non-grid target leaves the source icon in place.
79
101
  - A click selects an icon. Enter and Space activate its normal button action.
80
102
  - Right-click opens the icon menu; `Shift+F10` and the Context Menu key open it
81
103
  from the keyboard.
package/docs/ListBox.md CHANGED
@@ -20,6 +20,7 @@
20
20
  - `checkedIds?: string[]`
21
21
  - `onItemCheckChange?: (item, group, checked) => void`
22
22
  - `onItemDoubleClick?: (item, group) => void`
23
+ - `onPopupMenu?: (event, item, group) => void`
23
24
  - `rightCheckBox?: boolean`
24
25
  - `emptyText?: string`
25
26
 
@@ -33,3 +34,6 @@
33
34
  ## Notes
34
35
 
35
36
  - The same data format supports both grouped and flat lists by using `category: null`.
37
+ - Right-clicking an enabled item activates it, prevents the browser context menu,
38
+ and calls `onPopupMenu`. Use the event coordinates to open an application-owned
39
+ `PopupMenu` or another contextual action surface.
@@ -47,6 +47,8 @@ Supported props:
47
47
  - `onFontSizeChange?: (fontSize) => void`
48
48
  - `crtGlitch?: boolean | NuCrtGlitchProps`
49
49
  enables the ambient CRT-glitch effect (see [CrtGlitch](./CrtGlitch.md)). `true` uses its defaults; pass an options object to tune them. Defaults to `false` — opt-in.
50
+ - `style?: CSSProperties`
51
+ applies scoped CSS custom-property overrides after the resolved theme tokens. Popup portals mirror the computed values from the theme root.
50
52
 
51
53
  `useNuTheme()` exposes:
52
54
 
@@ -59,7 +61,9 @@ Supported props:
59
61
 
60
62
  ## Notes
61
63
 
62
- - Built-in themes are `classic` (default), `amber`, `phosphor`, and `midnight`.
64
+ - Built-in themes are `classic` (default), `amber`, `phosphor`, `midnight`, and
65
+ `grayscale`.
63
66
  - `midnight` is a restrained dark blue-gray palette with pale chrome and yellow focus accents.
67
+ - `grayscale` is a high-contrast monochrome monitor palette. Semantic button variants use brightness rather than hue.
64
68
  - Theme tokens drive desktop, windowing, controls, selection, and inactive overlays.
65
69
  - Provider typography affects preview content, managed windows, dialogs, menus, and popup portals rendered inside the theme scope.
package/docs/PopupMenu.md CHANGED
@@ -32,7 +32,7 @@ const popupMenu = usePopupMenu();
32
32
  - `open`
33
33
  - `setOpen`
34
34
  - `close()`
35
- - `openAtPoint(x, y)`
35
+ - `openAtPoint(x, y, themeSource?)`
36
36
  - `openAtElement(element)`
37
37
  - `openFromClick(event)`
38
38
  - `openFromContextMenu(event)`
@@ -40,3 +40,6 @@ const popupMenu = usePopupMenu();
40
40
  ## Notes
41
41
 
42
42
  - Popup menus render through a portal and are not clipped by local overflow containers.
43
+ - `openFromContextMenu(event)` carries the event target into the portal, so
44
+ context menus inherit tokens from the nearest `NuThemeProvider`. Pass the
45
+ optional `themeSource` to `openAtPoint(...)` when opening one manually.
@@ -97,7 +97,7 @@ If a component needs a new reusable visual value, add it there first instead of
97
97
  - Shadows:
98
98
  - If it is raised, is it reusing button/window shadow logic instead of inventing a new one?
99
99
  - Theme coverage:
100
- - Would the component still read correctly in `classic`, `amber`, `phosphor`, and `midnight`?
100
+ - Would the component still read correctly in `classic`, `amber`, `phosphor`, `midnight`, and `grayscale`?
101
101
  - Hardcoded values:
102
102
  - Are any colors or dimensions local only because they are truly component-specific?
103
103
 
@@ -54,6 +54,7 @@ const columns: TreeListColumn<Node>[] = [
54
54
  - `defaultExpandedIds`
55
55
  - `onItemSelect`
56
56
  - `onItemDoubleClick`
57
+ - `onPopupMenu`
57
58
  - `onItemCheckChange`
58
59
  - `onExpandedIdsChange`
59
60
  - `onActiveItemChange`
@@ -82,3 +83,7 @@ const columns: TreeListColumn<Node>[] = [
82
83
  - Columns auto-size from visible content by default.
83
84
  - `minWidth` sets a lower bound for auto-sized and resized columns.
84
85
  - Columns are resizable unless `resizable={false}` is set on a column.
86
+ - Right-clicking an enabled row activates it and calls
87
+ `onPopupMenu(event, item, context)`. `context` has the same `depth`, `isLeaf`,
88
+ `item`, and `rowIndex` fields as `getCellContent`; the host owns rendering the
89
+ resulting menu.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deadragdoll/reactnu",
3
- "version": "0.1.24",
3
+ "version": "0.1.49",
4
4
  "private": false,
5
5
  "description": "React components with a precise DOS-era utility interface.",
6
6
  "license": "MIT",