native-document 1.0.166 → 1.0.169

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 (139) hide show
  1. package/.vitepress/config.js +166 -0
  2. package/CHANGELOG.md +187 -0
  3. package/components.js +4 -1
  4. package/dist/native-document.components.min.js +593 -300
  5. package/dist/native-document.dev.js +99 -75
  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/icons.md +321 -0
  41. package/docs/components/index.md +337 -0
  42. package/docs/components/layout.md +279 -0
  43. package/docs/components/list.md +73 -0
  44. package/docs/components/menu.md +215 -0
  45. package/docs/components/modal.md +156 -0
  46. package/docs/components/pagination.md +95 -0
  47. package/docs/components/popover.md +131 -0
  48. package/docs/components/progress.md +111 -0
  49. package/docs/components/shortcut-manager.md +221 -0
  50. package/docs/components/simple-table.md +107 -0
  51. package/docs/components/skeleton.md +155 -0
  52. package/docs/components/spinner.md +100 -0
  53. package/docs/components/splitter.md +133 -0
  54. package/docs/components/stepper.md +163 -0
  55. package/docs/components/switch.md +113 -0
  56. package/docs/components/tabs.md +153 -0
  57. package/docs/components/toast.md +119 -0
  58. package/docs/components/tooltip.md +151 -0
  59. package/docs/components/traits.md +261 -0
  60. package/docs/conditional-rendering.md +170 -588
  61. package/docs/contributing.md +300 -25
  62. package/docs/core-concepts.md +205 -374
  63. package/docs/elements.md +251 -367
  64. package/docs/extending-native-document-element.md +192 -207
  65. package/docs/filters.md +153 -1122
  66. package/docs/getting-started.md +193 -267
  67. package/docs/i18n.md +241 -0
  68. package/docs/index.md +76 -0
  69. package/docs/lifecycle-events.md +143 -75
  70. package/docs/list-rendering.md +227 -852
  71. package/docs/memory-management.md +134 -47
  72. package/docs/native-document-element.md +337 -186
  73. package/docs/native-fetch.md +99 -630
  74. package/docs/observable-resource.md +364 -0
  75. package/docs/observables.md +592 -526
  76. package/docs/routing.md +244 -653
  77. package/docs/state-management.md +134 -241
  78. package/docs/svg-elements.md +231 -0
  79. package/docs/theming.md +409 -0
  80. package/docs/tutorials/.gitkeep +0 -0
  81. package/docs/validation.md +95 -97
  82. package/docs/vitepress-conventions.md +219 -0
  83. package/package.json +34 -13
  84. package/readme.md +269 -89
  85. package/src/components/card/Card.js +93 -39
  86. package/src/components/card/index.js +1 -1
  87. package/src/components/form/field/types/FileField.js +3 -3
  88. package/src/components/icon/Icon.js +107 -0
  89. package/src/components/icon/icon-getters.js +142 -0
  90. package/src/components/icon/icons.js +171 -0
  91. package/src/components/icon/index.js +17 -0
  92. package/src/components/icon/types/Icon.d.ts +191 -0
  93. package/src/components/index.d.ts +6 -1
  94. package/src/components/list/HasListItem.js +171 -0
  95. package/src/components/list/List.js +41 -107
  96. package/src/components/list/ListDivider.js +39 -0
  97. package/src/components/list/ListGroup.js +76 -59
  98. package/src/components/list/ListItem.js +117 -69
  99. package/src/components/list/index.js +3 -1
  100. package/src/components/list/types/List.d.ts +45 -32
  101. package/src/components/list/types/ListDivider.ts +16 -0
  102. package/src/components/list/types/ListGroup.d.ts +51 -29
  103. package/src/components/list/types/ListItem.d.ts +40 -30
  104. package/src/components/spacer/Spacer.js +1 -1
  105. package/src/core/data/ObservableResource.js +5 -0
  106. package/src/core/data/observable-helpers/observable.prototypes.js +2 -0
  107. package/src/core/elements/content-formatter.js +16 -2
  108. package/src/core/elements/form.js +1 -1
  109. package/src/core/elements/img.js +1 -1
  110. package/src/core/elements/medias.js +2 -2
  111. package/src/core/elements/meta-data.js +1 -1
  112. package/src/core/wrappers/AttributesWrapper.js +37 -22
  113. package/src/core/wrappers/ElementCreator.js +9 -16
  114. package/src/core/wrappers/HtmlElementWrapper.js +26 -7
  115. package/src/core/wrappers/NDElement.js +12 -1
  116. package/src/core/wrappers/prototypes/attributes-extensions.js +24 -24
  117. package/src/core/wrappers/prototypes/nd-element-extensions.js +1 -8
  118. package/src/ui/components/card/CardRender.js +133 -0
  119. package/src/ui/components/card/card.css +169 -0
  120. package/src/ui/components/contextmenu/ContextmenuRender.js +1 -1
  121. package/src/ui/components/icon/material/MaterialIconRender.js +71 -0
  122. package/src/ui/components/icon/material/material.css +15 -0
  123. package/src/ui/components/icon/material/material.map.js +170 -0
  124. package/src/ui/components/icon/phosphor/PhosphorIconRender.js +71 -0
  125. package/src/ui/components/icon/phosphor/phosphor.css +17 -0
  126. package/src/ui/components/icon/phosphor/phosphor.map.js +165 -0
  127. package/src/ui/components/icon/tabler/TablerIconRender.js +59 -0
  128. package/src/ui/components/icon/tabler/tabler.css +14 -0
  129. package/src/ui/components/icon/tabler/tabler.map.js +165 -0
  130. package/src/ui/components/list/ListRender.js +18 -0
  131. package/src/ui/components/list/divider/ListDividerRender.js +10 -0
  132. package/src/ui/components/list/divider/list-divider.css +12 -0
  133. package/src/ui/components/list/group/ListGroupRender.js +61 -0
  134. package/src/ui/components/list/group/list-group.css +62 -0
  135. package/src/ui/components/list/item/ListItemRender.js +238 -0
  136. package/src/ui/components/list/item/list-item.css +191 -0
  137. package/src/ui/components/list/list.css +24 -0
  138. package/src/ui/components/spacer/SpacerRender.js +10 -0
  139. package/src/ui/index.js +13 -0
