native-document 1.0.166 → 1.0.168
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/.vitepress/config.js +166 -0
- package/CHANGELOG.md +153 -0
- package/components.js +2 -1
- package/dist/native-document.components.min.js +495 -228
- package/dist/native-document.dev.js +7 -0
- package/dist/native-document.dev.js.map +1 -1
- package/dist/native-document.min.js +1 -1
- package/docs/advanced-components.md +213 -608
- package/docs/anchor.md +173 -312
- package/docs/cache.md +95 -803
- package/docs/cli.md +179 -0
- package/docs/components/accordion.md +172 -0
- package/docs/components/alert.md +99 -0
- package/docs/components/avatar.md +160 -0
- package/docs/components/badge.md +102 -0
- package/docs/components/breadcrumb.md +89 -0
- package/docs/components/button.md +183 -0
- package/docs/components/card.md +69 -0
- package/docs/components/context-menu.md +118 -0
- package/docs/components/data-table.md +345 -0
- package/docs/components/dropdown.md +214 -0
- package/docs/components/form/autocomplete-field.md +81 -0
- package/docs/components/form/checkbox-field.md +41 -0
- package/docs/components/form/checkbox-group-field.md +54 -0
- package/docs/components/form/color-field.md +64 -0
- package/docs/components/form/date-field.md +92 -0
- package/docs/components/form/field-collection.md +63 -0
- package/docs/components/form/file-field.md +203 -0
- package/docs/components/form/form-control.md +87 -0
- package/docs/components/form/image-field.md +90 -0
- package/docs/components/form/index.md +115 -0
- package/docs/components/form/number-field.md +65 -0
- package/docs/components/form/radio-field.md +51 -0
- package/docs/components/form/select-field.md +123 -0
- package/docs/components/form/slider.md +136 -0
- package/docs/components/form/string-field.md +134 -0
- package/docs/components/form/textarea-field.md +65 -0
- package/docs/components/form-fields.md +372 -0
- package/docs/components/getting-started.md +264 -0
- package/docs/components/index.md +337 -0
- package/docs/components/layout.md +279 -0
- package/docs/components/list.md +73 -0
- package/docs/components/menu.md +215 -0
- package/docs/components/modal.md +156 -0
- package/docs/components/pagination.md +95 -0
- package/docs/components/popover.md +131 -0
- package/docs/components/progress.md +111 -0
- package/docs/components/shortcut-manager.md +221 -0
- package/docs/components/simple-table.md +107 -0
- package/docs/components/skeleton.md +155 -0
- package/docs/components/spinner.md +100 -0
- package/docs/components/splitter.md +133 -0
- package/docs/components/stepper.md +163 -0
- package/docs/components/switch.md +113 -0
- package/docs/components/tabs.md +153 -0
- package/docs/components/toast.md +119 -0
- package/docs/components/tooltip.md +151 -0
- package/docs/components/traits.md +261 -0
- package/docs/conditional-rendering.md +170 -588
- package/docs/contributing.md +300 -25
- package/docs/core-concepts.md +205 -374
- package/docs/elements.md +251 -367
- package/docs/extending-native-document-element.md +192 -207
- package/docs/filters.md +153 -1122
- package/docs/getting-started.md +193 -267
- package/docs/i18n.md +241 -0
- package/docs/index.md +76 -0
- package/docs/lifecycle-events.md +143 -75
- package/docs/list-rendering.md +227 -852
- package/docs/memory-management.md +134 -47
- package/docs/native-document-element.md +337 -186
- package/docs/native-fetch.md +99 -630
- package/docs/observable-resource.md +364 -0
- package/docs/observables.md +592 -526
- package/docs/routing.md +244 -653
- package/docs/state-management.md +134 -241
- package/docs/svg-elements.md +231 -0
- package/docs/theming.md +409 -0
- package/docs/tutorials/.gitkeep +0 -0
- package/docs/validation.md +95 -97
- package/docs/vitepress-conventions.md +219 -0
- package/package.json +34 -13
- package/readme.md +269 -89
- package/src/components/card/Card.js +93 -39
- package/src/components/card/index.js +1 -1
- package/src/components/list/HasListItem.js +171 -0
- package/src/components/list/List.js +41 -107
- package/src/components/list/ListDivider.js +39 -0
- package/src/components/list/ListGroup.js +76 -59
- package/src/components/list/ListItem.js +117 -69
- package/src/components/list/index.js +3 -1
- package/src/components/list/types/ListItem.d.ts +45 -34
- package/src/components/spacer/Spacer.js +1 -1
- package/src/core/data/ObservableResource.js +5 -0
- package/src/core/data/observable-helpers/observable.prototypes.js +2 -0
- package/src/ui/components/card/CardRender.js +133 -0
- package/src/ui/components/card/card.css +169 -0
- package/src/ui/components/contextmenu/ContextmenuRender.js +1 -1
- package/src/ui/components/list/ListRender.js +18 -0
- package/src/ui/components/list/divider/ListDividerRender.js +10 -0
- package/src/ui/components/list/divider/list-divider.css +12 -0
- package/src/ui/components/list/group/ListGroupRender.js +61 -0
- package/src/ui/components/list/group/list-group.css +62 -0
- package/src/ui/components/list/item/ListItemRender.js +238 -0
- package/src/ui/components/list/item/list-item.css +191 -0
- package/src/ui/components/list/list.css +24 -0
- package/src/ui/components/spacer/SpacerRender.js +10 -0
- package/src/ui/index.js +8 -0
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Stepper
|
|
3
|
+
description: Multi-step wizard component with validation, navigation, and custom renderers
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Stepper
|
|
7
|
+
|
|
8
|
+
```javascript
|
|
9
|
+
import { Stepper, StepperStep } from 'native-document/components';
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## Default Renderer
|
|
13
|
+
|
|
14
|
+
```javascript
|
|
15
|
+
import { StepperRender, StepperStepRender } from 'native-document/ui';
|
|
16
|
+
|
|
17
|
+
Stepper.use(StepperRender);
|
|
18
|
+
StepperStep.use(StepperStepRender);
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## `Stepper`
|
|
24
|
+
|
|
25
|
+
### Configuration
|
|
26
|
+
|
|
27
|
+
| Method | Parameters | Description |
|
|
28
|
+
|---|---|---|
|
|
29
|
+
| `.step(step)` | `step: StepperStep` | Add a step |
|
|
30
|
+
| `.data(data)` | `data: *` | Initial data passed to all steps |
|
|
31
|
+
| `.currentStep(obs)` | `obs: Observable<number>` | Bind the current step index to an observable |
|
|
32
|
+
| `.linear(enabled?)` | `enabled?: boolean` | Steps must complete in order (default) |
|
|
33
|
+
| `.nonLinear()` | - | Can jump to any step freely |
|
|
34
|
+
| `.editable(enabled?)` | `enabled?: boolean` | Allow returning to completed steps |
|
|
35
|
+
| `.horizontal()` | - | Horizontal layout (default) |
|
|
36
|
+
| `.vertical()` | - | Vertical layout |
|
|
37
|
+
| `.alternativeLabel(enabled?)` | `enabled?: boolean` | Labels below the indicators |
|
|
38
|
+
| `.showNumbers(enabled?)` | `enabled?: boolean` | Show step numbers in indicators |
|
|
39
|
+
| `.showConnector(enabled?)` | `enabled?: boolean` | Show connector line between steps |
|
|
40
|
+
|
|
41
|
+
### Navigation position
|
|
42
|
+
|
|
43
|
+
| Method | Description |
|
|
44
|
+
|---|---|
|
|
45
|
+
| `.navigationAtBottom()` | Navigation buttons at the bottom, horizontal layout |
|
|
46
|
+
| `.navigationAtTop()` | Navigation buttons at the top, horizontal layout |
|
|
47
|
+
| `.navigationAtLeading()` | Navigation buttons on the leading side, vertical layout |
|
|
48
|
+
|
|
49
|
+
### Navigation methods
|
|
50
|
+
|
|
51
|
+
| Method | Parameters | Description |
|
|
52
|
+
|---|---|---|
|
|
53
|
+
| `await stepper.next()` | - | Validates current step, then advances |
|
|
54
|
+
| `stepper.previous()` | - | Go to previous step |
|
|
55
|
+
| `await stepper.goToStep(index)` | `index: number` | Jump to a step (0-indexed) |
|
|
56
|
+
| `stepper.reset()` | - | Reset to step 0 |
|
|
57
|
+
|
|
58
|
+
### Events
|
|
59
|
+
|
|
60
|
+
| Method | Parameters | Description |
|
|
61
|
+
|---|---|---|
|
|
62
|
+
| `.onStepChange(handler)` | `handler: (current, previous) => void` | Fires on every step change |
|
|
63
|
+
| `.onNext(handler)` | `handler: (step, index) => void` | Fires when advancing |
|
|
64
|
+
| `.onPrevious(handler)` | `handler: (step, index) => void` | Fires when going back |
|
|
65
|
+
| `.onComplete(handler)` | `handler: (data) => void` | Fires when last step is completed |
|
|
66
|
+
| `.onReset(handler)` | `handler: () => void` | Fires on reset |
|
|
67
|
+
|
|
68
|
+
### Custom renderers
|
|
69
|
+
|
|
70
|
+
| Method | Parameters | Description |
|
|
71
|
+
|---|---|---|
|
|
72
|
+
| `.renderStepIndicator(fn)` | `fn: ($step) => NdChild` | Custom step indicator (circle/icon) |
|
|
73
|
+
| `.renderStepIndicatorConnector(fn)` | `fn: ($step) => NdChild` | Custom connector between indicators |
|
|
74
|
+
| `.renderContent(fn)` | `fn: ($step) => NdChild` | Custom step content wrapper |
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## `StepperStep`
|
|
79
|
+
|
|
80
|
+
```javascript
|
|
81
|
+
StepperStep(title)
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
| Method | Parameters | Description |
|
|
85
|
+
|---|---|---|
|
|
86
|
+
| `.description(text)` | `text: string` | Subtitle below the step title |
|
|
87
|
+
| `.icon(element)` | `element: NdChild` | Icon displayed in the indicator |
|
|
88
|
+
| `.content(element)` | `element: NdChild` | Step body content |
|
|
89
|
+
| `.key(key)` | `key: string` | Unique identifier for this step |
|
|
90
|
+
| `.optional()` | - | Mark step as optional (can be skipped) |
|
|
91
|
+
| `.disabled(val)` | `val: boolean \| Observable<boolean>` | Disable the step |
|
|
92
|
+
| `.visibility(val)` | `val: boolean \| Observable<boolean>` | Show or hide the step reactively |
|
|
93
|
+
| `.validator(fn)` | `fn: () => boolean \| Promise<boolean>` | Validation function - must return `true` to advance |
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## Example
|
|
98
|
+
|
|
99
|
+
```javascript
|
|
100
|
+
const refs = {};
|
|
101
|
+
|
|
102
|
+
const wizard = Stepper()
|
|
103
|
+
.linear()
|
|
104
|
+
.step(
|
|
105
|
+
StepperStep('Account')
|
|
106
|
+
.content(AccountForm().nd.refSelf(refs, 'accountForm'))
|
|
107
|
+
.validator(async () => refs.accountForm.validate())
|
|
108
|
+
)
|
|
109
|
+
.step(
|
|
110
|
+
StepperStep('Profile')
|
|
111
|
+
.description('Tell us about yourself')
|
|
112
|
+
.content(ProfileForm)
|
|
113
|
+
.optional()
|
|
114
|
+
)
|
|
115
|
+
.step(
|
|
116
|
+
StepperStep('Done')
|
|
117
|
+
.icon(CheckIcon)
|
|
118
|
+
.content(SuccessPanel)
|
|
119
|
+
)
|
|
120
|
+
.navigationAtBottom()
|
|
121
|
+
.onComplete(async (data) => {
|
|
122
|
+
await registerUser(data);
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
document.body.appendChild(wizard);
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## Theming
|
|
132
|
+
|
|
133
|
+
```css
|
|
134
|
+
:root {
|
|
135
|
+
--stepper-connector-color: var(--gray-lite-3);
|
|
136
|
+
--stepper-connector-color-active: var(--color-primary);
|
|
137
|
+
--stepper-connector-color-completed: var(--color-success);
|
|
138
|
+
--stepper-connector-thickness: 2px;
|
|
139
|
+
--step-indicator-size: 32px;
|
|
140
|
+
--step-indicator-bg: var(--gray-lite-4);
|
|
141
|
+
--step-indicator-bg-active: var(--color-primary);
|
|
142
|
+
--step-indicator-bg-completed: var(--color-success);
|
|
143
|
+
--step-indicator-bg-error: var(--color-danger);
|
|
144
|
+
--step-indicator-color: var(--gray);
|
|
145
|
+
--step-indicator-color-active: var(--white);
|
|
146
|
+
--step-indicator-font-size: var(--hint-size);
|
|
147
|
+
--step-indicator-font-weight: 600;
|
|
148
|
+
--step-label-size: var(--hint-size);
|
|
149
|
+
--step-label-color: var(--gray);
|
|
150
|
+
--step-label-color-active: var(--text-color);
|
|
151
|
+
--step-label-weight: 500;
|
|
152
|
+
--step-description-size: var(--note-size);
|
|
153
|
+
--step-description-color: var(--gray);
|
|
154
|
+
--stepper-content-padding: var(--space-comfortable) 0;
|
|
155
|
+
}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
## Next Steps
|
|
161
|
+
|
|
162
|
+
- **[Components Overview](./index.md)** - BaseComponent philosophy
|
|
163
|
+
- **[FormControl](./form/form-control.md)** - Form validation with steps
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Switch
|
|
3
|
+
description: Toggle switch component with two-way binding, variants, icons, and inner labels
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Switch
|
|
7
|
+
|
|
8
|
+
```javascript
|
|
9
|
+
import { Switch } from 'native-document/components';
|
|
10
|
+
|
|
11
|
+
Switch(props?)
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Default Renderer
|
|
15
|
+
|
|
16
|
+
```javascript
|
|
17
|
+
import { SwitchRender } from 'native-document/ui';
|
|
18
|
+
|
|
19
|
+
Switch.use(SwitchRender);
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## `$description`
|
|
23
|
+
|
|
24
|
+
```javascript
|
|
25
|
+
{
|
|
26
|
+
value: Observable(false),
|
|
27
|
+
label: null,
|
|
28
|
+
labelPosition: Observable('right'), // 'left' | 'right' | 'top' | 'bottom'
|
|
29
|
+
variant: Observable('primary'),
|
|
30
|
+
outline: false,
|
|
31
|
+
disabled: false,
|
|
32
|
+
loading: false,
|
|
33
|
+
readonly: false,
|
|
34
|
+
onIcon: null,
|
|
35
|
+
offIcon: null,
|
|
36
|
+
innerOnLabel: null,
|
|
37
|
+
innerOffLabel: null,
|
|
38
|
+
props: {} // HTML attributes for the root element
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Methods
|
|
43
|
+
|
|
44
|
+
```javascript
|
|
45
|
+
// Binding
|
|
46
|
+
.model(Observable(false))
|
|
47
|
+
|
|
48
|
+
// Label
|
|
49
|
+
.label('Enable notifications')
|
|
50
|
+
.labelPosition('left') // 'left' | 'right' | 'top' | 'bottom'
|
|
51
|
+
|
|
52
|
+
// Inner labels (inside the toggle)
|
|
53
|
+
.innerLabel('Yes', 'No')
|
|
54
|
+
|
|
55
|
+
// Icons
|
|
56
|
+
.icon(SunIcon, MoonIcon) // onIcon, offIcon
|
|
57
|
+
|
|
58
|
+
// Variants
|
|
59
|
+
.primary()
|
|
60
|
+
.success()
|
|
61
|
+
.danger()
|
|
62
|
+
.warning()
|
|
63
|
+
.ghost()
|
|
64
|
+
.outline()
|
|
65
|
+
|
|
66
|
+
// State
|
|
67
|
+
.disabled(Observable(false))
|
|
68
|
+
.loading(Observable(false))
|
|
69
|
+
.readonly(true)
|
|
70
|
+
|
|
71
|
+
// Programmatic
|
|
72
|
+
.toggle()
|
|
73
|
+
.on()
|
|
74
|
+
.off()
|
|
75
|
+
|
|
76
|
+
// Events
|
|
77
|
+
.onChange((value) => console.log('Changed:', value))
|
|
78
|
+
.onOn(() => console.log('Switched on'))
|
|
79
|
+
.onOff(() => console.log('Switched off'))
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Example
|
|
83
|
+
|
|
84
|
+
```javascript
|
|
85
|
+
const darkMode = Observable(false);
|
|
86
|
+
|
|
87
|
+
Switch()
|
|
88
|
+
.model(darkMode)
|
|
89
|
+
.label('Dark mode')
|
|
90
|
+
.icon(SunIcon, MoonIcon)
|
|
91
|
+
.innerLabel('On', 'Off')
|
|
92
|
+
.onChange((value) => applyTheme(value ? 'dark' : 'light'))
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## Theming
|
|
98
|
+
|
|
99
|
+
```css
|
|
100
|
+
:root {
|
|
101
|
+
--switch-width: 44px;
|
|
102
|
+
--switch-height: 24px;
|
|
103
|
+
--switch-thumb-size: 18px;
|
|
104
|
+
--switch-thumb-offset: 3px;
|
|
105
|
+
--switch-border-width: 0px;
|
|
106
|
+
--switch-track-bg: var(--gray-lite-3);
|
|
107
|
+
--switch-track-bg-active: var(--color-primary);
|
|
108
|
+
--switch-thumb-bg: var(--background);
|
|
109
|
+
--switch-thumb-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
|
|
110
|
+
--switch-transition: 0.2s ease;
|
|
111
|
+
--switch-label-gap: var(--space-cozy);
|
|
112
|
+
}
|
|
113
|
+
```
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Tabs
|
|
3
|
+
description: Tabs component with sortable tabs, closable tabs, overflow handling, and flexible navigation positioning
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Tabs
|
|
7
|
+
|
|
8
|
+
```javascript
|
|
9
|
+
import { Tabs } from 'native-document/components';
|
|
10
|
+
|
|
11
|
+
Tabs(props?)
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Default Renderer
|
|
15
|
+
|
|
16
|
+
```javascript
|
|
17
|
+
import { TabsRender } from 'native-document/ui';
|
|
18
|
+
|
|
19
|
+
Tabs.use(TabsRender);
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## `$description`
|
|
23
|
+
|
|
24
|
+
```javascript
|
|
25
|
+
{
|
|
26
|
+
active: Observable(''),
|
|
27
|
+
tabs: {},
|
|
28
|
+
sortable: false,
|
|
29
|
+
tabAppearance: 'segmented', // 'segmented' | 'pills' | 'underline'
|
|
30
|
+
stickyHeader: false,
|
|
31
|
+
overflow: 'scroll', // 'scroll' | 'menu'
|
|
32
|
+
navigationBarPosition:'top',
|
|
33
|
+
tabsAlignment: 'leading', // 'leading' | 'trailing' | 'center' | 'justified'
|
|
34
|
+
closable: false,
|
|
35
|
+
focusOnNewTab: false,
|
|
36
|
+
props: {} // HTML attributes for the root element
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Methods
|
|
41
|
+
|
|
42
|
+
### Building tabs
|
|
43
|
+
|
|
44
|
+
```javascript
|
|
45
|
+
.tab(label, content, key?)
|
|
46
|
+
.tabWithIcon(icon, label, content, key?)
|
|
47
|
+
|
|
48
|
+
.tab('Overview', OverviewPanel, 'overview')
|
|
49
|
+
.tabWithIcon(HomeIcon, 'Dashboard', DashboardPanel, 'dashboard')
|
|
50
|
+
|
|
51
|
+
.tabs([
|
|
52
|
+
{ key: 'home', label: 'Home', content: HomePanel },
|
|
53
|
+
{ key: 'profile', label: 'Profile', content: ProfilePanel }
|
|
54
|
+
])
|
|
55
|
+
|
|
56
|
+
.addTab(null, 'New Tab', EmptyPanel, 'tab-1')
|
|
57
|
+
.closeTab('settings')
|
|
58
|
+
|
|
59
|
+
.active('overview')
|
|
60
|
+
.active(Observable('overview'))
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Appearance
|
|
64
|
+
|
|
65
|
+
```javascript
|
|
66
|
+
.pills()
|
|
67
|
+
.segmented()
|
|
68
|
+
.underline()
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Navigation position
|
|
72
|
+
|
|
73
|
+
```javascript
|
|
74
|
+
.navigationBarAtTop()
|
|
75
|
+
.navigationBarAtLeft()
|
|
76
|
+
.navigationBarAtRight()
|
|
77
|
+
.navigationBarAsDock()
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Alignment
|
|
81
|
+
|
|
82
|
+
```javascript
|
|
83
|
+
.tabsAtLeading()
|
|
84
|
+
.tabsAtTrailing()
|
|
85
|
+
.tabsAtCenter()
|
|
86
|
+
.tabsJustified()
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Behavior
|
|
90
|
+
|
|
91
|
+
```javascript
|
|
92
|
+
.sortable()
|
|
93
|
+
.closable()
|
|
94
|
+
.stickyHeader()
|
|
95
|
+
.focusOnNewTab()
|
|
96
|
+
.overflow('scroll')
|
|
97
|
+
.overflow('menu')
|
|
98
|
+
.addPlusButton((tabs) => {
|
|
99
|
+
const key = `tab-${Date.now()}`;
|
|
100
|
+
tabs.addTab(null, 'New Tab', EmptyPanel, key);
|
|
101
|
+
})
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### Events
|
|
105
|
+
|
|
106
|
+
```javascript
|
|
107
|
+
.onChange((key) => loadTabContent(key))
|
|
108
|
+
.onClickTab((key) => console.log('Clicked:', key))
|
|
109
|
+
.onCloseTab((key) => confirmClose(key))
|
|
110
|
+
.onBeforeTabClose((key) => confirm('Close this tab?'))
|
|
111
|
+
.onAddTab((key) => console.log('Tab added:', key))
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### Custom renderers
|
|
115
|
+
|
|
116
|
+
```javascript
|
|
117
|
+
.renderTab(($tab) => HStack([$tab.icon, Span($tab.label)]).spacing(4))
|
|
118
|
+
.renderCloseButton(() => Span('x'))
|
|
119
|
+
.renderPlusButton(() => Span('+'))
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Example
|
|
123
|
+
|
|
124
|
+
```javascript
|
|
125
|
+
Tabs()
|
|
126
|
+
.tabWithIcon(HomeIcon, 'Dashboard', DashboardPanel, 'dashboard')
|
|
127
|
+
.tabWithIcon(UsersIcon, 'Users', UsersPanel, 'users')
|
|
128
|
+
.tabWithIcon(SettingsIcon, 'Settings', SettingsPanel, 'settings')
|
|
129
|
+
.active('dashboard')
|
|
130
|
+
.tabsJustified()
|
|
131
|
+
.stickyHeader()
|
|
132
|
+
.onChange((key) => Router.push({ name: key }))
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
## Theming
|
|
138
|
+
|
|
139
|
+
```css
|
|
140
|
+
:root {
|
|
141
|
+
--tabs-border: var(--gray-lite-3);
|
|
142
|
+
--tabs-radius: var(--radius-button);
|
|
143
|
+
--tabs-font-size: var(--description-size);
|
|
144
|
+
--tabs-font-weight: 500;
|
|
145
|
+
--tab-padding: var(--space-cozy) var(--space-comfortable);
|
|
146
|
+
--tab-color: var(--gray);
|
|
147
|
+
--tab-color-active: var(--color-primary);
|
|
148
|
+
--tab-color-hover: var(--text-color);
|
|
149
|
+
--tab-bg-hover: var(--gray-lite-5);
|
|
150
|
+
--tab-indicator-height: 2px;
|
|
151
|
+
--tabs-content-padding: var(--space-comfortable) 0;
|
|
152
|
+
}
|
|
153
|
+
```
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Toast
|
|
3
|
+
description: Toast notification component with auto-dismiss, actions, and positioning
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Toast
|
|
7
|
+
|
|
8
|
+
```javascript
|
|
9
|
+
import { Toast } from 'native-document/components';
|
|
10
|
+
|
|
11
|
+
Toast(content, props?)
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Default Renderer
|
|
15
|
+
|
|
16
|
+
```javascript
|
|
17
|
+
import { ToastRender } from 'native-document/ui';
|
|
18
|
+
import { ButtonRender } from 'native-document/ui';
|
|
19
|
+
|
|
20
|
+
Toast.use(ToastRender);
|
|
21
|
+
Button.use(ButtonRender); // required - actions are rendered as buttons
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## `$description`
|
|
25
|
+
|
|
26
|
+
```javascript
|
|
27
|
+
{
|
|
28
|
+
visibility: Observable(true),
|
|
29
|
+
type: null, // 'info' | 'success' | 'warning' | 'error'
|
|
30
|
+
title: null,
|
|
31
|
+
content: null,
|
|
32
|
+
icon: null,
|
|
33
|
+
showIcon: true,
|
|
34
|
+
duration: 5000, // ms, 0 = no auto-dismiss
|
|
35
|
+
closable: true,
|
|
36
|
+
pauseOnHover: true,
|
|
37
|
+
position: 'top-trailing',
|
|
38
|
+
actions: [],
|
|
39
|
+
props: {} // HTML attributes for the root element
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Methods
|
|
44
|
+
|
|
45
|
+
```javascript
|
|
46
|
+
// Type
|
|
47
|
+
.info()
|
|
48
|
+
.success()
|
|
49
|
+
.warning()
|
|
50
|
+
.error()
|
|
51
|
+
|
|
52
|
+
// Content
|
|
53
|
+
.title('Saved!')
|
|
54
|
+
.content(Div('Your changes have been saved.'))
|
|
55
|
+
.icon(CheckIcon)
|
|
56
|
+
.showIcon(false)
|
|
57
|
+
|
|
58
|
+
// Behavior
|
|
59
|
+
.duration(3000)
|
|
60
|
+
.duration(0) // no auto-dismiss
|
|
61
|
+
.closable(false)
|
|
62
|
+
.pauseOnHover(false)
|
|
63
|
+
|
|
64
|
+
// Position
|
|
65
|
+
.atTopLeading()
|
|
66
|
+
.atTopTrailing() // default
|
|
67
|
+
.atTopCenter()
|
|
68
|
+
.atBottomLeading()
|
|
69
|
+
.atBottomTrailing()
|
|
70
|
+
.atBottomCenter()
|
|
71
|
+
|
|
72
|
+
// Actions
|
|
73
|
+
.action('Undo', () => undo())
|
|
74
|
+
.action('Retry', retry)
|
|
75
|
+
|
|
76
|
+
// Programmatic
|
|
77
|
+
.close()
|
|
78
|
+
|
|
79
|
+
// Events
|
|
80
|
+
.onClose(() => console.log('Dismissed'))
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Example
|
|
84
|
+
|
|
85
|
+
```javascript
|
|
86
|
+
const saveToast = Toast('Your changes have been saved.')
|
|
87
|
+
.success()
|
|
88
|
+
.title('Saved!')
|
|
89
|
+
.duration(3000)
|
|
90
|
+
.atTopTrailing()
|
|
91
|
+
.action('Undo', () => undoChanges())
|
|
92
|
+
|
|
93
|
+
Button('Save')
|
|
94
|
+
.primary()
|
|
95
|
+
.nd.onClick(async () => {
|
|
96
|
+
await saveData();
|
|
97
|
+
saveToast.show()
|
|
98
|
+
})
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
## Theming
|
|
104
|
+
|
|
105
|
+
```css
|
|
106
|
+
:root {
|
|
107
|
+
--toast-width: 320px;
|
|
108
|
+
--toast-padding: var(--space-comfortable);
|
|
109
|
+
--toast-gap: var(--space-cozy);
|
|
110
|
+
--toast-radius: var(--radius-card);
|
|
111
|
+
--toast-font-size: var(--note-size);
|
|
112
|
+
--toast-title-size: var(--description-size);
|
|
113
|
+
--toast-title-weight: 600;
|
|
114
|
+
--toast-shadow: var(--shadow-lg);
|
|
115
|
+
--toast-container-gap: var(--space-cozy);
|
|
116
|
+
--toast-container-offset: var(--space-comfortable);
|
|
117
|
+
--toast-duration: 0.25s;
|
|
118
|
+
}
|
|
119
|
+
```
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Tooltip
|
|
3
|
+
description: Tooltip component with automatic trigger binding, positioning, and interactive mode
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Tooltip
|
|
7
|
+
|
|
8
|
+
```javascript
|
|
9
|
+
import { Tooltip } from 'native-document/components';
|
|
10
|
+
|
|
11
|
+
Tooltip(content, props?)
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
When `Tooltip.use()` is called, it automatically adds a `.nd.tooltip()` method to all NDElements and BaseComponents.
|
|
15
|
+
|
|
16
|
+
## Default Renderer
|
|
17
|
+
|
|
18
|
+
```javascript
|
|
19
|
+
import { TooltipRender } from 'native-document/ui';
|
|
20
|
+
|
|
21
|
+
Tooltip.use(TooltipRender);
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## `$description`
|
|
25
|
+
|
|
26
|
+
```javascript
|
|
27
|
+
{
|
|
28
|
+
trigger: null,
|
|
29
|
+
interaction: 'hover', // 'hover' | 'click' | 'focus'
|
|
30
|
+
content: null,
|
|
31
|
+
title: null,
|
|
32
|
+
position: 'top',
|
|
33
|
+
isOpen: Observable(false),
|
|
34
|
+
offset: 8,
|
|
35
|
+
hideDelay: 0,
|
|
36
|
+
arrow: true,
|
|
37
|
+
interactive: true,
|
|
38
|
+
updatePositionOn: null,
|
|
39
|
+
variant: null,
|
|
40
|
+
props: {}
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Methods
|
|
45
|
+
|
|
46
|
+
| Method | Parameters | Description |
|
|
47
|
+
|---|---|---|
|
|
48
|
+
| `.content(element)` | `element: NdChild` | Tooltip body content |
|
|
49
|
+
| `.title(text)` | `text: string` | Tooltip title |
|
|
50
|
+
| `.trigger(element)` | `element: HTMLElement` | Element that opens the tooltip |
|
|
51
|
+
| `.onHovered()` | - | Open on hover (default) |
|
|
52
|
+
| `.onClicked()` | - | Open on click |
|
|
53
|
+
| `.onFocused()` | - | Open on focus |
|
|
54
|
+
| `.atTop()` | - | Position above trigger |
|
|
55
|
+
| `.atBottom()` | - | Position below trigger |
|
|
56
|
+
| `.atLeft()` | - | Position left of trigger |
|
|
57
|
+
| `.atRight()` | - | Position right of trigger |
|
|
58
|
+
| `.arrow(enabled?)` | `enabled?: boolean` | Show/hide the arrow. Default: `true` |
|
|
59
|
+
| `.offset(px)` | `px: number` | Distance from the trigger in px |
|
|
60
|
+
| `.hideDelay(ms)` | `ms: number` | Delay before hiding on hover leave |
|
|
61
|
+
| `.interactive(enabled)` | `enabled: boolean` | Keep open when hovering the tooltip itself |
|
|
62
|
+
| `.updatePositionOn(observable)` | `observable: Observable` | Recalculate position when the observable changes |
|
|
63
|
+
| `.open()` | - | Open programmatically |
|
|
64
|
+
| `.close()` | - | Close programmatically |
|
|
65
|
+
| `.toggle()` | - | Toggle open/close |
|
|
66
|
+
|
|
67
|
+
## Via `.nd.tooltip()`
|
|
68
|
+
|
|
69
|
+
Once registered, any element gains `.nd.tooltip()`:
|
|
70
|
+
|
|
71
|
+
```javascript
|
|
72
|
+
// Simple string
|
|
73
|
+
Button('Delete')
|
|
74
|
+
.danger()
|
|
75
|
+
.nd.tooltip('Permanently delete this item')
|
|
76
|
+
|
|
77
|
+
// Tooltip instance for full control
|
|
78
|
+
const hint = Tooltip(
|
|
79
|
+
VStack([
|
|
80
|
+
Strong('Keyboard shortcut'),
|
|
81
|
+
Span('Cmd S')
|
|
82
|
+
]).spacing(4)
|
|
83
|
+
)
|
|
84
|
+
.atBottom()
|
|
85
|
+
.arrow()
|
|
86
|
+
|
|
87
|
+
Button('Save')
|
|
88
|
+
.primary()
|
|
89
|
+
.nd.tooltip(hint)
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Presets
|
|
93
|
+
|
|
94
|
+
```javascript
|
|
95
|
+
Tooltip.preset('shortcut', (content, props) => {
|
|
96
|
+
return Tooltip(content, props)
|
|
97
|
+
.atBottom()
|
|
98
|
+
.hideDelay(100);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
Button('Save').nd.tooltip(Tooltip.shortcut('Cmd S'))
|
|
102
|
+
Button('Open').nd.tooltip(Tooltip.shortcut('Cmd O'))
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## `updatePositionOn`
|
|
106
|
+
|
|
107
|
+
Pass an observable to trigger a position recalculation when its value changes - useful when the trigger element moves or resizes dynamically:
|
|
108
|
+
|
|
109
|
+
```javascript
|
|
110
|
+
const isExpanded = Observable(false);
|
|
111
|
+
|
|
112
|
+
Tooltip('More info')
|
|
113
|
+
.trigger(myButton)
|
|
114
|
+
.updatePositionOn(isExpanded)
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## Tooltip vs Popover
|
|
120
|
+
|
|
121
|
+
| | Tooltip | Popover |
|
|
122
|
+
|---|---|---|
|
|
123
|
+
| **Purpose** | Short contextual hint | Rich floating panel |
|
|
124
|
+
| **Content** | Text or simple element | Header, body, footer |
|
|
125
|
+
| **Triggered by** | Hover (default) | Click (default) |
|
|
126
|
+
| **Focus trap** | No | Optional |
|
|
127
|
+
| **Use when** | Labeling an icon, short help text | User profile card, settings panel |
|
|
128
|
+
|
|
129
|
+
See **[Popover](./popover.md)** for the richer alternative.
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
## Theming
|
|
135
|
+
|
|
136
|
+
```css
|
|
137
|
+
:root {
|
|
138
|
+
--tooltip-bg: #1a1a2e;
|
|
139
|
+
--tooltip-color: var(--white);
|
|
140
|
+
--tooltip-border: transparent;
|
|
141
|
+
--tooltip-radius: var(--radius-button);
|
|
142
|
+
--tooltip-shadow: var(--shadow-lg);
|
|
143
|
+
--tooltip-padding: var(--space-cozy) var(--space-cozy-comfortable);
|
|
144
|
+
--tooltip-min-width: 0;
|
|
145
|
+
--tooltip-max-width: 280px;
|
|
146
|
+
--tooltip-z-index: 100001;
|
|
147
|
+
--tooltip-font-size: var(--note-size);
|
|
148
|
+
--tooltip-arrow-size: 6px;
|
|
149
|
+
--tooltip-animation-duration: 0.12s;
|
|
150
|
+
}
|
|
151
|
+
```
|