@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
|
@@ -40,10 +40,16 @@ const AutoCompleteInput = ({ blockId, classNames = {}, components, events, loadi
|
|
|
40
40
|
backfill: properties.backfill,
|
|
41
41
|
variant: properties.bordered === false ? 'borderless' : properties.variant,
|
|
42
42
|
className: classNames.element,
|
|
43
|
+
classNames: {
|
|
44
|
+
content: classNames.selector
|
|
45
|
+
},
|
|
43
46
|
style: {
|
|
44
47
|
width: '100%',
|
|
45
48
|
...styles.element
|
|
46
49
|
},
|
|
50
|
+
styles: {
|
|
51
|
+
content: styles.selector
|
|
52
|
+
},
|
|
47
53
|
defaultOpen: properties.defaultOpen,
|
|
48
54
|
disabled: properties.disabled || loading,
|
|
49
55
|
placeholder: properties.placeholder ?? 'Type or select item',
|
|
@@ -23,6 +23,7 @@ export default {
|
|
|
23
23
|
valueType: 'string',
|
|
24
24
|
cssKeys: {
|
|
25
25
|
element: 'The AutoComplete element.',
|
|
26
|
+
selector: 'The inner value container of the AutoComplete (antd `content` semantic slot).',
|
|
26
27
|
label: 'The AutoComplete label.',
|
|
27
28
|
extra: 'The AutoComplete extra content.',
|
|
28
29
|
feedback: 'The AutoComplete validation feedback.',
|
|
@@ -22,21 +22,58 @@ import withTheme from '../withTheme.js';
|
|
|
22
22
|
import disabledDate from '../../disabledDate.js';
|
|
23
23
|
dayjs.extend(utc);
|
|
24
24
|
function buildDateMap(dateCellData) {
|
|
25
|
-
if (!type.isArray(dateCellData)) return {
|
|
25
|
+
if (!type.isArray(dateCellData)) return {
|
|
26
|
+
map: {},
|
|
27
|
+
earliest: undefined
|
|
28
|
+
};
|
|
26
29
|
const map = {};
|
|
30
|
+
let earliest;
|
|
27
31
|
dateCellData.forEach((item)=>{
|
|
28
32
|
if (type.isNone(item?.date)) return;
|
|
29
|
-
const
|
|
33
|
+
const parsed = dayjs(item.date);
|
|
34
|
+
if (!parsed.isValid()) return;
|
|
35
|
+
const key = parsed.format('YYYY-MM-DD');
|
|
30
36
|
if (!map[key]) map[key] = [];
|
|
31
37
|
map[key].push(item);
|
|
38
|
+
if (!earliest || parsed.isBefore(earliest)) earliest = parsed;
|
|
32
39
|
});
|
|
33
|
-
return
|
|
40
|
+
return {
|
|
41
|
+
map,
|
|
42
|
+
earliest
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
// Pick a default month to open on when `value` is unset: earliest event date
|
|
46
|
+
// from dateCellData, or the earliest specific disabled date, or the min of a
|
|
47
|
+
// disabledDates range. Without this, a calendar whose data lives in a past or
|
|
48
|
+
// future month opens on "today" and shows nothing, confusing the user.
|
|
49
|
+
function pickDefaultValue({ earliestCellData, disabledDates }) {
|
|
50
|
+
if (earliestCellData) return earliestCellData;
|
|
51
|
+
if (type.isObject(disabledDates)) {
|
|
52
|
+
if (type.isArray(disabledDates.dates) && disabledDates.dates.length > 0) {
|
|
53
|
+
let earliest;
|
|
54
|
+
disabledDates.dates.forEach((d)=>{
|
|
55
|
+
const parsed = dayjs(d);
|
|
56
|
+
if (parsed.isValid() && (!earliest || parsed.isBefore(earliest))) earliest = parsed;
|
|
57
|
+
});
|
|
58
|
+
if (earliest) return earliest;
|
|
59
|
+
}
|
|
60
|
+
if (!type.isNone(disabledDates.min)) {
|
|
61
|
+
const parsed = dayjs(disabledDates.min);
|
|
62
|
+
if (parsed.isValid()) return parsed;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return undefined;
|
|
34
66
|
}
|
|
35
67
|
const CalendarBlock = ({ blockId, classNames = {}, events, methods, properties, styles = {}, value })=>{
|
|
36
|
-
const dateMap = buildDateMap(properties.dateCellData);
|
|
68
|
+
const { map: dateMap, earliest: earliestCellData } = buildDateMap(properties.dateCellData);
|
|
37
69
|
const hasDateData = Object.keys(dateMap).length > 0;
|
|
38
|
-
|
|
39
|
-
|
|
70
|
+
const defaultPanel = pickDefaultValue({
|
|
71
|
+
earliestCellData,
|
|
72
|
+
disabledDates: properties.disabledDates
|
|
73
|
+
});
|
|
74
|
+
return /*#__PURE__*/ React.createElement("div", {
|
|
75
|
+
id: blockId
|
|
76
|
+
}, /*#__PURE__*/ React.createElement(Calendar, {
|
|
40
77
|
className: classNames.element,
|
|
41
78
|
style: styles.element,
|
|
42
79
|
fullscreen: properties.fullscreen !== false,
|
|
@@ -47,21 +84,24 @@ const CalendarBlock = ({ blockId, classNames = {}, events, methods, properties,
|
|
|
47
84
|
dayjs(properties.validRange[1])
|
|
48
85
|
] : undefined,
|
|
49
86
|
value: type.isDate(value) ? dayjs(value) : undefined,
|
|
87
|
+
defaultValue: !type.isDate(value) ? defaultPanel : undefined,
|
|
50
88
|
onSelect: (date, selectInfo)=>{
|
|
51
89
|
// Wrap with our dayjs — antd v6's internal dayjs may lack the utc plugin.
|
|
52
90
|
const d = dayjs(date);
|
|
53
91
|
const val = d.toDate();
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
92
|
+
const source = selectInfo?.source ?? 'date';
|
|
93
|
+
// Only fire for date cell clicks — year/month panel navigation emits
|
|
94
|
+
// onSelect too but shouldn't push a value through the event chain.
|
|
95
|
+
if (source !== 'date' && source !== 'customize') return;
|
|
96
|
+
methods.setValue(val);
|
|
97
|
+
methods.triggerEvent({
|
|
98
|
+
name: 'onSelect',
|
|
99
|
+
event: {
|
|
100
|
+
value: val,
|
|
101
|
+
date: d.format('YYYY-MM-DD'),
|
|
102
|
+
source
|
|
103
|
+
}
|
|
104
|
+
});
|
|
65
105
|
},
|
|
66
106
|
onChange: (date)=>{
|
|
67
107
|
const d = dayjs(date);
|
|
@@ -90,6 +130,8 @@ const CalendarBlock = ({ blockId, classNames = {}, events, methods, properties,
|
|
|
90
130
|
if (info.type !== 'date') return info.originNode;
|
|
91
131
|
const key = dayjs(current).format('YYYY-MM-DD');
|
|
92
132
|
const items = dateMap[key];
|
|
133
|
+
// cellRender output is appended to the default date cell — return
|
|
134
|
+
// null for days with no events to avoid double-rendering the date.
|
|
93
135
|
if (!items) return null;
|
|
94
136
|
return /*#__PURE__*/ React.createElement("ul", {
|
|
95
137
|
className: "ant-picker-calendar-events",
|
|
@@ -106,6 +148,6 @@ const CalendarBlock = ({ blockId, classNames = {}, events, methods, properties,
|
|
|
106
148
|
text: item.content
|
|
107
149
|
}))));
|
|
108
150
|
} : undefined
|
|
109
|
-
});
|
|
151
|
+
}));
|
|
110
152
|
};
|
|
111
153
|
export default withTheme('Calendar', withBlockDefaults(CalendarBlock));
|
|
@@ -15,10 +15,11 @@
|
|
|
15
15
|
*/ import React, { useEffect } from 'react';
|
|
16
16
|
import { get } from '@lowdefy/helpers';
|
|
17
17
|
import { List, Typography } from 'antd';
|
|
18
|
-
import { withBlockDefaults } from '@lowdefy/block-utils';
|
|
18
|
+
import { cn, withBlockDefaults } from '@lowdefy/block-utils';
|
|
19
19
|
import Button from '../Button/Button.js';
|
|
20
20
|
import withTheme from '../withTheme.js';
|
|
21
|
-
|
|
21
|
+
import './style.module.css';
|
|
22
|
+
const ControlledListBlock = ({ blockId, classNames = {}, components: { Icon, Link, ShortcutBadge }, events, list, methods, properties, styles = {}, value = [] })=>{
|
|
22
23
|
useEffect(()=>{
|
|
23
24
|
methods.registerMethod('moveItemDown', methods.moveItemDown);
|
|
24
25
|
methods.registerMethod('moveItemUp', methods.moveItemUp);
|
|
@@ -31,6 +32,38 @@ const ControlledListBlock = ({ blockId, classNames = {}, components: { Icon, Lin
|
|
|
31
32
|
methods.pushItem({});
|
|
32
33
|
}
|
|
33
34
|
}
|
|
35
|
+
const addItemToFront = ()=>{
|
|
36
|
+
methods.unshiftItem();
|
|
37
|
+
methods.triggerEvent({
|
|
38
|
+
name: 'onAdd',
|
|
39
|
+
event: {
|
|
40
|
+
index: 0,
|
|
41
|
+
item: undefined
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
};
|
|
45
|
+
const addItemToBack = ()=>{
|
|
46
|
+
const index = value.length;
|
|
47
|
+
methods.pushItem();
|
|
48
|
+
methods.triggerEvent({
|
|
49
|
+
name: 'onAdd',
|
|
50
|
+
event: {
|
|
51
|
+
index,
|
|
52
|
+
item: undefined
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
};
|
|
56
|
+
const removeItemAt = (index)=>{
|
|
57
|
+
const item = value[index];
|
|
58
|
+
methods.removeItem(index);
|
|
59
|
+
methods.triggerEvent({
|
|
60
|
+
name: 'onRemove',
|
|
61
|
+
event: {
|
|
62
|
+
index,
|
|
63
|
+
item
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
};
|
|
34
67
|
return /*#__PURE__*/ React.createElement(List, {
|
|
35
68
|
id: blockId,
|
|
36
69
|
className: classNames.element,
|
|
@@ -61,7 +94,7 @@ const ControlledListBlock = ({ blockId, classNames = {}, components: { Icon, Lin
|
|
|
61
94
|
type: 'default',
|
|
62
95
|
...properties.addItemButton
|
|
63
96
|
},
|
|
64
|
-
onClick:
|
|
97
|
+
onClick: addItemToFront
|
|
65
98
|
})),
|
|
66
99
|
footer: !properties.addToFront && !properties.hideAddButton && /*#__PURE__*/ React.createElement("div", {
|
|
67
100
|
style: {
|
|
@@ -86,7 +119,7 @@ const ControlledListBlock = ({ blockId, classNames = {}, components: { Icon, Lin
|
|
|
86
119
|
type: 'dashed',
|
|
87
120
|
...properties.addItemButton
|
|
88
121
|
},
|
|
89
|
-
onClick:
|
|
122
|
+
onClick: addItemToBack
|
|
90
123
|
})),
|
|
91
124
|
bordered: true,
|
|
92
125
|
locale: {
|
|
@@ -102,24 +135,16 @@ const ControlledListBlock = ({ blockId, classNames = {}, components: { Icon, Lin
|
|
|
102
135
|
extra: !properties.hideRemoveButton && list.length > (properties.minItems ?? 0) && [
|
|
103
136
|
// eslint-disable-next-line react/jsx-key
|
|
104
137
|
/*#__PURE__*/ React.createElement("span", {
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
fontSize: properties.size === 'small' ? 16 : properties.size === 'large' ? 20 : 18
|
|
108
|
-
}
|
|
138
|
+
className: cn('lf-controlled-list-remove', classNames.removeIcon),
|
|
139
|
+
style: styles.removeIcon
|
|
109
140
|
}, /*#__PURE__*/ React.createElement(Icon, {
|
|
110
141
|
blockId: `${blockId}_${i}_remove_icon`,
|
|
111
|
-
classNames: {
|
|
112
|
-
element: classNames.removeIcon
|
|
113
|
-
},
|
|
114
142
|
events: events,
|
|
115
143
|
properties: {
|
|
116
144
|
name: 'AiOutlineMinusCircle',
|
|
117
145
|
...properties.removeItemIcon
|
|
118
146
|
},
|
|
119
|
-
|
|
120
|
-
element: styles.removeIcon
|
|
121
|
-
},
|
|
122
|
-
onClick: ()=>methods.removeItem(i)
|
|
147
|
+
onClick: ()=>removeItemAt(i)
|
|
123
148
|
}))
|
|
124
149
|
]
|
|
125
150
|
}, item.content && item.content({
|
|
@@ -22,12 +22,16 @@
|
|
|
22
22
|
slots: {
|
|
23
23
|
content: 'Blocks rendered for each list item.'
|
|
24
24
|
},
|
|
25
|
+
events: {
|
|
26
|
+
onAdd: 'Triggered after a new item is added via the add button. The event payload is `{ index, item }`, where `item` is the newly added value (`undefined` for an empty row).',
|
|
27
|
+
onRemove: 'Triggered after an item is removed via the remove icon. The event payload is `{ index, item }`, where `item` is the removed value captured before removal.'
|
|
28
|
+
},
|
|
25
29
|
cssKeys: {
|
|
26
30
|
element: 'The ControlledList element.',
|
|
27
31
|
header: 'The ControlledList header.',
|
|
28
32
|
footer: 'The ControlledList footer.',
|
|
29
33
|
item: 'The ControlledList item.',
|
|
30
|
-
removeIcon: 'The remove
|
|
34
|
+
removeIcon: 'The remove-item icon wrapper. Defaults to the antd error color at `fontSizeLG`; override `color`, `font-size`, or spacing here.'
|
|
31
35
|
},
|
|
32
36
|
properties: {
|
|
33
37
|
type: 'object',
|
|
@@ -69,7 +73,7 @@
|
|
|
69
73
|
'string',
|
|
70
74
|
'object'
|
|
71
75
|
],
|
|
72
|
-
description: 'Custom remove item icon properties.',
|
|
76
|
+
description: 'Custom remove item icon properties. Defaults to `AiOutlineMinusCircle` at a standard size with the antd error color inherited from the icon wrapper — override via `class.removeIcon` / `style.removeIcon` for visual tweaks, or via this property to change the icon name itself.',
|
|
73
77
|
docs: {
|
|
74
78
|
displayType: 'icon'
|
|
75
79
|
}
|
|
@@ -0,0 +1,35 @@
|
|
|
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
|
+
*/
|
|
16
|
+
|
|
17
|
+
@layer components {
|
|
18
|
+
:global(.lf-controlled-list-remove) {
|
|
19
|
+
display: inline-flex;
|
|
20
|
+
align-items: center;
|
|
21
|
+
padding-left: var(--ant-padding-xs);
|
|
22
|
+
font-size: var(--ant-font-size-lg);
|
|
23
|
+
color: var(--ant-color-error);
|
|
24
|
+
cursor: pointer;
|
|
25
|
+
transition: color 0.2s;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
:global(.lf-controlled-list-remove:hover) {
|
|
29
|
+
color: var(--ant-color-error-hover);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
:global(.lf-controlled-list-remove:active) {
|
|
33
|
+
color: var(--ant-color-error-active);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -112,6 +112,7 @@ const DrawerBlock = ({ blockId, classNames = {}, content, properties, methods, r
|
|
|
112
112
|
id: blockId,
|
|
113
113
|
closable: properties.closable,
|
|
114
114
|
extra: content.extra && content.extra(),
|
|
115
|
+
footer: content.footer && content.footer(),
|
|
115
116
|
getContainer: properties.getContainer,
|
|
116
117
|
mask: properties.mask,
|
|
117
118
|
maskClosable: properties.maskClosable,
|
|
@@ -145,6 +146,7 @@ const DrawerBlock = ({ blockId, classNames = {}, content, properties, methods, r
|
|
|
145
146
|
styles: {
|
|
146
147
|
header: styles.header,
|
|
147
148
|
body: styles.body,
|
|
149
|
+
footer: styles.footer,
|
|
148
150
|
mask: styles.mask,
|
|
149
151
|
wrapper: styles.wrapper,
|
|
150
152
|
content: styles.content
|
|
@@ -62,10 +62,12 @@ function DropdownButtonBlock({ blockId, classNames = {}, components: { Icon, Sho
|
|
|
62
62
|
key: `divider-${i}`
|
|
63
63
|
};
|
|
64
64
|
}
|
|
65
|
+
const eventShortcut = item.eventName ? events[item.eventName]?.shortcut : undefined;
|
|
66
|
+
const itemShortcut = eventShortcut ?? item.shortcut;
|
|
65
67
|
return {
|
|
66
68
|
key: item.eventName ?? `item-${i}`,
|
|
67
|
-
label: /*#__PURE__*/ React.createElement("span", null, item.title,
|
|
68
|
-
shortcut:
|
|
69
|
+
label: /*#__PURE__*/ React.createElement("span", null, item.title, itemShortcut && /*#__PURE__*/ React.createElement(ShortcutBadge, {
|
|
70
|
+
shortcut: itemShortcut
|
|
69
71
|
})),
|
|
70
72
|
icon: item.icon ? /*#__PURE__*/ React.createElement(Icon, {
|
|
71
73
|
blockId: `${blockId}_icon_${i}`,
|
|
@@ -82,7 +84,10 @@ function DropdownButtonBlock({ blockId, classNames = {}, components: { Icon, Sho
|
|
|
82
84
|
disabled: item.disabled
|
|
83
85
|
};
|
|
84
86
|
});
|
|
85
|
-
|
|
87
|
+
// Item-level shortcut fallback for items that declare `shortcut` on properties
|
|
88
|
+
// rather than via `events.<eventName>.shortcut`. Skip items whose event already
|
|
89
|
+
// owns the shortcut — the framework-level shortcut manager handles those.
|
|
90
|
+
const propertyShortcutItems = (properties.items ?? []).filter((item)=>item.shortcut && item.eventName && !item.disabled && !events[item.eventName]?.shortcut).map((item)=>({
|
|
86
91
|
key: item.eventName,
|
|
87
92
|
shortcut: item.shortcut
|
|
88
93
|
}));
|
|
@@ -94,12 +99,13 @@ function DropdownButtonBlock({ blockId, classNames = {}, components: { Icon, Sho
|
|
|
94
99
|
methods
|
|
95
100
|
]);
|
|
96
101
|
useItemShortcuts({
|
|
97
|
-
items:
|
|
102
|
+
items: propertyShortcutItems,
|
|
98
103
|
onMatch: onShortcutMatch
|
|
99
104
|
});
|
|
100
105
|
const onClickActionName = get(rename, 'events.onClick', {
|
|
101
106
|
default: 'onClick'
|
|
102
107
|
});
|
|
108
|
+
const onClickShortcut = events[onClickActionName]?.shortcut;
|
|
103
109
|
const dropdownProps = {
|
|
104
110
|
menu: {
|
|
105
111
|
items,
|
|
@@ -162,7 +168,9 @@ function DropdownButtonBlock({ blockId, classNames = {}, components: { Icon, Sho
|
|
|
162
168
|
onClick: ()=>methods.triggerEvent({
|
|
163
169
|
name: onClickActionName
|
|
164
170
|
})
|
|
165
|
-
}, properties.title
|
|
171
|
+
}, properties.title, onClickShortcut && /*#__PURE__*/ React.createElement(ShortcutBadge, {
|
|
172
|
+
shortcut: onClickShortcut
|
|
173
|
+
})), /*#__PURE__*/ React.createElement(Dropdown, dropdownProps, /*#__PURE__*/ React.createElement(Button, {
|
|
166
174
|
color: resolvedColor,
|
|
167
175
|
variant: variant,
|
|
168
176
|
type: buttonType,
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
arrow: 'Dropdown arrow indicator.'
|
|
27
27
|
},
|
|
28
28
|
events: {
|
|
29
|
-
onClick: 'Trigger action when the button is clicked (split mode).',
|
|
29
|
+
onClick: 'Trigger action when the button is clicked (split mode). Renders a shortcut badge when a shortcut is configured on the event.',
|
|
30
30
|
onOpenChange: 'Trigger action when dropdown opens or closes.'
|
|
31
31
|
},
|
|
32
32
|
properties: {
|
|
@@ -162,7 +162,7 @@
|
|
|
162
162
|
},
|
|
163
163
|
items: {
|
|
164
164
|
type: 'array',
|
|
165
|
-
description: 'Menu items. Each with an eventName that triggers a named event.',
|
|
165
|
+
description: 'Menu items. Each with an eventName that triggers a named event. Keyboard shortcuts can be configured via the standard `events.<eventName>.shortcut` schema (preferred) or via the item-level `shortcut` property — both render a badge next to the item label. The event-level shortcut takes precedence when both are set.',
|
|
166
166
|
items: {
|
|
167
167
|
type: 'object',
|
|
168
168
|
properties: {
|
|
@@ -201,7 +201,7 @@
|
|
|
201
201
|
},
|
|
202
202
|
shortcut: {
|
|
203
203
|
type: 'string',
|
|
204
|
-
description: 'Keyboard shortcut. Binds the key and renders the badge.'
|
|
204
|
+
description: 'Keyboard shortcut. Binds the key and renders the badge. Prefer configuring this via `events.<eventName>.shortcut` to follow the standard Lowdefy event schema — the event-level shortcut takes precedence when both are set.'
|
|
205
205
|
}
|
|
206
206
|
}
|
|
207
207
|
}
|
package/dist/blocks/Menu/Menu.js
CHANGED
|
@@ -12,9 +12,10 @@
|
|
|
12
12
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
13
|
See the License for the specific language governing permissions and
|
|
14
14
|
limitations under the License.
|
|
15
|
-
*/ import React, { useCallback } from 'react';
|
|
16
|
-
import { Menu } from 'antd';
|
|
15
|
+
*/ import React, { useCallback, useContext } from 'react';
|
|
16
|
+
import { Layout, Menu } from 'antd';
|
|
17
17
|
import { type, get } from '@lowdefy/helpers';
|
|
18
|
+
const SiderContext = Layout._InternalSiderContext;
|
|
18
19
|
import { withBlockDefaults } from '@lowdefy/block-utils';
|
|
19
20
|
import withTheme from '../withTheme.js';
|
|
20
21
|
import useItemShortcuts from '../useItemShortcuts.js';
|
|
@@ -133,6 +134,8 @@ function MenuComp({ blockId, classNames = {}, components: { Icon, Link, Shortcut
|
|
|
133
134
|
}
|
|
134
135
|
const menu = getDefaultMenu(menus, properties.menuId, properties.links);
|
|
135
136
|
const theme = properties.theme;
|
|
137
|
+
const { siderCollapsed } = useContext(SiderContext) ?? {};
|
|
138
|
+
const isCollapsed = properties.collapsed === true || siderCollapsed === true;
|
|
136
139
|
const items = buildMenuItems({
|
|
137
140
|
links: menu,
|
|
138
141
|
events,
|
|
@@ -185,7 +188,7 @@ function MenuComp({ blockId, classNames = {}, components: { Icon, Link, Shortcut
|
|
|
185
188
|
mode: properties.mode,
|
|
186
189
|
selectable: true,
|
|
187
190
|
theme: theme,
|
|
188
|
-
defaultOpenKeys: properties.defaultOpenKeys ?? (properties.mode === 'inline' &&
|
|
191
|
+
defaultOpenKeys: properties.defaultOpenKeys ?? (properties.mode === 'inline' && !isCollapsed && [
|
|
189
192
|
(menu.find((link)=>(link.links || []).map((subLink)=>subLink.links ? subLink.links.map((subSubLink)=>subSubLink.pageId) : [
|
|
190
193
|
subLink.pageId
|
|
191
194
|
]).flat().some((link)=>(properties.selectedKeys ?? [
|
|
@@ -18,7 +18,8 @@ import { withBlockDefaults } from '@lowdefy/block-utils';
|
|
|
18
18
|
import Button from '../Button/Button.js';
|
|
19
19
|
import Drawer from '../Drawer/Drawer.js';
|
|
20
20
|
import Menu from '../Menu/Menu.js';
|
|
21
|
-
|
|
21
|
+
import { getDarkMode } from '../headerActions.js';
|
|
22
|
+
const MobileMenu = ({ basePath, blockId, classNames = {}, components, content, events, methods, menus, pageId, properties, rename, styles = {} })=>{
|
|
22
23
|
const [openState, setOpen] = useState(false);
|
|
23
24
|
useEffect(()=>{
|
|
24
25
|
methods.registerMethod(get(rename, 'methods.toggleOpen', {
|
|
@@ -88,7 +89,18 @@ const MobileMenu = ({ basePath, blockId, classNames = {}, components, events, me
|
|
|
88
89
|
default: 'toggleOpen'
|
|
89
90
|
})](),
|
|
90
91
|
content: {
|
|
91
|
-
|
|
92
|
+
extra: properties.logo ? ()=>/*#__PURE__*/ React.createElement("div", {
|
|
93
|
+
style: {
|
|
94
|
+
flex: '1 0 auto'
|
|
95
|
+
}
|
|
96
|
+
}, /*#__PURE__*/ React.createElement(components.Link, {
|
|
97
|
+
home: true
|
|
98
|
+
}, /*#__PURE__*/ React.createElement("img", {
|
|
99
|
+
src: properties.logo?.srcMobile ?? properties.logo?.src ?? `${basePath}/logo-square-${getDarkMode() ? 'dark' : 'light'}-theme.png`,
|
|
100
|
+
alt: properties.logo?.alt ?? 'Lowdefy',
|
|
101
|
+
style: properties.logo?.style
|
|
102
|
+
}))) : undefined,
|
|
103
|
+
content: ()=>/*#__PURE__*/ React.createElement(React.Fragment, null, /*#__PURE__*/ React.createElement(Menu, {
|
|
92
104
|
basePath: basePath,
|
|
93
105
|
components: components,
|
|
94
106
|
blockId: `${blockId}_menu`,
|
|
@@ -113,7 +125,8 @@ const MobileMenu = ({ basePath, blockId, classNames = {}, components, events, me
|
|
|
113
125
|
onSelect: 'onMenuItemSelect'
|
|
114
126
|
}
|
|
115
127
|
}
|
|
116
|
-
})
|
|
128
|
+
}), content?.drawerContent && content.drawerContent()),
|
|
129
|
+
footer: content?.drawerFooter ? ()=>content.drawerFooter() : undefined
|
|
117
130
|
}
|
|
118
131
|
}));
|
|
119
132
|
};
|
|
@@ -13,12 +13,16 @@
|
|
|
13
13
|
See the License for the specific language governing permissions and
|
|
14
14
|
limitations under the License.
|
|
15
15
|
*/ export default {
|
|
16
|
-
category: '
|
|
16
|
+
category: 'container',
|
|
17
17
|
icons: [
|
|
18
18
|
'AiOutlineMenuUnfold',
|
|
19
19
|
'AiOutlineMenuFold'
|
|
20
20
|
],
|
|
21
21
|
valueType: null,
|
|
22
|
+
slots: {
|
|
23
|
+
drawerContent: 'Additional content below the menu in the drawer.',
|
|
24
|
+
drawerFooter: 'Footer content in the drawer.'
|
|
25
|
+
},
|
|
22
26
|
cssKeys: {
|
|
23
27
|
element: 'The MobileMenu element.'
|
|
24
28
|
},
|
|
@@ -48,6 +52,33 @@
|
|
|
48
52
|
displayType: 'yaml'
|
|
49
53
|
}
|
|
50
54
|
},
|
|
55
|
+
logo: {
|
|
56
|
+
type: 'object',
|
|
57
|
+
description: 'Logo settings for the mobile menu drawer header.',
|
|
58
|
+
additionalProperties: false,
|
|
59
|
+
properties: {
|
|
60
|
+
src: {
|
|
61
|
+
type: 'string',
|
|
62
|
+
description: 'Logo source url.'
|
|
63
|
+
},
|
|
64
|
+
srcMobile: {
|
|
65
|
+
type: 'string',
|
|
66
|
+
description: 'Mobile logo source url.'
|
|
67
|
+
},
|
|
68
|
+
alt: {
|
|
69
|
+
type: 'string',
|
|
70
|
+
default: 'Lowdefy',
|
|
71
|
+
description: 'Logo alternative text.'
|
|
72
|
+
},
|
|
73
|
+
style: {
|
|
74
|
+
type: 'object',
|
|
75
|
+
description: 'Css style object to apply to logo.',
|
|
76
|
+
docs: {
|
|
77
|
+
displayType: 'yaml'
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
},
|
|
51
82
|
menuId: {
|
|
52
83
|
type: 'string',
|
|
53
84
|
description: 'App menu id used to get menu links.'
|
|
@@ -41,7 +41,7 @@ const tagRender = (props, option, methods, components)=>{
|
|
|
41
41
|
}
|
|
42
42
|
});
|
|
43
43
|
};
|
|
44
|
-
const MultipleSelector = ({ blockId, classNames = {}, components: { Icon }, events, loading, methods, properties, required, styles = {}, validation, value })=>{
|
|
44
|
+
const MultipleSelector = ({ blockId, classNames = {}, components: { Icon, ShortcutBadge }, events, loading, methods, properties, required, styles = {}, validation, value })=>{
|
|
45
45
|
const [fetchState, setFetch] = useState(false);
|
|
46
46
|
const [elementId] = useState((0 | Math.random() * 9e2) + 1e2);
|
|
47
47
|
const uniqueValueOptions = getUniqueValues(properties.options ?? []);
|
|
@@ -73,15 +73,22 @@ const MultipleSelector = ({ blockId, classNames = {}, components: { Icon }, even
|
|
|
73
73
|
autoFocus: properties.autoFocus,
|
|
74
74
|
variant: properties.bordered === false ? 'borderless' : properties.variant,
|
|
75
75
|
className: classNames.element,
|
|
76
|
+
classNames: {
|
|
77
|
+
content: classNames.selector
|
|
78
|
+
},
|
|
76
79
|
style: {
|
|
77
80
|
width: '100%',
|
|
78
81
|
...styles.element
|
|
79
82
|
},
|
|
83
|
+
styles: {
|
|
84
|
+
content: styles.selector
|
|
85
|
+
},
|
|
80
86
|
disabled: properties.disabled || loading,
|
|
81
87
|
getPopupContainer: ()=>document.getElementById(`${blockId}_${elementId}_popup`),
|
|
82
88
|
mode: "multiple",
|
|
83
89
|
tagRender: properties.renderTags && ((props)=>tagRender(props, uniqueValueOptions[props.value], methods, {
|
|
84
|
-
Icon
|
|
90
|
+
Icon,
|
|
91
|
+
ShortcutBadge
|
|
85
92
|
})),
|
|
86
93
|
maxTagCount: properties.maxTagCount,
|
|
87
94
|
notFoundContent: fetchState ? properties.loadingPlaceholder || 'Loading' : properties.notFoundContent || 'Not found',
|
|
@@ -27,6 +27,7 @@ export default {
|
|
|
27
27
|
valueType: 'array',
|
|
28
28
|
cssKeys: {
|
|
29
29
|
element: 'The MultipleSelector element.',
|
|
30
|
+
selector: 'The inner tag/value container of the MultipleSelector (antd `content` semantic slot). Use for capping the tag area height and enabling internal scroll.',
|
|
30
31
|
clearIcon: 'The clear icon in the MultipleSelector.',
|
|
31
32
|
label: 'The MultipleSelector label.',
|
|
32
33
|
extra: 'The MultipleSelector extra content.',
|