@@ -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
+ ```
@@ -0,0 +1,261 @@
1
+ ---
2
+ title: Traits
3
+ description: Reusable behavior mixins for NativeDocument components - HasEventEmitter, HasDraggable, HasResizable, HasItems, HasPosition
4
+ ---
5
+
6
+ # Traits
7
+
8
+ Traits are behavior mixins that add capabilities to components. They are applied via `BaseComponent.use()` and follow the same composition pattern as the rest of the framework.
9
+
10
+ ```javascript
11
+ import { BaseComponent, HasEventEmitter, HasDraggable, HasResizable } from '@native-document/components';
12
+
13
+ BaseComponent.use(MyComponent, HasEventEmitter, HasDraggable, HasResizable);
14
+ ```
15
+
16
+ > `HasDraggable` and `HasResizable` both **require** `HasEventEmitter` to be applied first.
17
+
18
+ ---
19
+
20
+ ## `HasEventEmitter`
21
+
22
+ Adds a custom event system to a component. Components use this to communicate state changes to their parent.
23
+
24
+ ### Methods
25
+
26
+ | Method | Parameters | Description |
27
+ |---|---|---|
28
+ | `.on(eventName, callback)` | `eventName: string`, `callback: function` | Register a listener. Multiple listeners allowed per event. |
29
+ | `.emit(eventName, ...args)` | `eventName: string`, `...args` | Emit an event. Async - all listeners awaited in order. |
30
+ | `.trigger(eventName, ...args)` | same as `emit` | Alias for `emit`. |
31
+ | `.hasListeners(eventName)` | `eventName: string` | Returns `true` if any listeners are registered. |
32
+
33
+ ### Built-in component events
34
+
35
+ | Event | When |
36
+ |---|---|
37
+ | `open` | Component becomes visible |
38
+ | `close` | Component is dismissed |
39
+ | `change` | Value or selection changes |
40
+ | `select` | An item is selected |
41
+
42
+ ### Example
43
+
44
+ ```javascript
45
+ import { Modal } from '@native-document/components';
46
+
47
+ const confirmModal = Modal(Div('Are you sure?'))
48
+ .title('Confirm')
49
+ .on('close', (confirmed) => {
50
+ if (confirmed) deleteItem();
51
+ });
52
+
53
+ Button('Delete')
54
+ .danger()
55
+ .nd.onClick(() => confirmModal.open())
56
+ ```
57
+
58
+ ---
59
+
60
+ ## `HasDraggable`
61
+
62
+ Adds mouse-driven drag behavior to a component. Requires `HasEventEmitter`.
63
+
64
+ ### Methods
65
+
66
+ | Method | Parameters | Description |
67
+ |---|---|---|
68
+ | `.makeDraggable(element, grip?)` | `element: HTMLElement`, `grip?: HTMLElement` | Makes the element draggable. Returns a cleanup function. `grip` defaults to `element`. |
69
+ | `.move(x, y)` | `x: number`, `y: number` | Programmatically move to an absolute position (px). |
70
+
71
+ ### CSS classes
72
+
73
+ | Class | When applied |
74
+ |---|---|
75
+ | `is-draggable` | On the grip element |
76
+ | `is-dragging` | While drag is active |
77
+
78
+ ### Events
79
+
80
+ | Event | Args | When |
81
+ |---|---|---|
82
+ | `onDragStart` | `(event, initialX, initialY)` | Mouse down on grip |
83
+ | `onDrag` | `(event, x, y)` | Mouse move while dragging |
84
+ | `onDragEnd` | - | Mouse up |
85
+
86
+ ```javascript
87
+ MyModal
88
+ .on('onDragStart', (e, x, y) => console.log('Started at', x, y))
89
+ .on('onDrag', (e, x, y) => console.log('Moving to', x, y))
90
+ .on('onDragEnd', () => console.log('Done'))
91
+ ```
92
+
93
+ ---
94
+
95
+ ## `HasResizable`
96
+
97
+ Adds mouse-driven resize behavior to a component. Requires `HasEventEmitter`.
98
+
99
+ ### Methods
100
+
101
+ | Method | Parameters | Description |
102
+ |---|---|---|
103
+ | `.makeResizable(element, options?)` | `element: HTMLElement`, `options?: object` | Makes the element resizable. Returns a cleanup function. |
104
+
105
+ Options:
106
+
107
+ | Option | Type | Default | Description |
108
+ |---|---|---|---|
109
+ | `directions` | `string[]` | `['right', 'bottom', 'bottom-right']` | Which resize handles to show. Available: `right`, `left`, `top`, `bottom`, `top-right`, `top-left`, `bottom-right`, `bottom-left` |
110
+ | `size.minWidth` | `number` | `200` | Minimum width in px |
111
+ | `size.minHeight` | `number` | `200` | Minimum height in px |
112
+ | `size.maxWidth` | `number` | - | Maximum width in px |
113
+ | `size.maxHeight` | `number` | - | Maximum height in px |
114
+
115
+ ### CSS classes
116
+
117
+ | Class | Applied to | When |
118
+ |---|---|---|
119
+ | `is-resizable` | Parent element | Always (when resizable) |
120
+ | `resize-handle is-{direction}` | Handle divs | Always |
121
+ | `is-resizing` | `document.body` | While resize is active |
122
+
123
+ ### Events
124
+
125
+ | Event | Args | When |
126
+ |---|---|---|
127
+ | `onResizeStart` | `(event, width, height)` | Mouse down on handle |
128
+ | `onResize` | `(event, width, height)` | Mouse move while resizing |
129
+ | `onResizeEnd` | `(width, height)` | Mouse up |
130
+
131
+ ```javascript
132
+ MyPanel
133
+ .on('onResizeStart', (e, w, h) => console.log('Started at', w, h))
134
+ .on('onResize', (e, w, h) => saveSize(w, h))
135
+ .on('onResizeEnd', (w, h) => persist({ width: w, height: h }))
136
+ ```
137
+
138
+ ---
139
+
140
+ ## `HasItems`
141
+
142
+ Manages a reactive collection of items. Used internally by `Accordion`, `Menu`, `Tabs`, `Dropdown`.
143
+
144
+ ### Methods
145
+
146
+ | Method | Parameters | Description |
147
+ |---|---|---|
148
+ | `.dynamic(observableArray?)` | `observableArray?: ObservableArray` | Binds to an existing array or creates a new `Observable.array()`. |
149
+ | `.bind(observableArray?)` | same as `dynamic` | Alias for `dynamic`. |
150
+ | `.items(items)` | `items: array` | Replaces all current items. |
151
+ | `.clear()` | - | Removes all items. |
152
+ | `.removeItem(item)` | `item: *` | Removes a specific item by reference. |
153
+
154
+ ### Example
155
+
156
+ ```javascript
157
+ import { BaseComponent, HasEventEmitter, HasItems } from '@native-document/components';
158
+ import { Div, Span } from 'native-document/elements';
159
+ import { ForEach } from 'native-document/elements';
160
+
161
+ function TagList(props = {}) {
162
+ if (!(this instanceof TagList)) return new TagList(props);
163
+ BaseComponent.call(this);
164
+ this.$description = { items: [], props };
165
+ }
166
+
167
+ BaseComponent.extends(TagList);
168
+ BaseComponent.use(TagList, HasEventEmitter, HasItems);
169
+
170
+ TagList.use(($d) => {
171
+ return Div({ class: 'tag-list' },
172
+ ForEach($d.items, tag => Span({ class: 'tag' }, tag))
173
+ );
174
+ });
175
+
176
+ TagList()
177
+ .dynamic()
178
+ .items(['JavaScript', 'TypeScript', 'Python'])
179
+ ```
180
+
181
+ ---
182
+
183
+ ## `HasPosition` / `HasFullPosition`
184
+
185
+ Position shorthand methods for components that accept a `position` prop (`Tooltip`, `Popover`, `Toast`). Applied internally - you do not need to apply them manually.
186
+
187
+ ### `HasPosition` - 4 directions + center
188
+
189
+ | Method | Sets `position` to |
190
+ |---|---|
191
+ | `.atTop()` | `'top'` |
192
+ | `.atBottom()` | `'bottom'` |
193
+ | `.atLeft()` | `'left'` |
194
+ | `.atRight()` | `'right'` |
195
+ | `.atCenter()` | `'center'` |
196
+
197
+ ### `HasFullPosition` - all positions
198
+
199
+ | Method | Sets `position` to |
200
+ |---|---|
201
+ | `.atTop()` | `'top'` |
202
+ | `.atBottom()` | `'bottom'` |
203
+ | `.atLeft()` | `'left'` |
204
+ | `.atRight()` | `'right'` |
205
+ | `.atTopLeading()` | `'top-leading'` |
206
+ | `.atTopTrailing()` | `'top-trailing'` |
207
+ | `.atTopCenter()` | `'top-center'` |
208
+ | `.atBottomLeading()` | `'bottom-leading'` |
209
+ | `.atBottomTrailing()` | `'bottom-trailing'` |
210
+ | `.atBottomCenter()` | `'bottom-center'` |
211
+ | `.atLeadingCenter()` | `'leading-center'` |
212
+ | `.atTrailingCenter()` | `'trailing-center'` |
213
+
214
+ ---
215
+
216
+ ## Building Custom Components with Traits
217
+
218
+ ```javascript
219
+ import { BaseComponent, HasEventEmitter, HasDraggable } from '@native-document/components';
220
+ import { Div, H2 } from 'native-document/elements';
221
+
222
+ function FloatingPanel(title, content, props = {}) {
223
+ if (!(this instanceof FloatingPanel)) {
224
+ return new FloatingPanel(title, content, props);
225
+ }
226
+ BaseComponent.call(this);
227
+ this.$description = { title, content, props };
228
+ }
229
+
230
+ FloatingPanel.defaultTemplate = null;
231
+ FloatingPanel.use = function(template) {
232
+ FloatingPanel.defaultTemplate = template;
233
+ };
234
+
235
+ BaseComponent.extends(FloatingPanel);
236
+ BaseComponent.use(FloatingPanel, HasEventEmitter, HasDraggable);
237
+
238
+ FloatingPanel.use(($d, component) => {
239
+ const header = Div({ class: 'panel-header' }, H2($d.title));
240
+ const body = Div({ class: 'panel-body' }, $d.content);
241
+ const panel = Div({ class: 'panel', style: 'position: absolute', ...$d.props }, [header, body]);
242
+
243
+ component.postBuild(() => {
244
+ component.makeDraggable(panel, header);
245
+ });
246
+
247
+ return panel;
248
+ });
249
+
250
+ FloatingPanel('Settings', Div('Panel content'))
251
+ .on('onDragEnd', () => console.log('Moved'))
252
+ .nd
253
+ ```
254
+
255
+ ---
256
+
257
+ ## Next Steps
258
+
259
+ - **[Components Overview](./index.md)** - BaseComponent philosophy
260
+ - **[Modal](./modal.md)** - Uses HasEventEmitter, HasDraggable, HasResizable
261
+ - **[Getting Started](./getting-started.md)** - Register renderers