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.
Files changed (108) hide show
  1. package/.vitepress/config.js +166 -0
  2. package/CHANGELOG.md +153 -0
  3. package/components.js +2 -1
  4. package/dist/native-document.components.min.js +495 -228
  5. package/dist/native-document.dev.js +7 -0
  6. package/dist/native-document.dev.js.map +1 -1
  7. package/dist/native-document.min.js +1 -1
  8. package/docs/advanced-components.md +213 -608
  9. package/docs/anchor.md +173 -312
  10. package/docs/cache.md +95 -803
  11. package/docs/cli.md +179 -0
  12. package/docs/components/accordion.md +172 -0
  13. package/docs/components/alert.md +99 -0
  14. package/docs/components/avatar.md +160 -0
  15. package/docs/components/badge.md +102 -0
  16. package/docs/components/breadcrumb.md +89 -0
  17. package/docs/components/button.md +183 -0
  18. package/docs/components/card.md +69 -0
  19. package/docs/components/context-menu.md +118 -0
  20. package/docs/components/data-table.md +345 -0
  21. package/docs/components/dropdown.md +214 -0
  22. package/docs/components/form/autocomplete-field.md +81 -0
  23. package/docs/components/form/checkbox-field.md +41 -0
  24. package/docs/components/form/checkbox-group-field.md +54 -0
  25. package/docs/components/form/color-field.md +64 -0
  26. package/docs/components/form/date-field.md +92 -0
  27. package/docs/components/form/field-collection.md +63 -0
  28. package/docs/components/form/file-field.md +203 -0
  29. package/docs/components/form/form-control.md +87 -0
  30. package/docs/components/form/image-field.md +90 -0
  31. package/docs/components/form/index.md +115 -0
  32. package/docs/components/form/number-field.md +65 -0
  33. package/docs/components/form/radio-field.md +51 -0
  34. package/docs/components/form/select-field.md +123 -0
  35. package/docs/components/form/slider.md +136 -0
  36. package/docs/components/form/string-field.md +134 -0
  37. package/docs/components/form/textarea-field.md +65 -0
  38. package/docs/components/form-fields.md +372 -0
  39. package/docs/components/getting-started.md +264 -0
  40. package/docs/components/index.md +337 -0
  41. package/docs/components/layout.md +279 -0
  42. package/docs/components/list.md +73 -0
  43. package/docs/components/menu.md +215 -0
  44. package/docs/components/modal.md +156 -0
  45. package/docs/components/pagination.md +95 -0
  46. package/docs/components/popover.md +131 -0
  47. package/docs/components/progress.md +111 -0
  48. package/docs/components/shortcut-manager.md +221 -0
  49. package/docs/components/simple-table.md +107 -0
  50. package/docs/components/skeleton.md +155 -0
  51. package/docs/components/spinner.md +100 -0
  52. package/docs/components/splitter.md +133 -0
  53. package/docs/components/stepper.md +163 -0
  54. package/docs/components/switch.md +113 -0
  55. package/docs/components/tabs.md +153 -0
  56. package/docs/components/toast.md +119 -0
  57. package/docs/components/tooltip.md +151 -0
  58. package/docs/components/traits.md +261 -0
  59. package/docs/conditional-rendering.md +170 -588
  60. package/docs/contributing.md +300 -25
  61. package/docs/core-concepts.md +205 -374
  62. package/docs/elements.md +251 -367
  63. package/docs/extending-native-document-element.md +192 -207
  64. package/docs/filters.md +153 -1122
  65. package/docs/getting-started.md +193 -267
  66. package/docs/i18n.md +241 -0
  67. package/docs/index.md +76 -0
  68. package/docs/lifecycle-events.md +143 -75
  69. package/docs/list-rendering.md +227 -852
  70. package/docs/memory-management.md +134 -47
  71. package/docs/native-document-element.md +337 -186
  72. package/docs/native-fetch.md +99 -630
  73. package/docs/observable-resource.md +364 -0
  74. package/docs/observables.md +592 -526
  75. package/docs/routing.md +244 -653
  76. package/docs/state-management.md +134 -241
  77. package/docs/svg-elements.md +231 -0
  78. package/docs/theming.md +409 -0
  79. package/docs/tutorials/.gitkeep +0 -0
  80. package/docs/validation.md +95 -97
  81. package/docs/vitepress-conventions.md +219 -0
  82. package/package.json +34 -13
  83. package/readme.md +269 -89
  84. package/src/components/card/Card.js +93 -39
  85. package/src/components/card/index.js +1 -1
  86. package/src/components/list/HasListItem.js +171 -0
  87. package/src/components/list/List.js +41 -107
  88. package/src/components/list/ListDivider.js +39 -0
  89. package/src/components/list/ListGroup.js +76 -59
  90. package/src/components/list/ListItem.js +117 -69
  91. package/src/components/list/index.js +3 -1
  92. package/src/components/list/types/ListItem.d.ts +45 -34
  93. package/src/components/spacer/Spacer.js +1 -1
  94. package/src/core/data/ObservableResource.js +5 -0
  95. package/src/core/data/observable-helpers/observable.prototypes.js +2 -0
  96. package/src/ui/components/card/CardRender.js +133 -0
  97. package/src/ui/components/card/card.css +169 -0
  98. package/src/ui/components/contextmenu/ContextmenuRender.js +1 -1
  99. package/src/ui/components/list/ListRender.js +18 -0
  100. package/src/ui/components/list/divider/ListDividerRender.js +10 -0
  101. package/src/ui/components/list/divider/list-divider.css +12 -0
  102. package/src/ui/components/list/group/ListGroupRender.js +61 -0
  103. package/src/ui/components/list/group/list-group.css +62 -0
  104. package/src/ui/components/list/item/ListItemRender.js +238 -0
  105. package/src/ui/components/list/item/list-item.css +191 -0
  106. package/src/ui/components/list/list.css +24 -0
  107. package/src/ui/components/spacer/SpacerRender.js +10 -0
  108. 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
+ ```