@lowdefy/blocks-antd 5.0.0 → 5.2.0
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/dist/blocks/AutoComplete/AutoComplete.js +6 -0
- package/dist/blocks/AutoComplete/meta.js +1 -0
- package/dist/blocks/Calendar/Calendar.js +60 -18
- package/dist/blocks/Calendar/meta.js +1 -1
- package/dist/blocks/ControlledList/ControlledList.js +40 -15
- package/dist/blocks/ControlledList/meta.js +6 -2
- package/dist/blocks/ControlledList/style.module.css +35 -0
- package/dist/blocks/Drawer/Drawer.js +2 -0
- package/dist/blocks/Drawer/meta.js +2 -1
- package/dist/blocks/DropdownButton/DropdownButton.js +13 -5
- package/dist/blocks/DropdownButton/meta.js +3 -3
- package/dist/blocks/Menu/Menu.js +6 -3
- package/dist/blocks/MobileMenu/MobileMenu.js +16 -3
- package/dist/blocks/MobileMenu/meta.js +32 -1
- package/dist/blocks/MultipleSelector/MultipleSelector.js +9 -2
- package/dist/blocks/MultipleSelector/meta.js +1 -0
- package/dist/blocks/PageSidebarLayout/PageSidebarLayout.js +517 -0
- package/dist/blocks/PageSidebarLayout/e2e.js +34 -0
- package/dist/blocks/PageSidebarLayout/meta.js +526 -0
- package/dist/blocks/PageSiderMenu/PageSiderMenu.js +39 -6
- package/dist/blocks/PageSiderMenu/meta.js +10 -0
- package/dist/blocks/Selector/Selector.js +6 -0
- package/dist/blocks/Selector/meta.js +1 -0
- package/dist/blocks/Sider/Sider.js +10 -0
- package/dist/blocks/headerActions.js +131 -28
- package/dist/blocks.js +1 -0
- package/dist/metas.js +1 -0
- package/package.json +7 -7
|
@@ -0,0 +1,526 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2020-2026 Lowdefy, Inc
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this file except in compliance with the License.
|
|
6
|
+
You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
|
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
See the License for the specific language governing permissions and
|
|
14
|
+
limitations under the License.
|
|
15
|
+
*/ import MobileMenuMeta from '../MobileMenu/meta.js';
|
|
16
|
+
import icon from '../../schemas/icon.js';
|
|
17
|
+
import menuLinks from '../../schemas/menuLinks.js';
|
|
18
|
+
import breadcrumbList from '../../schemas/breadcrumbList.js';
|
|
19
|
+
export default {
|
|
20
|
+
category: 'container',
|
|
21
|
+
icons: [
|
|
22
|
+
'AiOutlineBell',
|
|
23
|
+
'AiOutlineLaptop',
|
|
24
|
+
'AiOutlineMenuFold',
|
|
25
|
+
'AiOutlineMenuUnfold',
|
|
26
|
+
'AiOutlineMoon',
|
|
27
|
+
'AiOutlineSun',
|
|
28
|
+
'AiOutlineUser',
|
|
29
|
+
...MobileMenuMeta.icons
|
|
30
|
+
],
|
|
31
|
+
valueType: null,
|
|
32
|
+
slots: {
|
|
33
|
+
content: 'Main page content.',
|
|
34
|
+
footer: 'Page footer.',
|
|
35
|
+
header: 'Additional header content.',
|
|
36
|
+
mobileDrawerContent: 'Content in the mobile menu drawer.',
|
|
37
|
+
mobileDrawerFooter: 'Footer in the mobile menu drawer.',
|
|
38
|
+
mobileExtra: 'Extra content in the mobile header bar.',
|
|
39
|
+
siderClosed: 'Content shown in the sider when collapsed.',
|
|
40
|
+
siderOpen: 'Content shown in the sider when expanded.'
|
|
41
|
+
},
|
|
42
|
+
cssKeys: {
|
|
43
|
+
element: 'The PageSidebarLayout element.',
|
|
44
|
+
sider: 'The PageSidebarLayout sider.',
|
|
45
|
+
menu: 'The PageSidebarLayout menu.',
|
|
46
|
+
mobileHeader: 'The PageSidebarLayout mobile header.',
|
|
47
|
+
mobileMenu: 'The PageSidebarLayout mobile menu.',
|
|
48
|
+
header: 'The PageSidebarLayout header.',
|
|
49
|
+
headerActions: 'The header actions container (notifications, profile, dark mode toggle).',
|
|
50
|
+
logo: 'The PageSidebarLayout logo.',
|
|
51
|
+
notifications: 'The notification bell button.',
|
|
52
|
+
notificationsBadge: 'The notification badge wrapper.',
|
|
53
|
+
notificationsIcon: 'The notification bell icon.',
|
|
54
|
+
profile: 'The profile avatar and dropdown wrapper.',
|
|
55
|
+
profileAvatar: 'The profile avatar element.',
|
|
56
|
+
profileMenu: 'The profile dropdown menu popup.',
|
|
57
|
+
darkModeToggle: 'The dark mode toggle button.',
|
|
58
|
+
content: 'The PageSidebarLayout content.',
|
|
59
|
+
breadcrumb: 'The PageSidebarLayout breadcrumb.',
|
|
60
|
+
footer: 'The PageSidebarLayout footer.',
|
|
61
|
+
toggleButton: 'The PageSidebarLayout sider toggle button.'
|
|
62
|
+
},
|
|
63
|
+
events: {
|
|
64
|
+
onToggleSider: 'Trigger action when sider toggle button is clicked.',
|
|
65
|
+
onMenuItemClick: 'Trigger action when menu item is clicked.',
|
|
66
|
+
onMenuItemSelect: 'Trigger action when menu item is selected.',
|
|
67
|
+
onToggleMenuGroup: 'Trigger action when menu group is opened.',
|
|
68
|
+
onBreadcrumbClick: 'Trigger action when a breadcrumb item is clicked.',
|
|
69
|
+
onMobileMenuOpen: 'Trigger action when mobile menu is opened.',
|
|
70
|
+
onMobileMenuClose: 'Trigger action when mobile menu is closed.',
|
|
71
|
+
onToggleDrawer: 'Trigger action when mobile menu drawer is toggled.',
|
|
72
|
+
onProfileMenuClick: {
|
|
73
|
+
description: 'Trigger action when a profile dropdown menu item is clicked.',
|
|
74
|
+
event: {
|
|
75
|
+
key: 'The menu item key (id).',
|
|
76
|
+
keyPath: 'The key path of the menu item.',
|
|
77
|
+
pageId: 'The page id of the menu item.',
|
|
78
|
+
url: 'The url of the menu item.'
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
onProfileMenuOpen: {
|
|
82
|
+
description: 'Trigger action when the profile dropdown opens or closes.',
|
|
83
|
+
event: {
|
|
84
|
+
open: 'Whether the dropdown is open.'
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
properties: {
|
|
89
|
+
type: 'object',
|
|
90
|
+
additionalProperties: false,
|
|
91
|
+
properties: {
|
|
92
|
+
title: {
|
|
93
|
+
type: 'string',
|
|
94
|
+
description: 'Page title. Accepted for compatibility.'
|
|
95
|
+
},
|
|
96
|
+
theme: {
|
|
97
|
+
type: 'object',
|
|
98
|
+
description: 'Antd design token overrides for this block. See <a href="https://ant.design/components/overview#design-token">antd design tokens</a>.',
|
|
99
|
+
docs: {
|
|
100
|
+
displayType: 'yaml'
|
|
101
|
+
}
|
|
102
|
+
},
|
|
103
|
+
logo: {
|
|
104
|
+
type: 'object',
|
|
105
|
+
description: 'Header logo settings. By default, images are served from the app public folder and auto-swap between light and dark variants based on dark mode. See <a href="/hosting-files">Hosting Files</a> for details.',
|
|
106
|
+
additionalProperties: false,
|
|
107
|
+
properties: {
|
|
108
|
+
src: {
|
|
109
|
+
type: 'string',
|
|
110
|
+
description: 'Logo image URL for desktop. Defaults to logo-light-theme.png or logo-dark-theme.png from the public folder (~250x72px), auto-selected based on dark mode.'
|
|
111
|
+
},
|
|
112
|
+
srcMobile: {
|
|
113
|
+
type: 'string',
|
|
114
|
+
description: 'Logo image URL for mobile. Defaults to logo-square-light-theme.png or logo-square-dark-theme.png from the public folder (~125x125px), auto-selected based on dark mode.'
|
|
115
|
+
},
|
|
116
|
+
alt: {
|
|
117
|
+
type: 'string',
|
|
118
|
+
default: 'Lowdefy',
|
|
119
|
+
description: 'Logo alternative text.'
|
|
120
|
+
},
|
|
121
|
+
style: {
|
|
122
|
+
type: 'object',
|
|
123
|
+
description: 'Css style object to apply to logo.',
|
|
124
|
+
docs: {
|
|
125
|
+
displayType: 'yaml'
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
},
|
|
130
|
+
sider: {
|
|
131
|
+
type: 'object',
|
|
132
|
+
description: 'Sider properties.',
|
|
133
|
+
additionalProperties: false,
|
|
134
|
+
properties: {
|
|
135
|
+
collapsedWidth: {
|
|
136
|
+
type: 'integer',
|
|
137
|
+
description: 'Width of the collapsed sidebar, by setting to 0 a special trigger will appear.'
|
|
138
|
+
},
|
|
139
|
+
collapsible: {
|
|
140
|
+
type: 'boolean',
|
|
141
|
+
default: true,
|
|
142
|
+
description: 'Whether can be collapsed.'
|
|
143
|
+
},
|
|
144
|
+
initialCollapsed: {
|
|
145
|
+
type: 'boolean',
|
|
146
|
+
default: false,
|
|
147
|
+
description: 'Set the initial collapsed state.'
|
|
148
|
+
},
|
|
149
|
+
width: {
|
|
150
|
+
type: [
|
|
151
|
+
'string',
|
|
152
|
+
'number'
|
|
153
|
+
],
|
|
154
|
+
description: 'Width of the sidebar.',
|
|
155
|
+
docs: {
|
|
156
|
+
displayType: 'string'
|
|
157
|
+
}
|
|
158
|
+
},
|
|
159
|
+
hideToggleButton: {
|
|
160
|
+
type: 'boolean',
|
|
161
|
+
description: 'Hide toggle button in sider.',
|
|
162
|
+
default: false
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
},
|
|
166
|
+
siderStorageKey: {
|
|
167
|
+
type: 'string',
|
|
168
|
+
default: 'sider',
|
|
169
|
+
description: "localStorage key suffix for sider state persistence. Produces key 'lf-{siderStorageKey}-open'."
|
|
170
|
+
},
|
|
171
|
+
header: {
|
|
172
|
+
type: 'object',
|
|
173
|
+
description: 'Header properties.',
|
|
174
|
+
additionalProperties: false,
|
|
175
|
+
properties: {
|
|
176
|
+
contentStyle: {
|
|
177
|
+
type: 'object',
|
|
178
|
+
description: 'Header content css style object.',
|
|
179
|
+
docs: {
|
|
180
|
+
displayType: 'yaml'
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
},
|
|
185
|
+
toggleSiderButton: {
|
|
186
|
+
type: 'object',
|
|
187
|
+
description: 'Toggle sider button properties.',
|
|
188
|
+
docs: {
|
|
189
|
+
displayType: 'button'
|
|
190
|
+
}
|
|
191
|
+
},
|
|
192
|
+
footer: {
|
|
193
|
+
type: 'object',
|
|
194
|
+
description: 'Footer properties.',
|
|
195
|
+
additionalProperties: false,
|
|
196
|
+
properties: {
|
|
197
|
+
style: {
|
|
198
|
+
type: 'object',
|
|
199
|
+
description: 'Footer css style object.',
|
|
200
|
+
docs: {
|
|
201
|
+
displayType: 'yaml'
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
},
|
|
206
|
+
content: {
|
|
207
|
+
type: 'object',
|
|
208
|
+
description: 'Content properties.',
|
|
209
|
+
additionalProperties: false,
|
|
210
|
+
properties: {
|
|
211
|
+
style: {
|
|
212
|
+
type: 'object',
|
|
213
|
+
description: 'Content css style object.',
|
|
214
|
+
docs: {
|
|
215
|
+
displayType: 'yaml'
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
},
|
|
220
|
+
breadcrumb: {
|
|
221
|
+
type: 'object',
|
|
222
|
+
description: 'Breadcrumb properties.',
|
|
223
|
+
properties: {
|
|
224
|
+
separator: {
|
|
225
|
+
type: 'string',
|
|
226
|
+
default: '/',
|
|
227
|
+
description: 'Use a custom separator string.'
|
|
228
|
+
},
|
|
229
|
+
list: breadcrumbList
|
|
230
|
+
}
|
|
231
|
+
},
|
|
232
|
+
menu: {
|
|
233
|
+
type: 'object',
|
|
234
|
+
description: 'Menu properties.',
|
|
235
|
+
properties: {
|
|
236
|
+
links: menuLinks
|
|
237
|
+
}
|
|
238
|
+
},
|
|
239
|
+
menuLg: {
|
|
240
|
+
type: 'object',
|
|
241
|
+
description: 'Menu large screen properties. Overwrites menu properties on desktop screen sizes.',
|
|
242
|
+
docs: {
|
|
243
|
+
displayType: 'yaml'
|
|
244
|
+
}
|
|
245
|
+
},
|
|
246
|
+
menuMd: {
|
|
247
|
+
type: 'object',
|
|
248
|
+
description: 'Mobile menu properties. Overwrites menu properties on mobile screen sizes.',
|
|
249
|
+
docs: {
|
|
250
|
+
displayType: 'yaml'
|
|
251
|
+
}
|
|
252
|
+
},
|
|
253
|
+
notifications: {
|
|
254
|
+
type: 'object',
|
|
255
|
+
description: 'Notification bell icon with badge. Shown in the sider on desktop and the mobile header on small screens. Renders when configured. Use the link property to navigate when clicked.',
|
|
256
|
+
additionalProperties: false,
|
|
257
|
+
properties: {
|
|
258
|
+
title: {
|
|
259
|
+
type: 'string',
|
|
260
|
+
default: 'Notifications',
|
|
261
|
+
description: 'Label shown next to the bell icon when the sider is expanded. Hidden on mobile header and collapsed sider.'
|
|
262
|
+
},
|
|
263
|
+
link: {
|
|
264
|
+
type: 'object',
|
|
265
|
+
description: 'Link to navigate to when the notification bell is clicked.',
|
|
266
|
+
additionalProperties: false,
|
|
267
|
+
properties: {
|
|
268
|
+
pageId: {
|
|
269
|
+
type: 'string',
|
|
270
|
+
description: 'Page to link to.'
|
|
271
|
+
},
|
|
272
|
+
url: {
|
|
273
|
+
type: 'string',
|
|
274
|
+
description: 'External URL to link to.'
|
|
275
|
+
},
|
|
276
|
+
newTab: {
|
|
277
|
+
type: 'boolean',
|
|
278
|
+
description: 'Open link in new tab.'
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
},
|
|
282
|
+
count: {
|
|
283
|
+
type: 'number',
|
|
284
|
+
description: 'Number to display on the badge. Set to 0 to hide the badge (unless showZero is true).'
|
|
285
|
+
},
|
|
286
|
+
dot: {
|
|
287
|
+
type: 'boolean',
|
|
288
|
+
default: false,
|
|
289
|
+
description: 'Show a dot instead of a count number.'
|
|
290
|
+
},
|
|
291
|
+
showZero: {
|
|
292
|
+
type: 'boolean',
|
|
293
|
+
default: false,
|
|
294
|
+
description: 'Show badge when count is zero.'
|
|
295
|
+
},
|
|
296
|
+
overflowCount: {
|
|
297
|
+
type: 'number',
|
|
298
|
+
default: 99,
|
|
299
|
+
description: 'Max count to show. Values above this display as "N+".'
|
|
300
|
+
},
|
|
301
|
+
color: {
|
|
302
|
+
type: 'string',
|
|
303
|
+
description: 'Badge color.',
|
|
304
|
+
docs: {
|
|
305
|
+
displayType: 'color'
|
|
306
|
+
}
|
|
307
|
+
},
|
|
308
|
+
icon: {
|
|
309
|
+
...icon,
|
|
310
|
+
description: 'Icon for the notification button. Defaults to AiOutlineBell.'
|
|
311
|
+
},
|
|
312
|
+
size: {
|
|
313
|
+
type: 'string',
|
|
314
|
+
enum: [
|
|
315
|
+
'small',
|
|
316
|
+
'default',
|
|
317
|
+
'large'
|
|
318
|
+
],
|
|
319
|
+
default: 'small',
|
|
320
|
+
description: 'Size of the notification button.'
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
},
|
|
324
|
+
profile: {
|
|
325
|
+
type: 'object',
|
|
326
|
+
description: 'Profile avatar with optional dropdown menu. Shown in the sider on desktop and the mobile header on small screens. Renders when configured. Use with the _user operator to populate from the authenticated user.',
|
|
327
|
+
additionalProperties: false,
|
|
328
|
+
properties: {
|
|
329
|
+
title: {
|
|
330
|
+
type: 'string',
|
|
331
|
+
default: 'Profile',
|
|
332
|
+
description: 'Label shown next to the avatar when the sider is expanded. Hidden on mobile header and collapsed sider.'
|
|
333
|
+
},
|
|
334
|
+
avatar: {
|
|
335
|
+
type: 'object',
|
|
336
|
+
description: 'Avatar display properties.',
|
|
337
|
+
additionalProperties: false,
|
|
338
|
+
properties: {
|
|
339
|
+
src: {
|
|
340
|
+
type: 'string',
|
|
341
|
+
description: 'Image URL for the avatar. Typically bound to _user: image.'
|
|
342
|
+
},
|
|
343
|
+
content: {
|
|
344
|
+
type: 'string',
|
|
345
|
+
description: 'Text content inside the avatar (e.g. user initials). Shown when no src is provided.'
|
|
346
|
+
},
|
|
347
|
+
icon: {
|
|
348
|
+
...icon,
|
|
349
|
+
description: 'Icon to display in avatar when no src or content is set. Defaults to AiOutlineUser.'
|
|
350
|
+
},
|
|
351
|
+
color: {
|
|
352
|
+
type: 'string',
|
|
353
|
+
description: 'Background color of the avatar when not using src.',
|
|
354
|
+
docs: {
|
|
355
|
+
displayType: 'color'
|
|
356
|
+
}
|
|
357
|
+
},
|
|
358
|
+
size: {
|
|
359
|
+
type: [
|
|
360
|
+
'string',
|
|
361
|
+
'number'
|
|
362
|
+
],
|
|
363
|
+
default: 'small',
|
|
364
|
+
enum: [
|
|
365
|
+
'default',
|
|
366
|
+
'small',
|
|
367
|
+
'large'
|
|
368
|
+
],
|
|
369
|
+
description: 'Size of the avatar.',
|
|
370
|
+
docs: {
|
|
371
|
+
displayType: 'string'
|
|
372
|
+
}
|
|
373
|
+
},
|
|
374
|
+
shape: {
|
|
375
|
+
type: 'string',
|
|
376
|
+
enum: [
|
|
377
|
+
'circle',
|
|
378
|
+
'square'
|
|
379
|
+
],
|
|
380
|
+
default: 'circle',
|
|
381
|
+
description: 'Shape of the avatar.'
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
},
|
|
385
|
+
links: {
|
|
386
|
+
type: 'array',
|
|
387
|
+
description: 'Dropdown menu items. Uses the same MenuLink/MenuGroup/MenuDivider schema as Menu. Compatible with _menu operator output for access-filtered menus.',
|
|
388
|
+
items: {
|
|
389
|
+
type: 'object',
|
|
390
|
+
required: [
|
|
391
|
+
'id',
|
|
392
|
+
'type'
|
|
393
|
+
],
|
|
394
|
+
properties: {
|
|
395
|
+
id: {
|
|
396
|
+
type: 'string',
|
|
397
|
+
description: 'Menu item id.'
|
|
398
|
+
},
|
|
399
|
+
type: {
|
|
400
|
+
type: 'string',
|
|
401
|
+
enum: [
|
|
402
|
+
'MenuDivider',
|
|
403
|
+
'MenuLink',
|
|
404
|
+
'MenuGroup'
|
|
405
|
+
],
|
|
406
|
+
default: 'MenuLink',
|
|
407
|
+
description: 'Menu item type.'
|
|
408
|
+
},
|
|
409
|
+
pageId: {
|
|
410
|
+
type: 'string',
|
|
411
|
+
description: 'Page to link to.'
|
|
412
|
+
},
|
|
413
|
+
url: {
|
|
414
|
+
type: 'string',
|
|
415
|
+
description: 'External URL to link to.'
|
|
416
|
+
},
|
|
417
|
+
newTab: {
|
|
418
|
+
type: 'boolean',
|
|
419
|
+
description: 'Open link in new tab.'
|
|
420
|
+
},
|
|
421
|
+
style: {
|
|
422
|
+
type: 'object',
|
|
423
|
+
description: 'CSS style applied to the link.',
|
|
424
|
+
docs: {
|
|
425
|
+
displayType: 'yaml'
|
|
426
|
+
}
|
|
427
|
+
},
|
|
428
|
+
properties: {
|
|
429
|
+
type: 'object',
|
|
430
|
+
description: 'Properties for the menu item.',
|
|
431
|
+
properties: {
|
|
432
|
+
title: {
|
|
433
|
+
type: 'string',
|
|
434
|
+
description: 'Menu item title.'
|
|
435
|
+
},
|
|
436
|
+
icon: {
|
|
437
|
+
...icon,
|
|
438
|
+
description: 'Icon for the menu item.'
|
|
439
|
+
},
|
|
440
|
+
danger: {
|
|
441
|
+
type: 'boolean',
|
|
442
|
+
default: false,
|
|
443
|
+
description: 'Apply danger style to menu item.'
|
|
444
|
+
},
|
|
445
|
+
disabled: {
|
|
446
|
+
type: 'boolean',
|
|
447
|
+
default: false,
|
|
448
|
+
description: 'Disable the menu item.'
|
|
449
|
+
},
|
|
450
|
+
dashed: {
|
|
451
|
+
type: 'boolean',
|
|
452
|
+
default: false,
|
|
453
|
+
description: 'Whether the divider line is dashed.'
|
|
454
|
+
},
|
|
455
|
+
shortcut: {
|
|
456
|
+
type: 'string',
|
|
457
|
+
description: 'Keyboard shortcut. Renders a shortcut badge next to the label. Use "mod" for Cmd/Ctrl.'
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
},
|
|
461
|
+
links: {
|
|
462
|
+
type: 'array',
|
|
463
|
+
description: 'Nested menu items for MenuGroup.'
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
},
|
|
468
|
+
trigger: {
|
|
469
|
+
type: 'string',
|
|
470
|
+
enum: [
|
|
471
|
+
'click',
|
|
472
|
+
'hover'
|
|
473
|
+
],
|
|
474
|
+
default: 'hover',
|
|
475
|
+
description: 'How the profile dropdown opens.'
|
|
476
|
+
},
|
|
477
|
+
placement: {
|
|
478
|
+
type: 'string',
|
|
479
|
+
enum: [
|
|
480
|
+
'bottomLeft',
|
|
481
|
+
'bottom',
|
|
482
|
+
'bottomRight',
|
|
483
|
+
'topLeft',
|
|
484
|
+
'top',
|
|
485
|
+
'topRight'
|
|
486
|
+
],
|
|
487
|
+
default: 'bottomRight',
|
|
488
|
+
description: 'Dropdown placement relative to the avatar.'
|
|
489
|
+
},
|
|
490
|
+
arrow: {
|
|
491
|
+
anyOf: [
|
|
492
|
+
{
|
|
493
|
+
type: 'boolean'
|
|
494
|
+
},
|
|
495
|
+
{
|
|
496
|
+
type: 'object',
|
|
497
|
+
properties: {
|
|
498
|
+
pointAtCenter: {
|
|
499
|
+
type: 'boolean'
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
],
|
|
504
|
+
default: false,
|
|
505
|
+
description: 'Show arrow on the dropdown.',
|
|
506
|
+
docs: {
|
|
507
|
+
displayType: 'switch'
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
},
|
|
512
|
+
darkModeToggle: {
|
|
513
|
+
type: 'boolean',
|
|
514
|
+
default: false,
|
|
515
|
+
description: 'Show a dark mode toggle button in the sider and mobile header. Toggles the Ant Design dark theme for the entire page. Preference is persisted to localStorage.'
|
|
516
|
+
},
|
|
517
|
+
iconsColor: {
|
|
518
|
+
type: 'string',
|
|
519
|
+
description: 'Color for notification and dark mode toggle icons.',
|
|
520
|
+
docs: {
|
|
521
|
+
displayType: 'color'
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
};
|
|
@@ -26,21 +26,51 @@ import Menu from '../Menu/Menu.js';
|
|
|
26
26
|
import MobileMenu from '../MobileMenu/MobileMenu.js';
|
|
27
27
|
import Sider from '../Sider/Sider.js';
|
|
28
28
|
import { getDarkMode, renderHeaderActions, registerDarkModeMethod } from '../headerActions.js';
|
|
29
|
+
function getInitialSiderState({ properties }) {
|
|
30
|
+
const storageKey = `lf-${properties.siderStorageKey ?? 'sider'}-open`;
|
|
31
|
+
try {
|
|
32
|
+
const stored = localStorage.getItem(storageKey);
|
|
33
|
+
if (stored === 'true') return true;
|
|
34
|
+
if (stored === 'false') return false;
|
|
35
|
+
} catch {
|
|
36
|
+
// localStorage unavailable (SSR, privacy mode)
|
|
37
|
+
}
|
|
38
|
+
return !properties.sider?.initialCollapsed;
|
|
39
|
+
}
|
|
40
|
+
function writeSiderState({ properties, open }) {
|
|
41
|
+
const storageKey = `lf-${properties.siderStorageKey ?? 'sider'}-open`;
|
|
42
|
+
try {
|
|
43
|
+
localStorage.setItem(storageKey, String(open));
|
|
44
|
+
} catch {
|
|
45
|
+
// localStorage unavailable
|
|
46
|
+
}
|
|
47
|
+
}
|
|
29
48
|
const PageSiderMenu = ({ basePath, blockId, classNames = {}, components: { Icon, Link, ShortcutBadge }, events, content, menus, methods, pageId, properties, styles = {} })=>{
|
|
30
|
-
const [openSiderState, setSiderOpen] = useState(
|
|
49
|
+
const [openSiderState, setSiderOpen] = useState(()=>getInitialSiderState({
|
|
50
|
+
properties
|
|
51
|
+
}));
|
|
31
52
|
useEffect(()=>{
|
|
32
53
|
registerDarkModeMethod(methods);
|
|
33
54
|
methods.registerMethod('toggleSiderOpen', ()=>{
|
|
34
|
-
|
|
35
|
-
|
|
55
|
+
const next = !openSiderState;
|
|
56
|
+
methods._setSiderOpen({
|
|
57
|
+
open: next
|
|
58
|
+
});
|
|
59
|
+
setSiderOpen(next);
|
|
60
|
+
writeSiderState({
|
|
61
|
+
properties,
|
|
62
|
+
open: next
|
|
36
63
|
});
|
|
37
|
-
setSiderOpen(!openSiderState);
|
|
38
64
|
});
|
|
39
65
|
methods.registerMethod('setSiderOpen', ({ open })=>{
|
|
40
|
-
methods.
|
|
66
|
+
methods._setSiderOpen({
|
|
41
67
|
open
|
|
42
68
|
});
|
|
43
69
|
setSiderOpen(open);
|
|
70
|
+
writeSiderState({
|
|
71
|
+
properties,
|
|
72
|
+
open
|
|
73
|
+
});
|
|
44
74
|
});
|
|
45
75
|
});
|
|
46
76
|
return /*#__PURE__*/ React.createElement(Layout, {
|
|
@@ -180,7 +210,10 @@ const PageSiderMenu = ({ basePath, blockId, classNames = {}, components: { Icon,
|
|
|
180
210
|
},
|
|
181
211
|
events: events,
|
|
182
212
|
methods: methods,
|
|
183
|
-
properties:
|
|
213
|
+
properties: {
|
|
214
|
+
...properties.sider ?? {},
|
|
215
|
+
initialCollapsed: !openSiderState
|
|
216
|
+
},
|
|
184
217
|
classNames: {
|
|
185
218
|
element: classNames.sider ?? 'hidden lg:block'
|
|
186
219
|
},
|
|
@@ -136,6 +136,11 @@ export default {
|
|
|
136
136
|
type: 'integer',
|
|
137
137
|
description: 'Width of the collapsed sidebar, by setting to 0 a special trigger will appear.'
|
|
138
138
|
},
|
|
139
|
+
initialCollapsed: {
|
|
140
|
+
type: 'boolean',
|
|
141
|
+
default: false,
|
|
142
|
+
description: 'Set the initial collapsed state. Used as the fallback when no persisted preference exists in localStorage.'
|
|
143
|
+
},
|
|
139
144
|
reverseArrow: {
|
|
140
145
|
type: 'boolean',
|
|
141
146
|
default: false,
|
|
@@ -158,6 +163,11 @@ export default {
|
|
|
158
163
|
}
|
|
159
164
|
}
|
|
160
165
|
},
|
|
166
|
+
siderStorageKey: {
|
|
167
|
+
type: 'string',
|
|
168
|
+
default: 'sider',
|
|
169
|
+
description: "localStorage key suffix for sider state persistence. Produces key 'lf-{siderStorageKey}-open'."
|
|
170
|
+
},
|
|
161
171
|
toggleSiderButton: {
|
|
162
172
|
type: 'object',
|
|
163
173
|
description: 'Toggle sider button properties.',
|
|
@@ -52,10 +52,16 @@ const Selector = ({ blockId, classNames = {}, components: { Icon, Link }, events
|
|
|
52
52
|
id: `${blockId}_input`,
|
|
53
53
|
variant: properties.bordered === false ? 'borderless' : properties.variant,
|
|
54
54
|
className: classNames.element,
|
|
55
|
+
classNames: {
|
|
56
|
+
content: classNames.selector
|
|
57
|
+
},
|
|
55
58
|
style: {
|
|
56
59
|
width: '100%',
|
|
57
60
|
...styles.element
|
|
58
61
|
},
|
|
62
|
+
styles: {
|
|
63
|
+
content: styles.selector
|
|
64
|
+
},
|
|
59
65
|
mode: "single",
|
|
60
66
|
autoFocus: properties.autoFocus,
|
|
61
67
|
getPopupContainer: ()=>document.getElementById(`${blockId}_${elementId}_popup`),
|
|
@@ -26,6 +26,7 @@ export default {
|
|
|
26
26
|
valueType: 'any',
|
|
27
27
|
cssKeys: {
|
|
28
28
|
element: 'The Selector element.',
|
|
29
|
+
selector: 'The inner value/tag container of the Selector (antd `content` semantic slot).',
|
|
29
30
|
clearIcon: 'The clear icon in the Selector.',
|
|
30
31
|
label: 'The Selector label.',
|
|
31
32
|
extra: 'The Selector extra content.',
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
import { get } from '@lowdefy/helpers';
|
|
17
17
|
import { Layout } from 'antd';
|
|
18
18
|
import { withBlockDefaults } from '@lowdefy/block-utils';
|
|
19
|
+
import { getDarkMode } from '../headerActions.js';
|
|
19
20
|
const Sider = Layout.Sider;
|
|
20
21
|
const triggerSetOpen = async ({ state, setOpen, methods, rename })=>{
|
|
21
22
|
if (!state) {
|
|
@@ -36,6 +37,14 @@ const triggerSetOpen = async ({ state, setOpen, methods, rename })=>{
|
|
|
36
37
|
};
|
|
37
38
|
const SiderBlock = ({ blockId, classNames = {}, properties, content, methods, rename, styles = {} })=>{
|
|
38
39
|
const [openState, setOpen] = useState(!properties.initialCollapsed);
|
|
40
|
+
// Sync internal state when the parent (e.g. PageSidebarLayout) changes
|
|
41
|
+
// `initialCollapsed` after mount — typically because a hydration-time read
|
|
42
|
+
// from localStorage restored a value different from the SSR default.
|
|
43
|
+
useEffect(()=>{
|
|
44
|
+
setOpen(!properties.initialCollapsed);
|
|
45
|
+
}, [
|
|
46
|
+
properties.initialCollapsed
|
|
47
|
+
]);
|
|
39
48
|
useEffect(()=>{
|
|
40
49
|
methods.registerMethod(get(rename, 'methods.toggleOpen', {
|
|
41
50
|
default: 'toggleOpen'
|
|
@@ -62,6 +71,7 @@ const SiderBlock = ({ blockId, classNames = {}, properties, content, methods, re
|
|
|
62
71
|
collapsedWidth: properties.collapsedWidth,
|
|
63
72
|
collapsible: properties.collapsible,
|
|
64
73
|
reverseArrow: properties.reverseArrow,
|
|
74
|
+
theme: properties.theme ?? (getDarkMode() ? 'dark' : 'light'),
|
|
65
75
|
style: {
|
|
66
76
|
overflow: 'auto',
|
|
67
77
|
background: 'var(--ant-color-bg-container)',
|