@jsenv/navi 0.20.11 → 0.20.13

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 CHANGED
@@ -1,110 +1,41 @@
1
1
  # @jsenv/navi
2
2
 
3
- > ⚠️ **Work in Progress** - This framework is being actively developed and APIs may change.
3
+ A frontend framework for building modern web applications, focused on routing, async data, and UI components.
4
4
 
5
- > Helps to build modern web application
5
+ ## Routing
6
6
 
7
- **@jsenv/navi** is a comprehensive frontend framework designed to simplify navigation, state management, and UI development. Named after Navi, the fairy guide from Zelda, it helps you navigate through the complexities of building modern web applications.
7
+ Routing is signal-based, which means URL state — including search params — can be bound to signals with two-way synchronization. Change a signal, the URL updates. Navigate to a URL, the signals update. Search params can also be validated and typed, so you always work with the right shape of data.
8
8
 
9
- ## What it provides
9
+ Routes are flexible: you can create route groups to share logic, state, or UI across multiple routes. Nested routing is supported, and the structure naturally maps to how your application is organized.
10
10
 
11
- ### 🧭 Navigation & Routing
11
+ ## Actions
12
12
 
13
- - **Client-side routing** with URL synchronization and code splitting
14
- - **Link components** that enhance standard anchor tags
15
- - **Route components** with nested routing support
16
- - **History management** and navigation state hooks
17
- - **Keyboard navigation** with keyboard shortcuts
13
+ Actions are async operations with lifecycle management — pending, success, error. You can declare actions that run when navigating to a route, and any component can subscribe to them via `useAsyncData` to reflect what is happening: loading states, results, errors. No manual wiring.
18
14
 
19
- ### 🔄 State Management & Actions
15
+ ## Layout & Typography
20
16
 
21
- - **Signal-based reactive state** with local storage integration
22
- - **Action system** for async operations with lifecycle management
23
- - **Resource management** with caching and request deduplication
24
- - **Form validation** with custom constraints and real-time feedback
25
- - **State synchronization** utilities and debugging tools
17
+ **`Box`** is the main layout primitive. It wraps CSS Flexbox with a friendlier API: `flex` for horizontal layout, `flex="y"` for vertical (no more guessing what `flex-direction: column` does visually). Supports `grid`, `inline`, alignment via `alignX`/`alignY`, and spacing props.
26
18
 
27
- ### 🎨 UI Components
19
+ **`Text`** and related components (`Title`, `Paragraph`, `Code`, `Caption`) handle typography consistently across the app.
28
20
 
29
- #### Form & Input Controls
21
+ ## Icons
30
22
 
31
- - **Input, Button, Label** - Enhanced form elements with validation
32
- - **Radio/RadioList, Checkbox/CheckboxList** - Selection controls with icons and custom appearances
33
- - **Select, Form** - Complete form building blocks
34
- - **Field validation** with constraint validation API integration
35
- - **Editable components** with inline editing capabilities
23
+ Icons are a piece that is often missing or painful in web projects. The `Icon` component makes icons behave like text — they scale with font size, inherit color, and align naturally in any layout. No sizing hacks, no SVG wrangling.
36
24
 
37
- #### Data Visualization
25
+ ## Fields & Forms
38
26
 
39
- - **Table** - Complete table system with selection, sorting, and column management
40
- - **Selection system** - Multi-selection with keyboard shortcuts
41
- - **Error boundaries** - Graceful error handling components
27
+ UI field components (`Input`, `Select`, `Checkbox`, `Radio`, etc.) accept an `action` prop — the standard way to respond to user interaction. Composing fields into forms is natural, and form submission flows through the same action system.
42
28
 
43
- #### Layout & Structure
29
+ Validation goes beyond native browser constraints: custom rules, better error positioning, real-time feedback, and a UX that doesn't punish users before they've finished typing.
44
30
 
45
- - **Box** - Flexible layout container with spacing and alignment
46
- - **Details** - Collapsible content with navigation state persistence
47
- - **Dialog, Viewport layouts** - Modal and full-screen layouts
48
- - **Separator** - Visual content dividers
49
- - **UI Transitions** - Smooth component transitions
31
+ ## Table
50
32
 
51
- #### Typography & Graphics
33
+ A capable `Table` component that handles what you'd expect from a spreadsheet-like interface: column management, sorting, multi-selection with keyboard shortcuts, and more.
52
34
 
53
- - **Text, Title, Paragraph** - Typography components with theming
54
- - **Code, Caption** - Specialized text displays
55
- - **Icon, Image, Svg** - Graphics with built-in icon library
56
- - **Badge, MessageBox** - Status and notification displays
57
- - **Address** - Semantic contact information component
35
+ ## Other
58
36
 
59
- #### Interactive Features
37
+ Dialogs, badges, details/collapsible, separators, keyboard shortcuts, popovers, copy-to-clipboard, and other utilities.
60
38
 
