native-document 1.0.199 → 1.0.201
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/components.js +1 -0
- package/package.json +1 -1
- package/src/components/drawer/Drawer.js +344 -0
- package/src/components/drawer/index.js +7 -0
- package/src/components/drawer/types/Drawer.d.ts +61 -0
- package/src/components/index.d.ts +1 -0
- package/src/ui/components/drawer/DrawerRender.js +76 -0
- package/src/ui/components/drawer/drawer.css +180 -0
- package/src/ui/index.js +1 -0
package/components.js
CHANGED
|
@@ -5,6 +5,7 @@ export * from './src/components/badge/index';
|
|
|
5
5
|
export * from './src/components/breadcrumb/index';
|
|
6
6
|
export * from './src/components/button/index';
|
|
7
7
|
export * from './src/components/card/index';
|
|
8
|
+
export * from './src/components/drawer/index';
|
|
8
9
|
export * from './src/components/context-menu/index';
|
|
9
10
|
export * from './src/components/divider/index';
|
|
10
11
|
export * from './src/components/dropdown/index';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "native-document",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.201",
|
|
4
4
|
"description": "A reactive JavaScript framework that preserves native DOM simplicity without sacrificing modern features",
|
|
5
5
|
"author": "AfroCodeur <https://github.com/afrocodeur>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
import BaseComponent from '../BaseComponent';
|
|
2
|
+
import HasEventEmitter from '../../core/utils/HasEventEmitter';
|
|
3
|
+
import DebugManager from '../../core/utils/debug-manager';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Side panel that slides in from an edge of the screen, with an overlay behind it.
|
|
7
|
+
* Supports position (left, right, top, bottom), size, closable, footer actions,
|
|
8
|
+
* and open/close lifecycle events.
|
|
9
|
+
*
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* const drawer = new Drawer(Div('Drawer body content'))
|
|
13
|
+
* .title('Create ticket')
|
|
14
|
+
* .subtitle('This ticket will be visible for sale immediately')
|
|
15
|
+
* .position('right')
|
|
16
|
+
* .size('400px')
|
|
17
|
+
* .closable(true)
|
|
18
|
+
* .overlay(true)
|
|
19
|
+
* .action('Cancel', (_, instance) => instance.close())
|
|
20
|
+
* .action('Create ticket', () => console.log('create'), 'primary')
|
|
21
|
+
* .onClose(() => console.log('closed'));
|
|
22
|
+
*
|
|
23
|
+
* Drawer.use((description, instance) => {
|
|
24
|
+
* // description.content, description.position, description.actions...
|
|
25
|
+
* return Div({ class: `drawer drawer--${description.position}` }, description.content);
|
|
26
|
+
* });
|
|
27
|
+
*
|
|
28
|
+
* @constructor
|
|
29
|
+
* @param {NdChild} content
|
|
30
|
+
* @param {GlobalAttributes} [props={}]
|
|
31
|
+
*/
|
|
32
|
+
export default function Drawer(content = null, props = {}) {
|
|
33
|
+
if(!(this instanceof Drawer)) {
|
|
34
|
+
return new Drawer(content, props);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
BaseComponent.call(this, props);
|
|
38
|
+
|
|
39
|
+
this.$description = {
|
|
40
|
+
title: null,
|
|
41
|
+
subtitle: null,
|
|
42
|
+
content,
|
|
43
|
+
position: 'right',
|
|
44
|
+
size: '400px',
|
|
45
|
+
overlay: true,
|
|
46
|
+
closeOnOverlayClick: true,
|
|
47
|
+
closable: true,
|
|
48
|
+
actions: [],
|
|
49
|
+
renderContent: null,
|
|
50
|
+
renderFooter: null,
|
|
51
|
+
renderHeader: null,
|
|
52
|
+
backdrop: true,
|
|
53
|
+
isOpen: null,
|
|
54
|
+
props,
|
|
55
|
+
};
|
|
56
|
+
this.aria = {
|
|
57
|
+
'role': 'dialog',
|
|
58
|
+
'aria-modal': 'true',
|
|
59
|
+
'aria-hidden': 'true',
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
Drawer.defaultTemplate = null;
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Registers the render template for Drawer.
|
|
67
|
+
* @param {(description: {
|
|
68
|
+
* title: NdChild|null,
|
|
69
|
+
* subtitle: NdChild|null,
|
|
70
|
+
* content: NdChild,
|
|
71
|
+
* position: 'left'|'right'|'top'|'bottom',
|
|
72
|
+
* size: string,
|
|
73
|
+
* overlay: boolean,
|
|
74
|
+
* closeOnOverlayClick: boolean,
|
|
75
|
+
* closable: boolean,
|
|
76
|
+
* actions: Array<{ label: NdChild, handler: Function|null, variant: string|null }>,
|
|
77
|
+
* props: GlobalAttributes,
|
|
78
|
+
* }, instance: Drawer) => NdChild} template
|
|
79
|
+
*/
|
|
80
|
+
Drawer.use = function(template) {
|
|
81
|
+
Drawer.defaultTemplate = template;
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
BaseComponent.extends(Drawer);
|
|
85
|
+
BaseComponent.use(Drawer, HasEventEmitter);
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* @param {string} name
|
|
89
|
+
* @param {(d: Drawer) => Drawer} callback
|
|
90
|
+
*/
|
|
91
|
+
Drawer.preset = function(name, callback) {
|
|
92
|
+
if (Drawer.prototype[name] || Drawer[name]) {
|
|
93
|
+
DebugManager.warn(`Warning: the ${name} method already exist in Drawer.`);
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
Drawer[name] = (content, props) => callback(new Drawer(content, props));
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* @param {Record<string, (d: Drawer) => Drawer>} presets
|
|
101
|
+
*/
|
|
102
|
+
Drawer.presets = function(presets) {
|
|
103
|
+
for (const name in presets) {
|
|
104
|
+
Drawer.preset(name, presets[name]);
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Sets which edge the drawer slides in from
|
|
110
|
+
* @param {'left'|'right'|'top'|'bottom'} position
|
|
111
|
+
* @returns {this}
|
|
112
|
+
*/
|
|
113
|
+
Drawer.prototype.position = function(position) {
|
|
114
|
+
this.$description.position = position;
|
|
115
|
+
return this;
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Sets the drawer to slide in from the left
|
|
120
|
+
* @returns {this}
|
|
121
|
+
*/
|
|
122
|
+
Drawer.prototype.atLeft = function() {
|
|
123
|
+
return this.position('left');
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Sets the drawer to slide in from the right
|
|
128
|
+
* @returns {this}
|
|
129
|
+
*/
|
|
130
|
+
Drawer.prototype.atRight = function() {
|
|
131
|
+
return this.position('right');
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Sets the drawer to slide in from the top
|
|
136
|
+
* @returns {this}
|
|
137
|
+
*/
|
|
138
|
+
Drawer.prototype.atTop = function() {
|
|
139
|
+
return this.position('top');
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Sets the drawer to slide in from the bottom
|
|
144
|
+
* @returns {this}
|
|
145
|
+
*/
|
|
146
|
+
Drawer.prototype.atBottom = function() {
|
|
147
|
+
return this.position('bottom');
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Sets the width (for left/right) or height (for top/bottom) of the drawer
|
|
152
|
+
* @param {string} size - e.g. '400px', '30%'
|
|
153
|
+
* @returns {this}
|
|
154
|
+
*/
|
|
155
|
+
Drawer.prototype.size = function(size) {
|
|
156
|
+
this.$description.size = size;
|
|
157
|
+
return this;
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Sets the title of the drawer header
|
|
162
|
+
* @param {ValidChildren} title
|
|
163
|
+
* @returns {this}
|
|
164
|
+
*/
|
|
165
|
+
Drawer.prototype.title = function(title) {
|
|
166
|
+
this.$description.title = title;
|
|
167
|
+
return this;
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Sets the subtitle shown under the title
|
|
172
|
+
* @param {ValidChildren} subtitle
|
|
173
|
+
* @returns {this}
|
|
174
|
+
*/
|
|
175
|
+
Drawer.prototype.subtitle = function(subtitle) {
|
|
176
|
+
this.$description.subtitle = subtitle;
|
|
177
|
+
return this;
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Sets the content of the drawer
|
|
182
|
+
* @param {ValidChildren} content
|
|
183
|
+
* @returns {this}
|
|
184
|
+
*/
|
|
185
|
+
Drawer.prototype.content = function(content) {
|
|
186
|
+
this.$description.content = content;
|
|
187
|
+
return this;
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Sets the content render function
|
|
192
|
+
* @param {Function} callback
|
|
193
|
+
* @returns {this}
|
|
194
|
+
*/
|
|
195
|
+
Drawer.prototype.renderContent = function(callback) {
|
|
196
|
+
this.$description.renderContent = callback;
|
|
197
|
+
return this;
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Sets the footer render function (defaults to the action buttons row)
|
|
202
|
+
* @param {Function} callback
|
|
203
|
+
* @returns {this}
|
|
204
|
+
*/
|
|
205
|
+
Drawer.prototype.renderFooter = function(callback) {
|
|
206
|
+
this.$description.renderFooter = callback;
|
|
207
|
+
return this;
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Sets the header render function (defaults titl, subtitle and close button)
|
|
213
|
+
* @param {Function} callback
|
|
214
|
+
* @returns {this}
|
|
215
|
+
*/
|
|
216
|
+
Drawer.prototype.renderHeader = function(callback) {
|
|
217
|
+
this.$description.renderHeader = callback;
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Whether to render a dimmed overlay behind the drawer
|
|
222
|
+
* @param {boolean} [overlay=true]
|
|
223
|
+
* @returns {this}
|
|
224
|
+
*/
|
|
225
|
+
Drawer.prototype.overlay = function(overlay = true) {
|
|
226
|
+
this.$description.overlay = !!overlay;
|
|
227
|
+
return this;
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* @param {Boolean} backdrop
|
|
232
|
+
* @return {this}
|
|
233
|
+
*/
|
|
234
|
+
Drawer.prototype.overlay = function(backdrop = true) {
|
|
235
|
+
this.$description.backdrop = !!backdrop;
|
|
236
|
+
return this;
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Whether clicking the overlay closes the drawer
|
|
241
|
+
* @param {boolean} [closeOnOverlayClick=true]
|
|
242
|
+
* @returns {this}
|
|
243
|
+
*/
|
|
244
|
+
Drawer.prototype.closeOnOverlayClick = function(closeOnOverlayClick = true) {
|
|
245
|
+
this.$description.closeOnOverlayClick = !!closeOnOverlayClick;
|
|
246
|
+
return this;
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Whether the drawer shows a close (x) button and can be dismissed
|
|
251
|
+
* @param {boolean} [closable=true]
|
|
252
|
+
* @returns {this}
|
|
253
|
+
*/
|
|
254
|
+
Drawer.prototype.closable = function(closable = true) {
|
|
255
|
+
this.$description.closable = !!closable;
|
|
256
|
+
if(closable) {
|
|
257
|
+
this.showIf(closable);
|
|
258
|
+
}
|
|
259
|
+
return this;
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* @param {Observable} observable
|
|
264
|
+
* @return {Drawer}
|
|
265
|
+
*/
|
|
266
|
+
Drawer.prototype.isOpen = function(observable) {
|
|
267
|
+
this.$description.isOpen = observable;
|
|
268
|
+
observable.subscribe((isOpen) => {
|
|
269
|
+
this.emit(isOpen ? 'open' : 'close');
|
|
270
|
+
});
|
|
271
|
+
return this;
|
|
272
|
+
};
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Clears all footer action buttons
|
|
276
|
+
* @returns {this}
|
|
277
|
+
*/
|
|
278
|
+
Drawer.prototype.clearActions = function() {
|
|
279
|
+
this.$description.actions = [];
|
|
280
|
+
return this;
|
|
281
|
+
};
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Adds an action button to the drawer footer
|
|
285
|
+
* @param {string} label - The button label
|
|
286
|
+
* @param {Function} handler - The click handler
|
|
287
|
+
* @param {?string} variant - The button variant style (e.g. 'primary', 'secondary')
|
|
288
|
+
* @returns {this}
|
|
289
|
+
*/
|
|
290
|
+
Drawer.prototype.action = function(label, handler, variant = null) {
|
|
291
|
+
handler = handler || ((_, instance) => instance.close());
|
|
292
|
+
this.$description.actions.push({ label, handler, variant });
|
|
293
|
+
return this;
|
|
294
|
+
};
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* Opens the drawer
|
|
298
|
+
*/
|
|
299
|
+
Drawer.prototype.open = function() {
|
|
300
|
+
if(this.$description.isOpen.val() === true) {
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
this.$description.isOpen.set(true);
|
|
304
|
+
};
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* Closes the drawer
|
|
308
|
+
*/
|
|
309
|
+
Drawer.prototype.close = function() {
|
|
310
|
+
if(this.$description.isOpen.val() === false) {
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
this.$description.isOpen.set(false);
|
|
314
|
+
};
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* Alias for open()
|
|
318
|
+
*/
|
|
319
|
+
Drawer.prototype.show = Drawer.prototype.open;
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* Alias for close()
|
|
323
|
+
*/
|
|
324
|
+
Drawer.prototype.hide = Drawer.prototype.close;
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* Registers a handler for the open event
|
|
328
|
+
* @param {(instance: Drawer) => void} handler
|
|
329
|
+
* @returns {this}
|
|
330
|
+
*/
|
|
331
|
+
Drawer.prototype.onOpen = function(handler) {
|
|
332
|
+
this.on('open', handler);
|
|
333
|
+
return this;
|
|
334
|
+
};
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* Registers a handler for the close event
|
|
338
|
+
* @param {(instance: Drawer) => void} handler
|
|
339
|
+
* @returns {this}
|
|
340
|
+
*/
|
|
341
|
+
Drawer.prototype.onClose = function(handler) {
|
|
342
|
+
this.on('close', handler);
|
|
343
|
+
return this;
|
|
344
|
+
};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type {Observable, ValidChild} from '../../../../types/elements';
|
|
2
|
+
import type { ObservableItem } from '../../../../types/observable';
|
|
3
|
+
import type { BaseComponent } from '../../BaseComponent';
|
|
4
|
+
import type { GlobalAttributes } from '../../../../types/globals';
|
|
5
|
+
|
|
6
|
+
export type DrawerDescription = {
|
|
7
|
+
title: ValidChild | null;
|
|
8
|
+
subtitle: ValidChild | null;
|
|
9
|
+
content: ValidChild;
|
|
10
|
+
position: 'left' | 'right' | 'top' | 'bottom';
|
|
11
|
+
size: string;
|
|
12
|
+
overlay: boolean;
|
|
13
|
+
closeOnOverlayClick: boolean;
|
|
14
|
+
closable: boolean;
|
|
15
|
+
actions: Array<{
|
|
16
|
+
label: ValidChild;
|
|
17
|
+
handler: ((event: Event, instance: DrawerInterface) => void) | null;
|
|
18
|
+
variant: string | null;
|
|
19
|
+
}>;
|
|
20
|
+
contentRender: ((desc: DrawerDescription, instance: DrawerInterface) => ValidChild) | null;
|
|
21
|
+
footerRender: ((desc: DrawerDescription, instance: DrawerInterface) => ValidChild) | null;
|
|
22
|
+
visible: ObservableItem<boolean> | null;
|
|
23
|
+
props: GlobalAttributes;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export interface DrawerInterface extends BaseComponent {
|
|
27
|
+
position(position: 'left' | 'right' | 'top' | 'bottom'): this;
|
|
28
|
+
atLeft(): this;
|
|
29
|
+
atRight(): this;
|
|
30
|
+
atTop(): this;
|
|
31
|
+
atBottom(): this;
|
|
32
|
+
size(size: string): this;
|
|
33
|
+
title(title: ValidChild): this;
|
|
34
|
+
subtitle(subtitle: ValidChild): this;
|
|
35
|
+
content(content: ValidChild): this;
|
|
36
|
+
renderContent(template: (description: DrawerDescription, instance: DrawerInterface) => ValidChild): this;
|
|
37
|
+
renderFooter(template: (description: DrawerDescription, instance: DrawerInterface) => ValidChild): this;
|
|
38
|
+
renderHeader(template: (description: DrawerDescription, instance: DrawerInterface) => ValidChild): this;
|
|
39
|
+
overlay(overlay?: boolean): this;
|
|
40
|
+
closeOnOverlayClick(closeOnOverlayClick?: boolean): this;
|
|
41
|
+
closable(closable?: boolean): this;
|
|
42
|
+
isOpen(closable: Observable<Boolean>): this;
|
|
43
|
+
clearActions(): this;
|
|
44
|
+
action(label: string, handler?: ((event: Event, instance: DrawerInterface) => void) | null, variant?: string | null): this;
|
|
45
|
+
open(): void;
|
|
46
|
+
close(): void;
|
|
47
|
+
show(): void;
|
|
48
|
+
hide(): void;
|
|
49
|
+
onOpen(handler: (instance: DrawerInterface) => void): this;
|
|
50
|
+
onClose(handler: (instance: DrawerInterface) => void): this;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
export declare function Drawer(props?: Record<string, unknown>, content?: ValidChild): DrawerInterface;
|
|
55
|
+
export declare namespace Drawer {
|
|
56
|
+
|
|
57
|
+
function use(template: (description: DrawerDescription, instance: DrawerInterface) => ValidChild): void;
|
|
58
|
+
function preset(name: string, callback: (instance: DrawerInterface) => DrawerInterface): void;
|
|
59
|
+
function presets(presets: Record<string, (instance: DrawerInterface) => DrawerInterface>): void;
|
|
60
|
+
|
|
61
|
+
}
|
|
@@ -162,6 +162,7 @@ export type { ToastWarning, ToastWarningInterface } from './toast/types/ToastWar
|
|
|
162
162
|
|
|
163
163
|
// --- Tooltip -----------------------------------------------------------------
|
|
164
164
|
export type { Tooltip, TooltipInterface, TooltipDescription } from './tooltip/types/Tooltip';
|
|
165
|
+
export type { Drawer, DrawerDescription, DrawerInterface } from './drawer/types/Drawer';
|
|
165
166
|
|
|
166
167
|
|
|
167
168
|
// --- Icon -----------------------------------------------------------------
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import {Aside, Div, H2, Header, Paragraph, Button, Footer, ShowIf} from '../../../core/elements';
|
|
2
|
+
import { $ } from '../../../core/data/Observable';
|
|
3
|
+
|
|
4
|
+
import './drawer.css';
|
|
5
|
+
|
|
6
|
+
export default function DrawerRender($desc, instance) {
|
|
7
|
+
const $isOpen = $desc.isOpen ?? $(false);
|
|
8
|
+
if(!$desc.isOpen) {
|
|
9
|
+
instance.isOpen($isOpen);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const props = instance.getEditableProps();
|
|
13
|
+
props.class.add('drawer-container');
|
|
14
|
+
|
|
15
|
+
if($desc.backdrop) {
|
|
16
|
+
props.class.add('drawer-has-backdrop');
|
|
17
|
+
}
|
|
18
|
+
props.class.add('at-'+$desc.position);
|
|
19
|
+
props.class.add('is-open', $isOpen);
|
|
20
|
+
|
|
21
|
+
const closeFn = () => {
|
|
22
|
+
$isOpen.set(false);
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
return ShowIf($isOpen, () => {
|
|
26
|
+
return Div({ ...instance.resolveProps() }, [
|
|
27
|
+
$desc.backdrop ? Div({ class: 'drawer-backdrop' }) : null,
|
|
28
|
+
|
|
29
|
+
Aside({ class: 'drawer-panel' },
|
|
30
|
+
Div({ class: 'drawer-panel-container' }, [
|
|
31
|
+
Header({ class: 'drawer-header' }, buildDrawerHeader($desc, instance, closeFn)),
|
|
32
|
+
buildDrawerBody($desc, instance, closeFn),
|
|
33
|
+
buildDrawerFooter($desc, instance, closeFn),
|
|
34
|
+
])
|
|
35
|
+
),
|
|
36
|
+
]);
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const buildDrawerHeader = ($desc, instance, closeFn) => {
|
|
41
|
+
if($desc.renderHeader) {
|
|
42
|
+
return $desc.renderHeader($desc, instance, closeFn);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if(!($desc.title || $desc.subtitle) && !$desc.closable) {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return [
|
|
50
|
+
Div({ class: 'drawer-title-container' }, [
|
|
51
|
+
H2({ class: 'drawer-title' }, $desc.title),
|
|
52
|
+
$desc.subtitle ? Paragraph({ class: 'drawer-sub-title' }, $desc.subtitle) : null,
|
|
53
|
+
]),
|
|
54
|
+
$desc.closable
|
|
55
|
+
? Button({ type: 'button', class: 'drawer-close', 'aria-label': 'Close menu' }, 'X').onClick(closeFn)
|
|
56
|
+
: null,
|
|
57
|
+
];
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const buildDrawerBody = ($desc, instance, closeFn) => {
|
|
61
|
+
if($desc.renderContent) {
|
|
62
|
+
return Div({ class: 'drawer-body' }, $desc.renderContent($desc, instance, closeFn));
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return Div({ class: 'drawer-body' }, $desc.content);
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
const buildDrawerFooter = ($desc, instance, closeFn) => {
|
|
69
|
+
if($desc.renderFooter) {
|
|
70
|
+
return Footer({ class: 'drawer-footer' }, $desc.renderFooter($desc, instance, closeFn));
|
|
71
|
+
}
|
|
72
|
+
if($desc.footerContent) {
|
|
73
|
+
return Footer({ class: 'drawer-footer' }, $desc.footerContent);
|
|
74
|
+
}
|
|
75
|
+
return null;
|
|
76
|
+
};
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
.drawer-container {
|
|
2
|
+
position: fixed;
|
|
3
|
+
z-index: 1000;
|
|
4
|
+
display: none;
|
|
5
|
+
inset: 0;
|
|
6
|
+
pointer-events: none
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
.drawer-container.is-open {
|
|
10
|
+
display: block;
|
|
11
|
+
pointer-events: auto;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
/* Backdrop ------------------------------------------------------------------------------------------*/
|
|
16
|
+
.drawer-backdrop {
|
|
17
|
+
position: absolute;
|
|
18
|
+
inset: 0;
|
|
19
|
+
background: rgba(0, 0, 0, var(--opacity-backdrop));
|
|
20
|
+
transition: opacity 0.25s ease;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/* Panel ------------------------------------------------------------------------------------------*/
|
|
24
|
+
.drawer-panel {
|
|
25
|
+
position: absolute;
|
|
26
|
+
top: 0;
|
|
27
|
+
bottom: 0;
|
|
28
|
+
transition: transform 0.3s ease;
|
|
29
|
+
max-width: 100%;
|
|
30
|
+
max-height: 100%;
|
|
31
|
+
display: flex;
|
|
32
|
+
width: 450px;
|
|
33
|
+
border-radius: var(--radius-large);
|
|
34
|
+
background: hsl(from var(--gray-lite-1) h s l / .5);
|
|
35
|
+
margin: var(--space-cozy)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.drawer-panel-container {
|
|
39
|
+
flex-direction: column;
|
|
40
|
+
justify-content: space-between;
|
|
41
|
+
flex: 1;
|
|
42
|
+
display: flex;
|
|
43
|
+
background: var(--gray-lite-4);
|
|
44
|
+
border-radius: var(--radius-large);
|
|
45
|
+
margin: var(--space-cozy);
|
|
46
|
+
box-shadow: 0 0 20px rgba(0, 0, 0, 0.2);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
/* Positions ------------------------------------------------------------------------------------------*/
|
|
51
|
+
/* Right */
|
|
52
|
+
.drawer-container.at-right .drawer-panel {
|
|
53
|
+
top: 0;
|
|
54
|
+
right: 0;
|
|
55
|
+
bottom: 0;
|
|
56
|
+
transform: translateX(100%);
|
|
57
|
+
}
|
|
58
|
+
.drawer-container.at-right.is-open .drawer-panel {
|
|
59
|
+
transform: translateX(0);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/* Left */
|
|
63
|
+
.drawer-container.at-left .drawer-panel {
|
|
64
|
+
top: 0;
|
|
65
|
+
left: 0;
|
|
66
|
+
bottom: 0;
|
|
67
|
+
width: 360px;
|
|
68
|
+
transform: translateX(-100%);
|
|
69
|
+
}
|
|
70
|
+
.drawer-container.at-left.is-open .drawer-panel {
|
|
71
|
+
transform: translateX(0);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/* Top */
|
|
75
|
+
.drawer-container.at-top .drawer-panel {
|
|
76
|
+
top: 0;
|
|
77
|
+
left: 0;
|
|
78
|
+
right: 0;
|
|
79
|
+
bottom: unset;
|
|
80
|
+
/*min-height: 280px;*/
|
|
81
|
+
width: 100%;
|
|
82
|
+
transform: translateY(-100%);
|
|
83
|
+
}
|
|
84
|
+
.drawer-container.at-top.is-open .drawer-panel {
|
|
85
|
+
transform: translateY(0);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/* Bottom */
|
|
89
|
+
.drawer-container.at-bottom .drawer-panel {
|
|
90
|
+
top: unset;
|
|
91
|
+
left: 0;
|
|
92
|
+
right: 0;
|
|
93
|
+
bottom: 0;
|
|
94
|
+
width: 100%;
|
|
95
|
+
/*min-height: 300px;*/
|
|
96
|
+
transform: translateY(100%);
|
|
97
|
+
}
|
|
98
|
+
.drawer-container.at-bottom.is-open .drawer-panel {
|
|
99
|
+
transform: translateY(0);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
.drawer-container.drawer-overlay .drawer-panel {
|
|
103
|
+
z-index: 2;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/* Header -----------------------------------------------------------------------------------------------*/
|
|
107
|
+
|
|
108
|
+
.drawer-header {
|
|
109
|
+
display: flex;
|
|
110
|
+
justify-content: space-between;
|
|
111
|
+
align-items: center;
|
|
112
|
+
gap: 12px;
|
|
113
|
+
padding: 16px 20px;
|
|
114
|
+
flex-shrink: 0;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
.drawer-title-container {
|
|
118
|
+
display: flex;
|
|
119
|
+
flex-direction: column;
|
|
120
|
+
gap: 4px;
|
|
121
|
+
min-width: 0;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
.drawer-title {
|
|
125
|
+
margin: 0;
|
|
126
|
+
font-size: 18px;
|
|
127
|
+
font-weight: 600;
|
|
128
|
+
color: #111;
|
|
129
|
+
overflow: hidden;
|
|
130
|
+
text-overflow: ellipsis;
|
|
131
|
+
white-space: nowrap;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
.drawer-sub-title {
|
|
135
|
+
margin: 0;
|
|
136
|
+
font-size: 13px;
|
|
137
|
+
color: #6b7280;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
.drawer-close {
|
|
141
|
+
flex-shrink: 0;
|
|
142
|
+
width: 32px;
|
|
143
|
+
height: 32px;
|
|
144
|
+
display: inline-flex;
|
|
145
|
+
align-items: center;
|
|
146
|
+
justify-content: center;
|
|
147
|
+
border: none;
|
|
148
|
+
background: transparent;
|
|
149
|
+
font-size: 16px;
|
|
150
|
+
line-height: 1;
|
|
151
|
+
cursor: pointer;
|
|
152
|
+
color: #6b7280;
|
|
153
|
+
transition: background 0.15s ease, color 0.15s ease;
|
|
154
|
+
border-radius: var(--radius-round);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
.drawer-close:hover {
|
|
158
|
+
background: #f3f4f6;
|
|
159
|
+
color: #111;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/* Body ------------------------------------------------------------------------------------------*/
|
|
163
|
+
.drawer-body {
|
|
164
|
+
flex: 1;
|
|
165
|
+
overflow-y: auto;
|
|
166
|
+
padding: 20px;
|
|
167
|
+
background: var(--gray-lite-3);
|
|
168
|
+
margin: var(--space-cozy);
|
|
169
|
+
border-radius: var(--radius-large);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/* Footer ------------------------------------------------------------------------------------------*/
|
|
173
|
+
.drawer-footer {
|
|
174
|
+
flex-shrink: 0;
|
|
175
|
+
padding: 16px 20px;
|
|
176
|
+
display: flex;
|
|
177
|
+
justify-content: center;
|
|
178
|
+
gap: 8px;
|
|
179
|
+
margin: var(--space-cozy);
|
|
180
|
+
}
|
package/src/ui/index.js
CHANGED
|
@@ -44,6 +44,7 @@ export { default as ListItemRender } from './components/list/item/ListItemRender
|
|
|
44
44
|
export { default as ListDividerRender } from './components/list/divider/ListDividerRender';
|
|
45
45
|
|
|
46
46
|
export { default as CardRender } from './components/card/CardRender';
|
|
47
|
+
export { default as DrawerRender } from './components/drawer/DrawerRender';
|
|
47
48
|
|
|
48
49
|
|
|
49
50
|
export { default as TablerIconRender } from './components/icon/tabler/TablerIconRender';
|