@lowdefy/blocks-antd 5.2.0 → 5.4.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 +1 -0
- package/dist/blocks/AutoComplete/meta.js +2 -1
- package/dist/blocks/ButtonSelector/ButtonSelector.js +74 -27
- package/dist/blocks/ButtonSelector/meta.js +18 -4
- package/dist/blocks/Carousel/meta.js +16 -0
- package/dist/blocks/CheckboxSelector/CheckboxSelector.js +46 -14
- package/dist/blocks/CheckboxSelector/meta.js +7 -1
- package/dist/blocks/CheckboxSwitch/CheckboxSwitch.js +1 -0
- package/dist/blocks/CheckboxSwitch/meta.js +4 -1
- package/dist/blocks/ColorSelector/ColorSelector.js +1 -0
- package/dist/blocks/ColorSelector/meta.js +2 -1
- package/dist/blocks/ConfigProvider/ConfigProvider.js +1 -0
- package/dist/blocks/ConfigProvider/meta.js +7 -0
- package/dist/blocks/ConfirmModal/ConfirmModal.js +2 -2
- package/dist/blocks/ConfirmModal/meta.js +2 -4
- package/dist/blocks/DateRangeSelector/DateRangeSelector.js +4 -9
- package/dist/blocks/DateRangeSelector/meta.js +4 -8
- package/dist/blocks/DateSelector/DateSelector.js +4 -3
- package/dist/blocks/DateSelector/meta.js +4 -5
- package/dist/blocks/DateTimeSelector/DateTimeSelector.js +4 -3
- package/dist/blocks/DateTimeSelector/meta.js +4 -5
- package/dist/blocks/DropdownMenu/meta.js +46 -6
- package/dist/blocks/Label/Label.js +30 -5
- package/dist/blocks/Label/meta.js +8 -2
- package/dist/blocks/ListSelector/ListSelector.js +384 -0
- package/dist/blocks/ListSelector/e2e.js +40 -0
- package/dist/blocks/ListSelector/meta.js +215 -0
- package/dist/blocks/Menu/Menu.js +26 -80
- package/dist/blocks/Menu/meta.js +160 -64
- package/dist/blocks/MobileMenu/meta.js +50 -50
- package/dist/blocks/Modal/Modal.js +2 -2
- package/dist/blocks/Modal/meta.js +2 -4
- package/dist/blocks/MonthSelector/MonthSelector.js +4 -3
- package/dist/blocks/MonthSelector/meta.js +4 -5
- package/dist/blocks/MultipleSelector/MultipleSelector.js +41 -9
- package/dist/blocks/MultipleSelector/meta.js +24 -5
- package/dist/blocks/NumberInput/NumberInput.js +3 -1
- package/dist/blocks/NumberInput/meta.js +3 -3
- package/dist/blocks/PageHeaderMenu/PageHeaderMenu.js +12 -3
- package/dist/blocks/PageHeaderMenu/meta.js +8 -1
- package/dist/blocks/PageSidebarLayout/PageSidebarLayout.js +2 -1
- package/dist/blocks/PageSidebarLayout/meta.js +8 -1
- package/dist/blocks/PageSiderMenu/PageSiderMenu.js +2 -1
- package/dist/blocks/PageSiderMenu/meta.js +8 -1
- package/dist/blocks/PasswordInput/PasswordInput.js +1 -0
- package/dist/blocks/PasswordInput/meta.js +2 -1
- package/dist/blocks/PhoneNumberInput/PhoneNumberInput.js +1 -0
- package/dist/blocks/PhoneNumberInput/meta.js +2 -1
- package/dist/blocks/RadioSelector/RadioSelector.js +44 -14
- package/dist/blocks/RadioSelector/meta.js +7 -1
- package/dist/blocks/RatingSlider/meta.js +2 -1
- package/dist/blocks/SegmentedSelector/SegmentedSelector.js +10 -4
- package/dist/blocks/SegmentedSelector/meta.js +7 -4
- package/dist/blocks/Selector/Selector.js +55 -9
- package/dist/blocks/Selector/meta.js +24 -5
- package/dist/blocks/Slider/Slider.js +1 -0
- package/dist/blocks/Slider/meta.js +2 -1
- package/dist/blocks/Switch/Switch.js +1 -0
- package/dist/blocks/Switch/meta.js +2 -1
- package/dist/blocks/Tabs/Tabs.js +30 -43
- package/dist/blocks/Tabs/meta.js +8 -10
- package/dist/blocks/TextArea/TextArea.js +1 -0
- package/dist/blocks/TextArea/meta.js +2 -1
- package/dist/blocks/TextInput/TextInput.js +1 -0
- package/dist/blocks/TextInput/meta.js +2 -1
- package/dist/blocks/TreeInput/TreeInput.js +91 -0
- package/dist/blocks/TreeInput/e2e.js +33 -0
- package/dist/blocks/TreeInput/meta.js +68 -0
- package/dist/blocks/TreeMultipleSelector/TreeMultipleSelector.js +161 -0
- package/dist/blocks/TreeMultipleSelector/e2e.js +46 -0
- package/dist/blocks/TreeMultipleSelector/meta.js +128 -0
- package/dist/blocks/TreeSelector/TreeSelector.js +127 -88
- package/dist/blocks/TreeSelector/e2e.js +20 -9
- package/dist/blocks/TreeSelector/meta.js +70 -254
- package/dist/blocks/WeekSelector/WeekSelector.js +2 -1
- package/dist/blocks/WeekSelector/meta.js +3 -3
- package/dist/blocks/buildMenuItems.js +89 -26
- package/dist/blocks/headerActions.js +87 -3
- package/dist/blocks/normalizeItemClassAndStyle.js +77 -0
- package/dist/blocks.js +3 -0
- package/dist/e2e.js +3 -0
- package/dist/getContrastTextColor.js +45 -0
- package/dist/getOptionColorStyle.js +36 -0
- package/dist/getSelectedIndex.js +42 -0
- package/dist/getSelectorOptions.js +67 -0
- package/dist/getTreeData.js +94 -0
- package/dist/metas.js +3 -0
- package/dist/schemas/dataOptions.js +36 -0
- package/dist/schemas/index.js +1 -0
- package/dist/schemas/label.js +3 -1
- package/dist/schemas/labelTooltip.js +44 -0
- package/dist/schemas/options.js +7 -3
- package/dist/schemas/treeSelectTheme.js +125 -0
- package/dist/serializeSelectorValue.js +38 -0
- package/dist/useSelectorOptions.js +38 -0
- package/dist/useSetData.js +27 -0
- package/package.json +9 -7
|
@@ -13,34 +13,40 @@
|
|
|
13
13
|
See the License for the specific language governing permissions and
|
|
14
14
|
limitations under the License.
|
|
15
15
|
*/ import React from 'react';
|
|
16
|
+
import { cn } from '@lowdefy/block-utils';
|
|
17
|
+
import normalizeItemClassAndStyle from './normalizeItemClassAndStyle.js';
|
|
16
18
|
function getTitle({ id, properties, pageId, url }) {
|
|
17
19
|
return properties?.title ?? pageId ?? url ?? id;
|
|
18
20
|
}
|
|
19
|
-
function
|
|
21
|
+
function defaultGetKey(link) {
|
|
22
|
+
return link.id;
|
|
23
|
+
}
|
|
24
|
+
function buildMenuItems({ links, components: { Icon, Link, ShortcutBadge }, classNames, styles, events, isTopLevel = true, wrapGroupLabel, nestedGroupAsGroup = false, getKey = defaultGetKey }) {
|
|
20
25
|
return (links ?? []).map((link, i)=>{
|
|
26
|
+
const item = normalizeItemClassAndStyle(link);
|
|
21
27
|
if (link.type === 'MenuDivider') {
|
|
22
28
|
return {
|
|
23
29
|
type: 'divider',
|
|
24
30
|
key: link.id ?? i,
|
|
25
31
|
dashed: link.properties?.dashed,
|
|
26
|
-
|
|
32
|
+
className: cn(classNames?.item, item.class.element) || undefined,
|
|
33
|
+
style: item.style.element
|
|
27
34
|
};
|
|
28
35
|
}
|
|
29
36
|
if (link.type === 'MenuGroup') {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
}) : undefined,
|
|
37
|
+
const labelText = getTitle(link);
|
|
38
|
+
const groupLabel = wrapGroupLabel ? wrapGroupLabel({
|
|
39
|
+
link,
|
|
40
|
+
labelText,
|
|
41
|
+
classNames: item.class.label,
|
|
42
|
+
styles: item.style.label
|
|
43
|
+
}) : labelText;
|
|
44
|
+
const renderAsGroup = !isTopLevel && nestedGroupAsGroup;
|
|
45
|
+
const groupItem = {
|
|
46
|
+
key: getKey(link),
|
|
47
|
+
label: groupLabel,
|
|
48
|
+
className: cn(classNames?.item, item.class.element) || undefined,
|
|
49
|
+
style: item.style.element,
|
|
44
50
|
children: buildMenuItems({
|
|
45
51
|
links: link.links,
|
|
46
52
|
components: {
|
|
@@ -50,35 +56,92 @@ function buildMenuItems({ links, components: { Icon, Link, ShortcutBadge }, clas
|
|
|
50
56
|
},
|
|
51
57
|
classNames,
|
|
52
58
|
styles,
|
|
53
|
-
events
|
|
59
|
+
events,
|
|
60
|
+
isTopLevel: false,
|
|
61
|
+
wrapGroupLabel,
|
|
62
|
+
nestedGroupAsGroup,
|
|
63
|
+
getKey
|
|
54
64
|
})
|
|
55
65
|
};
|
|
66
|
+
if (renderAsGroup) {
|
|
67
|
+
// antd MenuItemGroupType supports neither icon, disabled, title, nor popupClassName.
|
|
68
|
+
groupItem.type = 'group';
|
|
69
|
+
return groupItem;
|
|
70
|
+
}
|
|
71
|
+
// SubMenu — supports icon, disabled, title (tooltip when collapsed), popupClassName.
|
|
72
|
+
groupItem.disabled = link.properties?.disabled;
|
|
73
|
+
groupItem.title = link.properties?.tooltip;
|
|
74
|
+
if (link.properties?.icon) {
|
|
75
|
+
groupItem.icon = /*#__PURE__*/ React.createElement(Icon, {
|
|
76
|
+
blockId: `${link.id}_icon`,
|
|
77
|
+
classNames: {
|
|
78
|
+
element: cn(classNames?.itemIcon, item.class.icon) || undefined
|
|
79
|
+
},
|
|
80
|
+
events: events,
|
|
81
|
+
properties: link.properties.icon,
|
|
82
|
+
styles: {
|
|
83
|
+
element: {
|
|
84
|
+
...styles?.itemIcon,
|
|
85
|
+
...item.style.icon
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
if (item.class.popup) groupItem.popupClassName = item.class.popup;
|
|
91
|
+
return groupItem;
|
|
56
92
|
}
|
|
57
93
|
// MenuLink (default)
|
|
94
|
+
// Strip class/style from the spread so slot-keyed values don't reach Link as raw props.
|
|
95
|
+
const { class: _omitClass, style: _omitStyle, ...linkRest } = link;
|
|
96
|
+
const extra = link.properties?.extra;
|
|
97
|
+
// antd v6's `extra` prop triggers a `display: inline-flex; width: 100%` layout
|
|
98
|
+
// on `.ant-menu-title-content-with-extra` that interacts badly with the Lowdefy
|
|
99
|
+
// `<Link>` wrapper (collapses the label). Render extra ourselves inside the Link
|
|
100
|
+
// using float, which works regardless of the parent's display mode.
|
|
58
101
|
return {
|
|
59
|
-
key: link
|
|
102
|
+
key: getKey(link),
|
|
60
103
|
danger: link.properties?.danger,
|
|
61
104
|
disabled: link.properties?.disabled,
|
|
105
|
+
title: link.properties?.tooltip,
|
|
106
|
+
className: cn(classNames?.item, item.class.element) || undefined,
|
|
107
|
+
style: item.style.element,
|
|
62
108
|
icon: link.properties?.icon ? /*#__PURE__*/ React.createElement(Icon, {
|
|
63
109
|
blockId: `${link.id}_icon`,
|
|
64
110
|
classNames: {
|
|
65
|
-
element: classNames.
|
|
111
|
+
element: cn(classNames?.itemIcon, item.class.icon) || undefined
|
|
66
112
|
},
|
|
67
113
|
events: events,
|
|
68
114
|
properties: link.properties.icon,
|
|
69
115
|
styles: {
|
|
70
|
-
element:
|
|
116
|
+
element: {
|
|
117
|
+
...styles?.itemIcon,
|
|
118
|
+
...item.style.icon
|
|
119
|
+
}
|
|
71
120
|
}
|
|
72
121
|
}) : undefined,
|
|
73
122
|
label: /*#__PURE__*/ React.createElement(Link, {
|
|
123
|
+
...linkRest,
|
|
74
124
|
id: link.pageId ?? link.id ?? i,
|
|
75
|
-
|
|
125
|
+
className: item.class.label || undefined,
|
|
126
|
+
style: item.style.label,
|
|
76
127
|
url: link.url ?? link.properties?.url,
|
|
77
|
-
newTab: link.newTab ?? link.properties?.newTab
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
128
|
+
newTab: link.newTab ?? link.properties?.newTab
|
|
129
|
+
}, link.properties?.shortcut ? /*#__PURE__*/ React.createElement("span", {
|
|
130
|
+
style: {
|
|
131
|
+
float: 'right',
|
|
132
|
+
marginInlineStart: 12
|
|
133
|
+
}
|
|
134
|
+
}, /*#__PURE__*/ React.createElement(ShortcutBadge, {
|
|
135
|
+
shortcut: link.properties.shortcut
|
|
136
|
+
})) : null, extra ? /*#__PURE__*/ React.createElement("span", {
|
|
137
|
+
style: {
|
|
138
|
+
float: 'right',
|
|
139
|
+
marginInlineStart: 12,
|
|
140
|
+
opacity: 0.65,
|
|
141
|
+
fontSize: '0.9em',
|
|
142
|
+
pointerEvents: 'none'
|
|
143
|
+
}
|
|
144
|
+
}, extra) : null, getTitle(link))
|
|
82
145
|
};
|
|
83
146
|
});
|
|
84
147
|
}
|
|
@@ -34,6 +34,17 @@ function getDarkModeLabel() {
|
|
|
34
34
|
if (pref === 'light') return 'Light mode';
|
|
35
35
|
return 'System';
|
|
36
36
|
}
|
|
37
|
+
function getSupportedLocales() {
|
|
38
|
+
return window.__lowdefy_supported_locales ?? [];
|
|
39
|
+
}
|
|
40
|
+
function getActiveLocaleCode() {
|
|
41
|
+
return window.__lowdefy_locale;
|
|
42
|
+
}
|
|
43
|
+
function getActiveLocaleLabel() {
|
|
44
|
+
const active = getActiveLocaleCode();
|
|
45
|
+
const match = getSupportedLocales().find((l)=>l.code === active);
|
|
46
|
+
return match?.label ?? active ?? '';
|
|
47
|
+
}
|
|
37
48
|
// Wraps a header action row for the expanded sider. Icon cell has a fixed
|
|
38
49
|
// basis so bell / avatar / sun share a vertical line regardless of their
|
|
39
50
|
// own intrinsic size; label fills the remaining width. Row gets a subtle
|
|
@@ -280,11 +291,68 @@ function renderDarkModeToggle({ blockId, classNames, styles, methods, events, Ic
|
|
|
280
291
|
})
|
|
281
292
|
}, icon);
|
|
282
293
|
}
|
|
294
|
+
function renderLocaleSelector({ blockId, classNames, styles, methods, events, Icon, iconsColor, expanded }) {
|
|
295
|
+
const supported = getSupportedLocales();
|
|
296
|
+
if (supported.length === 0) return null;
|
|
297
|
+
const active = getActiveLocaleCode();
|
|
298
|
+
const items = supported.map((locale)=>({
|
|
299
|
+
key: locale.code,
|
|
300
|
+
label: locale.label ?? locale.code
|
|
301
|
+
}));
|
|
302
|
+
const icon = /*#__PURE__*/ React.createElement(Icon, {
|
|
303
|
+
blockId: `${blockId}_locale_selector_icon`,
|
|
304
|
+
events: events,
|
|
305
|
+
properties: {
|
|
306
|
+
name: 'AiOutlineGlobal'
|
|
307
|
+
},
|
|
308
|
+
styles: {
|
|
309
|
+
element: {
|
|
310
|
+
fontSize: 16,
|
|
311
|
+
color: iconsColor
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
});
|
|
315
|
+
const trigger = expanded ? /*#__PURE__*/ React.createElement(ExpandedRow, {
|
|
316
|
+
className: classNames.localeSelector,
|
|
317
|
+
style: styles.localeSelector,
|
|
318
|
+
label: getActiveLocaleLabel()
|
|
319
|
+
}, icon) : /*#__PURE__*/ React.createElement("div", {
|
|
320
|
+
className: classNames.localeSelector,
|
|
321
|
+
style: {
|
|
322
|
+
cursor: 'pointer',
|
|
323
|
+
lineHeight: 1,
|
|
324
|
+
...styles.localeSelector
|
|
325
|
+
}
|
|
326
|
+
}, icon);
|
|
327
|
+
return /*#__PURE__*/ React.createElement(Dropdown, {
|
|
328
|
+
menu: {
|
|
329
|
+
items,
|
|
330
|
+
selectedKeys: active ? [
|
|
331
|
+
active
|
|
332
|
+
] : [],
|
|
333
|
+
onClick: ({ key })=>{
|
|
334
|
+
methods.triggerEvent({
|
|
335
|
+
name: '__setLocale',
|
|
336
|
+
event: {
|
|
337
|
+
locale: key
|
|
338
|
+
}
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
},
|
|
342
|
+
trigger: [
|
|
343
|
+
expanded ? 'click' : 'hover'
|
|
344
|
+
],
|
|
345
|
+
placement: expanded ? 'topRight' : 'bottomRight',
|
|
346
|
+
popupClassName: classNames.localeSelectorMenu,
|
|
347
|
+
popupStyle: styles.localeSelectorMenu
|
|
348
|
+
}, /*#__PURE__*/ React.createElement("div", null, trigger));
|
|
349
|
+
}
|
|
283
350
|
function renderHeaderActions({ blockId, classNames = {}, styles = {}, properties, methods, events, components: { Icon, Link, ShortcutBadge }, iconsColor, expanded = false }) {
|
|
284
351
|
const hasNotifications = !type.isNone(properties.notifications);
|
|
285
352
|
const hasProfile = !type.isNone(properties.profile);
|
|
286
353
|
const hasDarkMode = properties.darkModeToggle;
|
|
287
|
-
|
|
354
|
+
const hasLocaleSelector = properties.localeSelector && getSupportedLocales().length > 0;
|
|
355
|
+
if (!hasNotifications && !hasProfile && !hasDarkMode && !hasLocaleSelector) return null;
|
|
288
356
|
const ctx = {
|
|
289
357
|
blockId,
|
|
290
358
|
classNames,
|
|
@@ -302,7 +370,7 @@ function renderHeaderActions({ blockId, classNames = {}, styles = {}, properties
|
|
|
302
370
|
return /*#__PURE__*/ React.createElement("div", {
|
|
303
371
|
className: classNames.headerActions ?? defaultClassName,
|
|
304
372
|
style: styles.headerActions
|
|
305
|
-
}, hasNotifications && renderNotifications(ctx), hasProfile && renderProfile(ctx), hasDarkMode && renderDarkModeToggle(ctx));
|
|
373
|
+
}, hasNotifications && renderNotifications(ctx), hasProfile && renderProfile(ctx), hasDarkMode && renderDarkModeToggle(ctx), hasLocaleSelector && renderLocaleSelector(ctx));
|
|
306
374
|
}
|
|
307
375
|
function registerDarkModeMethod(methods) {
|
|
308
376
|
methods.registerEvent({
|
|
@@ -320,4 +388,20 @@ function registerDarkModeMethod(methods) {
|
|
|
320
388
|
});
|
|
321
389
|
});
|
|
322
390
|
}
|
|
323
|
-
|
|
391
|
+
function registerLocaleMethod(methods) {
|
|
392
|
+
methods.registerEvent({
|
|
393
|
+
name: '__setLocale',
|
|
394
|
+
actions: [
|
|
395
|
+
{
|
|
396
|
+
id: '__set_locale',
|
|
397
|
+
type: 'SetLocale',
|
|
398
|
+
params: {
|
|
399
|
+
locale: {
|
|
400
|
+
_event: 'locale'
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
]
|
|
405
|
+
});
|
|
406
|
+
}
|
|
407
|
+
export { getDarkMode, renderHeaderActions, registerDarkModeMethod, registerLocaleMethod };
|
|
@@ -0,0 +1,77 @@
|
|
|
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 { type } from '@lowdefy/helpers';
|
|
16
|
+
import { cn } from '@lowdefy/block-utils';
|
|
17
|
+
const ITEM_SLOT_KEYS = [
|
|
18
|
+
'element',
|
|
19
|
+
'icon',
|
|
20
|
+
'label',
|
|
21
|
+
'popup'
|
|
22
|
+
];
|
|
23
|
+
function isSlotKeyed(value) {
|
|
24
|
+
return Object.keys(value).some((k)=>k.startsWith('.'));
|
|
25
|
+
}
|
|
26
|
+
function partitionStyle(style) {
|
|
27
|
+
const slots = {
|
|
28
|
+
element: undefined,
|
|
29
|
+
icon: undefined,
|
|
30
|
+
label: undefined,
|
|
31
|
+
popup: undefined
|
|
32
|
+
};
|
|
33
|
+
if (type.isNone(style)) return slots;
|
|
34
|
+
if (!type.isObject(style)) return slots;
|
|
35
|
+
if (!isSlotKeyed(style)) {
|
|
36
|
+
slots.element = style;
|
|
37
|
+
return slots;
|
|
38
|
+
}
|
|
39
|
+
for (const [key, value] of Object.entries(style)){
|
|
40
|
+
if (!key.startsWith('.')) continue;
|
|
41
|
+
const slot = key.slice(1);
|
|
42
|
+
if (ITEM_SLOT_KEYS.includes(slot)) slots[slot] = value;
|
|
43
|
+
}
|
|
44
|
+
return slots;
|
|
45
|
+
}
|
|
46
|
+
function partitionClass(classValue) {
|
|
47
|
+
const slots = {
|
|
48
|
+
element: undefined,
|
|
49
|
+
icon: undefined,
|
|
50
|
+
label: undefined,
|
|
51
|
+
popup: undefined
|
|
52
|
+
};
|
|
53
|
+
if (type.isNone(classValue)) return slots;
|
|
54
|
+
if (type.isString(classValue) || type.isArray(classValue)) {
|
|
55
|
+
slots.element = cn(classValue);
|
|
56
|
+
return slots;
|
|
57
|
+
}
|
|
58
|
+
if (!type.isObject(classValue)) return slots;
|
|
59
|
+
if (!isSlotKeyed(classValue)) {
|
|
60
|
+
slots.element = cn(classValue);
|
|
61
|
+
return slots;
|
|
62
|
+
}
|
|
63
|
+
for (const [key, value] of Object.entries(classValue)){
|
|
64
|
+
if (!key.startsWith('.')) continue;
|
|
65
|
+
const slot = key.slice(1);
|
|
66
|
+
if (ITEM_SLOT_KEYS.includes(slot)) slots[slot] = cn(value);
|
|
67
|
+
}
|
|
68
|
+
return slots;
|
|
69
|
+
}
|
|
70
|
+
function normalizeItemClassAndStyle(link) {
|
|
71
|
+
return {
|
|
72
|
+
class: partitionClass(link?.class),
|
|
73
|
+
style: partitionStyle(link?.style)
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
export default normalizeItemClassAndStyle;
|
|
77
|
+
export { partitionClass, partitionStyle };
|
package/dist/blocks.js
CHANGED
|
@@ -45,6 +45,7 @@ export { default as Footer } from './blocks/Footer/Footer.js';
|
|
|
45
45
|
export { default as Header } from './blocks/Header/Header.js';
|
|
46
46
|
export { default as Label } from './blocks/Label/Label.js';
|
|
47
47
|
export { default as Layout } from './blocks/Layout/Layout.js';
|
|
48
|
+
export { default as ListSelector } from './blocks/ListSelector/ListSelector.js';
|
|
48
49
|
export { default as Masonry } from './blocks/Masonry/Masonry.js';
|
|
49
50
|
export { default as MasonryList } from './blocks/MasonryList/MasonryList.js';
|
|
50
51
|
export { default as Menu } from './blocks/Menu/Menu.js';
|
|
@@ -85,6 +86,8 @@ export { default as TextArea } from './blocks/TextArea/TextArea.js';
|
|
|
85
86
|
export { default as TextInput } from './blocks/TextInput/TextInput.js';
|
|
86
87
|
export { default as Title } from './blocks/Title/Title.js';
|
|
87
88
|
export { default as TitleInput } from './blocks/TitleInput/TitleInput.js';
|
|
89
|
+
export { default as TreeInput } from './blocks/TreeInput/TreeInput.js';
|
|
90
|
+
export { default as TreeMultipleSelector } from './blocks/TreeMultipleSelector/TreeMultipleSelector.js';
|
|
88
91
|
export { default as TreeSelector } from './blocks/TreeSelector/TreeSelector.js';
|
|
89
92
|
export { default as Tooltip } from './blocks/Tooltip/Tooltip.js';
|
|
90
93
|
export { default as Tour } from './blocks/Tour/Tour.js';
|
package/dist/e2e.js
CHANGED
|
@@ -41,6 +41,7 @@ export { default as Footer } from './blocks/Footer/e2e.js';
|
|
|
41
41
|
export { default as Header } from './blocks/Header/e2e.js';
|
|
42
42
|
export { default as Label } from './blocks/Label/e2e.js';
|
|
43
43
|
export { default as Layout } from './blocks/Layout/e2e.js';
|
|
44
|
+
export { default as ListSelector } from './blocks/ListSelector/e2e.js';
|
|
44
45
|
export { default as Menu } from './blocks/Menu/e2e.js';
|
|
45
46
|
export { default as Message } from './blocks/Message/e2e.js';
|
|
46
47
|
export { default as MobileMenu } from './blocks/MobileMenu/e2e.js';
|
|
@@ -75,5 +76,7 @@ export { default as TimelineList } from './blocks/TimelineList/e2e.js';
|
|
|
75
76
|
export { default as Title } from './blocks/Title/e2e.js';
|
|
76
77
|
export { default as TitleInput } from './blocks/TitleInput/e2e.js';
|
|
77
78
|
export { default as Tooltip } from './blocks/Tooltip/e2e.js';
|
|
79
|
+
export { default as TreeInput } from './blocks/TreeInput/e2e.js';
|
|
80
|
+
export { default as TreeMultipleSelector } from './blocks/TreeMultipleSelector/e2e.js';
|
|
78
81
|
export { default as TreeSelector } from './blocks/TreeSelector/e2e.js';
|
|
79
82
|
export { default as WeekSelector } from './blocks/WeekSelector/e2e.js';
|
|
@@ -0,0 +1,45 @@
|
|
|
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 { type } from '@lowdefy/helpers';
|
|
16
|
+
// Returns '#000' or '#fff' for best contrast against a hex background color.
|
|
17
|
+
// Returns undefined for non-hex values (named colors, rgb(), etc.) so the
|
|
18
|
+
// caller can fall back to antd's default solid text color.
|
|
19
|
+
function getContrastTextColor(color) {
|
|
20
|
+
if (!type.isString(color)) return undefined;
|
|
21
|
+
const hex = color.trim().replace(/^#/, '');
|
|
22
|
+
let r;
|
|
23
|
+
let g;
|
|
24
|
+
let b;
|
|
25
|
+
if (hex.length === 3) {
|
|
26
|
+
r = parseInt(hex[0] + hex[0], 16);
|
|
27
|
+
g = parseInt(hex[1] + hex[1], 16);
|
|
28
|
+
b = parseInt(hex[2] + hex[2], 16);
|
|
29
|
+
} else if (hex.length === 6 || hex.length === 8) {
|
|
30
|
+
r = parseInt(hex.slice(0, 2), 16);
|
|
31
|
+
g = parseInt(hex.slice(2, 4), 16);
|
|
32
|
+
b = parseInt(hex.slice(4, 6), 16);
|
|
33
|
+
} else {
|
|
34
|
+
return undefined;
|
|
35
|
+
}
|
|
36
|
+
if ([
|
|
37
|
+
r,
|
|
38
|
+
g,
|
|
39
|
+
b
|
|
40
|
+
].some((n)=>Number.isNaN(n))) return undefined;
|
|
41
|
+
// YIQ perceptual brightness: dark text on light backgrounds, light text on dark.
|
|
42
|
+
const yiq = (r * 299 + g * 587 + b * 114) / 1000;
|
|
43
|
+
return yiq >= 128 ? '#000' : '#fff';
|
|
44
|
+
}
|
|
45
|
+
export default getContrastTextColor;
|
|
@@ -0,0 +1,36 @@
|
|
|
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 getContrastTextColor from './getContrastTextColor.js';
|
|
16
|
+
// Color styles for a selected selector option, by variant.
|
|
17
|
+
// outline: color the border/text + a low-opacity tint of the color.
|
|
18
|
+
// solid: fill with the color + auto-contrast text.
|
|
19
|
+
// `color` may be a CSS value such as 'var(--ant-color-primary)' for the active
|
|
20
|
+
// primary when no explicit color is set.
|
|
21
|
+
function getOptionColorStyle({ color, isOutline }) {
|
|
22
|
+
if (isOutline) {
|
|
23
|
+
return {
|
|
24
|
+
color,
|
|
25
|
+
borderColor: color,
|
|
26
|
+
backgroundColor: `color-mix(in srgb, ${color} 12%, transparent)`
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
const contrast = getContrastTextColor(color);
|
|
30
|
+
return {
|
|
31
|
+
backgroundColor: color,
|
|
32
|
+
borderColor: color,
|
|
33
|
+
color: contrast ?? '#fff'
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
export default getOptionColorStyle;
|
|
@@ -0,0 +1,42 @@
|
|
|
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 { get, type } from '@lowdefy/helpers';
|
|
16
|
+
import serializeSelectorValue from './serializeSelectorValue.js';
|
|
17
|
+
// Maps the current block value to the index (or indices) of the matching entry built by
|
|
18
|
+
// getSelectorOptions. Matching is by identity: when `primaryKey` names a field, that field is
|
|
19
|
+
// projected from both the stored value and each entry's value before comparison; with no
|
|
20
|
+
// `primaryKey` the whole value is compared (the original `value` behaviour, which also matches
|
|
21
|
+
// object-valued options). `valueKey` is only a build-time extraction key in getSelectorOptions —
|
|
22
|
+
// by match time the stored value is already the unwrapped entry value, so it must not drive
|
|
23
|
+
// identity here (reusing it projected a missing `.value` off the bare value and broke matching).
|
|
24
|
+
const serialize = serializeSelectorValue;
|
|
25
|
+
const getSelectedIndex = (value, entries, { properties = {}, multiple } = {})=>{
|
|
26
|
+
const { primaryKey } = properties;
|
|
27
|
+
const project = (val)=>type.isString(primaryKey) && type.isObject(val) ? get(val, primaryKey) : val;
|
|
28
|
+
const idOfValue = (v)=>project(v);
|
|
29
|
+
const idOfEntry = (entry)=>project(type.isPrimitive(entry) ? entry : entry.value);
|
|
30
|
+
const findIndex = (v)=>{
|
|
31
|
+
const target = serialize(idOfValue(v));
|
|
32
|
+
for(let i = 0; i < entries.length; i += 1){
|
|
33
|
+
if (serialize(idOfEntry(entries[i])) === target) return `${i}`;
|
|
34
|
+
}
|
|
35
|
+
return undefined;
|
|
36
|
+
};
|
|
37
|
+
if (multiple) {
|
|
38
|
+
return (type.isArray(value) ? value : []).map((v)=>findIndex(v));
|
|
39
|
+
}
|
|
40
|
+
return findIndex(value);
|
|
41
|
+
};
|
|
42
|
+
export default getSelectedIndex;
|
|
@@ -0,0 +1,67 @@
|
|
|
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 { get, type } from '@lowdefy/helpers';
|
|
16
|
+
import { nunjucksFunction } from '@lowdefy/nunjucks';
|
|
17
|
+
import serializeSelectorValue from './serializeSelectorValue.js';
|
|
18
|
+
// Selectors can be driven two ways:
|
|
19
|
+
// - `options`: an array of primitives or { label, value } pairs (the original model).
|
|
20
|
+
// - `data` + `html`: raw rows rendered through a Nunjucks template, where `valueKey` names the
|
|
21
|
+
// field stored as the value (omitted -> the whole row).
|
|
22
|
+
// Both are normalised here into the option shape the selectors already render: an array of
|
|
23
|
+
// primitives or objects carrying `label` (an html string) and `value`, plus any per-option extras
|
|
24
|
+
// (color, disabled, style, filterString, icon, tag) the blocks read.
|
|
25
|
+
// The identity used to de-duplicate options, kept symmetric with getSelectedIndex: project
|
|
26
|
+
// `primaryKey` from each entry's value, or compare the whole value when no `primaryKey` is set.
|
|
27
|
+
// `valueKey` has already been applied to produce `entry.value`, so it must not drive identity
|
|
28
|
+
// (projecting it off the wrapper entry yielded `undefined` for every option and collapsed them).
|
|
29
|
+
function identityOf(entry, primaryKey) {
|
|
30
|
+
const value = type.isPrimitive(entry) ? entry : entry.value;
|
|
31
|
+
return type.isString(primaryKey) && type.isObject(value) ? get(value, primaryKey) : value;
|
|
32
|
+
}
|
|
33
|
+
function dedupe(entries, primaryKey) {
|
|
34
|
+
const seen = new Set();
|
|
35
|
+
return entries.filter((entry)=>{
|
|
36
|
+
const id = serializeSelectorValue(identityOf(entry, primaryKey));
|
|
37
|
+
if (seen.has(id)) return false;
|
|
38
|
+
seen.add(id);
|
|
39
|
+
return true;
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
const getSelectorOptions = ({ properties })=>{
|
|
43
|
+
const { data, options, valueKey, primaryKey, html } = properties;
|
|
44
|
+
if (type.isArray(data)) {
|
|
45
|
+
const template = type.isString(html) ? nunjucksFunction(html) : null;
|
|
46
|
+
const entries = data.map((item, index)=>{
|
|
47
|
+
const value = type.isString(valueKey) && type.isObject(item) ? get(item, valueKey) : item;
|
|
48
|
+
const label = template ? template({
|
|
49
|
+
item,
|
|
50
|
+
index
|
|
51
|
+
}) : `${value}`;
|
|
52
|
+
return {
|
|
53
|
+
...type.isObject(item) ? item : {},
|
|
54
|
+
label,
|
|
55
|
+
value
|
|
56
|
+
};
|
|
57
|
+
});
|
|
58
|
+
return dedupe(entries, primaryKey);
|
|
59
|
+
}
|
|
60
|
+
const vk = type.isString(valueKey) ? valueKey : 'value';
|
|
61
|
+
const entries = (options ?? []).map((opt)=>type.isPrimitive(opt) ? opt : {
|
|
62
|
+
...opt,
|
|
63
|
+
value: get(opt, vk)
|
|
64
|
+
});
|
|
65
|
+
return dedupe(entries, primaryKey);
|
|
66
|
+
};
|
|
67
|
+
export default getSelectorOptions;
|