61
- - **Keyboard shortcuts** - Global and component-level hotkeys
62
- - **Focus management** - Accessibility-focused navigation
63
- - **Callouts & popovers** - Contextual overlays and tooltips
64
- - **Copy to clipboard** - One-click content copying
39
+ ---
65
40
 
66
- ## Quick Example
67
-
68
- ```jsx
69
- import { render } from "preact";
70
- import { Link, Button, Box } from "@jsenv/navi";
71
-
72
- const userSignal = signal();
73
- const requestUser = async ({ id }) => {
74
- const response = await fetch(`/api/users/${id}`);
75
- const user = response.json();
76
- userSignal.value = user;
77
- };
78
-
79
- const App = () => {
80
- const user = userSignal.value;
81
-
82
- return (
83
- <Box row spacing="lg">
84
- <Link href="/profile">Go to Profile</Link>
85
-
86
- <Button
87
- action={async () => {
88
- await requestUser();
89
- }}
90
- >
91
- Load User Data
92
- </Button>
93
-
94
- {user && <div>Welcome, {user.name}!</div>}
95
- </Box>
96
- );
97
- };
98
-
99
- render(<App />, document.querySelector("#root"));
100
- ```
101
-
102
- ## Architecture
103
-
104
- The framework is built around three core concepts:
105
-
106
- 1. **Signals** - Reactive state primitives that automatically update the UI
107
- 2. **Actions** - Async operations with built-in lifecycle management
108
- 3. **Components** - Composable UI building blocks with consistent APIs
109
-
110
- This combination provides a powerful yet simple foundation for building interactive web applications that scale from simple pages to complex SPAs.
41
+ Named after Navi, the fairy guide from Zelda — it helps you navigate through the complexities of building modern web applications.
@@ -7424,8 +7424,12 @@ const updateStyle = (element, style, preventInitialTransition) => {
7424
7424
  // Apply all styles normally (excluding transition during anti-flicker)
7425
7425
  const keysToDelete = new Set(oldStyleKeySet);
7426
7426
  for (const key of styleKeySetToApply) {
7427
- keysToDelete.delete(key);
7428
7427
  const value = style[key];
7428
+ if (value === undefined || value === null) {
7429
+ // Treat undefined/null as "remove" — leave key in keysToDelete
7430
+ continue;
7431
+ }
7432
+ keysToDelete.delete(key);
7429
7433
  if (key.startsWith("--")) {
7430
7434
  element.style.setProperty(key, value);
7431
7435
  } else {
@@ -30186,25 +30190,19 @@ const BadgeStyleCSSVars$1 = {
30186
30190
  };
30187
30191
  const Badge = ({
30188
30192
  children,
30193
+ className,
30189
30194
  ...props
30190
30195
  }) => {
30191
30196
  const defaultRef = useRef();
30192
30197
  const ref = props.ref || defaultRef;
30193
30198
  useDarkBackgroundAttribute(ref);
30194
- return jsxs(Text, {
30199
+ return jsx(Text, {
30195
30200
  ref: ref,
30196
- className: "navi_badge",
30201
+ className: withPropsClassName("navi_badge", className),
30197
30202
  bold: true,
30198
30203
  ...props,
30199
30204
  styleCSSVars: BadgeStyleCSSVars$1,
30200
- spacing: "pre",
30201
- children: [jsx("span", {
30202
- style: "user-select: none",
30203
- children: "\u200B"
30204
- }), children, jsx("span", {
30205
- style: "user-select: none",
30206
- children: "\u200B"
30207
- })]
30205
+ children: children
30208
30206
  });
30209
30207
  };
30210
30208
 
@@ -30451,14 +30449,15 @@ const applyMaxToValue = (max, value) => {
30451
30449
  const BadgeCountEllipse = ({
30452
30450
  ref,
30453
30451
  loading,
30454
- children,
30455
30452
  hasOverflow,
30456
30453
  charCount,
30454
+ className,
30455
+ children,
30457
30456
  ...props
30458
30457
  }) => {
30459
30458
  return jsx(Text, {
30460
30459
  ref: ref,
30461
- className: "navi_badge_count",
30460
+ className: withPropsClassName("navi_badge_count", className),
30462
30461
  bold: true,
30463
30462
  "data-ellipse": "",
30464
30463
  "data-value-overflow": hasOverflow ? "" : undefined,
@@ -30485,12 +30484,13 @@ const BadgeCountCircle = ({
30485
30484
  charCount,
30486
30485
  hasOverflow,
30487
30486
  loading,
30487
+ className,
30488
30488
  children,
30489
30489
  ...props
30490
30490
  }) => {
30491
30491
  return jsx(Text, {
30492
30492
  ref: ref,
30493
- className: "navi_badge_count",
30493
+ className: withPropsClassName("navi_badge_count", className),
30494
30494
  "data-circle": "",
30495
30495
  bold: true,
30496
30496
  "data-loading": loading ? "" : undefined,