@granularjs/ui 0.1.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/README.md +116 -0
- package/dist/fonts/Arimo-400.ttf +0 -0
- package/dist/fonts/Arimo-500.ttf +1449 -0
- package/dist/fonts/Arimo-600.ttf +1449 -0
- package/dist/fonts/Arimo-700.ttf +0 -0
- package/dist/fonts/Inter-400.woff2 +0 -0
- package/dist/fonts/Inter-500.woff2 +0 -0
- package/dist/fonts/Inter-600.woff2 +0 -0
- package/dist/fonts/Inter-700.woff2 +0 -0
- package/dist/fonts/Poppins-400.ttf +0 -0
- package/dist/fonts/Poppins-500.ttf +0 -0
- package/dist/fonts/Poppins-600.ttf +0 -0
- package/dist/fonts/Poppins-700.ttf +0 -0
- package/dist/granular-ui.min.js +3605 -0
- package/dist/granular-ui.min.js.map +7 -0
- package/package.json +55 -0
- package/src/components/Accordion.js +25 -0
- package/src/components/ActionIcon.js +20 -0
- package/src/components/Affix.js +11 -0
- package/src/components/Alert.js +33 -0
- package/src/components/Anchor.js +8 -0
- package/src/components/AppBar.js +14 -0
- package/src/components/Avatar.js +13 -0
- package/src/components/AvatarGroup.js +8 -0
- package/src/components/Badge.js +22 -0
- package/src/components/BadgeGroup.js +8 -0
- package/src/components/Blockquote.js +8 -0
- package/src/components/BottomBar.js +43 -0
- package/src/components/Breadcrumbs.js +19 -0
- package/src/components/Burger.js +13 -0
- package/src/components/Button.js +37 -0
- package/src/components/Calendar.js +109 -0
- package/src/components/Card.js +40 -0
- package/src/components/Center.js +8 -0
- package/src/components/Checkbox.js +46 -0
- package/src/components/CheckboxGroup.js +8 -0
- package/src/components/Chip.js +35 -0
- package/src/components/Code.js +8 -0
- package/src/components/Col.js +8 -0
- package/src/components/Collapse.js +8 -0
- package/src/components/Container.js +19 -0
- package/src/components/CopyButton.js +30 -0
- package/src/components/DateInput.js +123 -0
- package/src/components/DatePicker.js +7 -0
- package/src/components/Divider.js +22 -0
- package/src/components/Drawer.js +32 -0
- package/src/components/EventCalendar.js +972 -0
- package/src/components/Fieldset.js +12 -0
- package/src/components/Flex.js +25 -0
- package/src/components/Grid.js +8 -0
- package/src/components/GridTable.js +99 -0
- package/src/components/Group.js +29 -0
- package/src/components/HoverCard.js +24 -0
- package/src/components/Icon.js +19 -0
- package/src/components/Image.js +8 -0
- package/src/components/Indicator.js +21 -0
- package/src/components/Kbd.js +8 -0
- package/src/components/List.js +77 -0
- package/src/components/Loading.js +29 -0
- package/src/components/LoadingOverlay.js +9 -0
- package/src/components/Menu.js +129 -0
- package/src/components/Modal.js +61 -0
- package/src/components/MultiSelect.js +153 -0
- package/src/components/NavLink.js +72 -0
- package/src/components/Notification.js +42 -0
- package/src/components/Notifications.js +59 -0
- package/src/components/NumberField.js +389 -0
- package/src/components/NumberInput.js +5 -0
- package/src/components/Pagination.js +56 -0
- package/src/components/Paper.js +20 -0
- package/src/components/PasswordInput.js +29 -0
- package/src/components/PinInput.js +218 -0
- package/src/components/Popover.js +38 -0
- package/src/components/Popper.js +25 -0
- package/src/components/Progress.js +27 -0
- package/src/components/ProgressRing.js +11 -0
- package/src/components/Radio.js +22 -0
- package/src/components/RadioGroup.js +8 -0
- package/src/components/RangePicker.js +45 -0
- package/src/components/RangeSlider.js +143 -0
- package/src/components/Rating.js +42 -0
- package/src/components/ScrollArea.js +11 -0
- package/src/components/SearchInput.js +17 -0
- package/src/components/SegmentedControl.js +39 -0
- package/src/components/Select.js +71 -0
- package/src/components/SelectSearch.js +37 -0
- package/src/components/Sidebar.js +136 -0
- package/src/components/SimpleGrid.js +11 -0
- package/src/components/Skeleton.js +24 -0
- package/src/components/Slider.js +126 -0
- package/src/components/Space.js +8 -0
- package/src/components/Stack.js +27 -0
- package/src/components/Stepper.js +20 -0
- package/src/components/Switch.js +16 -0
- package/src/components/SwitchGroup.js +8 -0
- package/src/components/Table.js +42 -0
- package/src/components/Tabs.js +194 -0
- package/src/components/Tag.js +8 -0
- package/src/components/Text.js +42 -0
- package/src/components/TextInput.js +74 -0
- package/src/components/Textarea.js +15 -0
- package/src/components/Timeline.js +22 -0
- package/src/components/Title.js +18 -0
- package/src/components/Toast.js +16 -0
- package/src/components/ToastStack.js +21 -0
- package/src/components/Tooltip.js +12 -0
- package/src/hooks/useDisclosure.js +13 -0
- package/src/index.js +98 -0
- package/src/theme/fonts/Arimo-400.ttf +0 -0
- package/src/theme/fonts/Arimo-500.ttf +1449 -0
- package/src/theme/fonts/Arimo-600.ttf +1449 -0
- package/src/theme/fonts/Arimo-700.ttf +0 -0
- package/src/theme/fonts/Inter-400.woff2 +0 -0
- package/src/theme/fonts/Inter-500.woff2 +0 -0
- package/src/theme/fonts/Inter-600.woff2 +0 -0
- package/src/theme/fonts/Inter-700.woff2 +0 -0
- package/src/theme/fonts/Poppins-400.ttf +0 -0
- package/src/theme/fonts/Poppins-500.ttf +0 -0
- package/src/theme/fonts/Poppins-600.ttf +0 -0
- package/src/theme/fonts/Poppins-700.ttf +0 -0
- package/src/theme/icons.js +10 -0
- package/src/theme/styles.js +3630 -0
- package/src/theme/theme.js +71 -0
- package/src/utils.js +75 -0
- package/types/components/Accordion.d.ts +1 -0
- package/types/components/ActionIcon.d.ts +1 -0
- package/types/components/Affix.d.ts +1 -0
- package/types/components/Alert.d.ts +1 -0
- package/types/components/Anchor.d.ts +1 -0
- package/types/components/AppBar.d.ts +1 -0
- package/types/components/Avatar.d.ts +1 -0
- package/types/components/AvatarGroup.d.ts +1 -0
- package/types/components/Badge.d.ts +1 -0
- package/types/components/BadgeGroup.d.ts +1 -0
- package/types/components/Blockquote.d.ts +1 -0
- package/types/components/BottomBar.d.ts +4 -0
- package/types/components/Breadcrumbs.d.ts +1 -0
- package/types/components/Burger.d.ts +1 -0
- package/types/components/Button.d.ts +1 -0
- package/types/components/Calendar.d.ts +1 -0
- package/types/components/Card.d.ts +1 -0
- package/types/components/Center.d.ts +1 -0
- package/types/components/Checkbox.d.ts +1 -0
- package/types/components/CheckboxGroup.d.ts +1 -0
- package/types/components/Chip.d.ts +1 -0
- package/types/components/Code.d.ts +1 -0
- package/types/components/Col.d.ts +1 -0
- package/types/components/Collapse.d.ts +1 -0
- package/types/components/Container.d.ts +1 -0
- package/types/components/CopyButton.d.ts +1 -0
- package/types/components/DateInput.d.ts +1 -0
- package/types/components/DatePicker.d.ts +1 -0
- package/types/components/Divider.d.ts +1 -0
- package/types/components/Drawer.d.ts +1 -0
- package/types/components/EventCalendar.d.ts +1 -0
- package/types/components/Fieldset.d.ts +1 -0
- package/types/components/Flex.d.ts +1 -0
- package/types/components/Grid.d.ts +1 -0
- package/types/components/GridTable.d.ts +5 -0
- package/types/components/Group.d.ts +1 -0
- package/types/components/HoverCard.d.ts +1 -0
- package/types/components/Icon.d.ts +1 -0
- package/types/components/Image.d.ts +1 -0
- package/types/components/Indicator.d.ts +1 -0
- package/types/components/Kbd.d.ts +1 -0
- package/types/components/List.d.ts +5 -0
- package/types/components/Loading.d.ts +1 -0
- package/types/components/LoadingOverlay.d.ts +1 -0
- package/types/components/Menu.d.ts +2 -0
- package/types/components/Modal.d.ts +1 -0
- package/types/components/MultiSelect.d.ts +1 -0
- package/types/components/NavLink.d.ts +1 -0
- package/types/components/Notification.d.ts +1 -0
- package/types/components/Notifications.d.ts +1 -0
- package/types/components/NumberField.d.ts +1 -0
- package/types/components/NumberInput.d.ts +1 -0
- package/types/components/Pagination.d.ts +1 -0
- package/types/components/Paper.d.ts +1 -0
- package/types/components/PasswordInput.d.ts +1 -0
- package/types/components/PinInput.d.ts +1 -0
- package/types/components/Popover.d.ts +1 -0
- package/types/components/Popper.d.ts +1 -0
- package/types/components/Progress.d.ts +1 -0
- package/types/components/ProgressRing.d.ts +1 -0
- package/types/components/Radio.d.ts +1 -0
- package/types/components/RadioGroup.d.ts +1 -0
- package/types/components/RangePicker.d.ts +1 -0
- package/types/components/RangeSlider.d.ts +1 -0
- package/types/components/Rating.d.ts +1 -0
- package/types/components/ScrollArea.d.ts +1 -0
- package/types/components/SearchInput.d.ts +1 -0
- package/types/components/SegmentedControl.d.ts +1 -0
- package/types/components/Select.d.ts +1 -0
- package/types/components/SelectSearch.d.ts +1 -0
- package/types/components/Sidebar.d.ts +1 -0
- package/types/components/SimpleGrid.d.ts +1 -0
- package/types/components/Skeleton.d.ts +1 -0
- package/types/components/Slider.d.ts +5 -0
- package/types/components/Space.d.ts +1 -0
- package/types/components/Stack.d.ts +1 -0
- package/types/components/Stepper.d.ts +1 -0
- package/types/components/Switch.d.ts +1 -0
- package/types/components/SwitchGroup.d.ts +1 -0
- package/types/components/Table.d.ts +1 -0
- package/types/components/Tabs.d.ts +1 -0
- package/types/components/Tag.d.ts +1 -0
- package/types/components/Text.d.ts +1 -0
- package/types/components/TextInput.d.ts +1 -0
- package/types/components/Textarea.d.ts +1 -0
- package/types/components/Timeline.d.ts +1 -0
- package/types/components/Title.d.ts +1 -0
- package/types/components/Toast.d.ts +1 -0
- package/types/components/ToastStack.d.ts +1 -0
- package/types/components/Tooltip.d.ts +1 -0
- package/types/hooks/useDisclosure.d.ts +1 -0
- package/types/index.d.ts +93 -0
- package/types/theme/icons.d.ts +10 -0
- package/types/theme/styles.d.ts +1 -0
- package/types/theme/theme.d.ts +2 -0
- package/types/utils.d.ts +12 -0
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { Div, Span, Input, when, after, state } from '@granularjs/core';
|
|
2
|
+
import { cx, splitPropsChildren, classVar, resolveValue } from '../utils.js';
|
|
3
|
+
import { checkedSvg, closeSvg } from '../theme/icons.js';
|
|
4
|
+
|
|
5
|
+
export function MultiSelect(...args) {
|
|
6
|
+
const { props, rawProps } = splitPropsChildren(args, { data: [], size: 'md', searchable: true });
|
|
7
|
+
const { value, data, size, className, placeholder, searchable, ...rest } = props;
|
|
8
|
+
const { onChange, onSearchChange } = rawProps;
|
|
9
|
+
const currentState = state(resolveValue(value) ?? []);
|
|
10
|
+
const searchState = state('');
|
|
11
|
+
const openState = state(false);
|
|
12
|
+
const rootNode = state(null);
|
|
13
|
+
|
|
14
|
+
const normalizeData = (items) => {
|
|
15
|
+
const resolved = resolveValue(items) ?? [];
|
|
16
|
+
return resolved.map((item) => {
|
|
17
|
+
if (typeof item === 'string') return { value: item, label: item };
|
|
18
|
+
if (item && typeof item === 'object') return item;
|
|
19
|
+
return { value: String(item), label: String(item) };
|
|
20
|
+
});
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
after(value).change((next) => {
|
|
24
|
+
const resolved = resolveValue(next);
|
|
25
|
+
if (resolved == null) return;
|
|
26
|
+
currentState.set(resolved);
|
|
27
|
+
});
|
|
28
|
+
let outsideCleanup = null;
|
|
29
|
+
after(openState).change((next) => {
|
|
30
|
+
if (outsideCleanup) {
|
|
31
|
+
outsideCleanup();
|
|
32
|
+
outsideCleanup = null;
|
|
33
|
+
}
|
|
34
|
+
if (!next) return;
|
|
35
|
+
const handler = (ev) => {
|
|
36
|
+
const root = rootNode.get();
|
|
37
|
+
if (!root) return;
|
|
38
|
+
if (root.contains(ev.target)) return;
|
|
39
|
+
openState.set(false);
|
|
40
|
+
};
|
|
41
|
+
document.addEventListener('mousedown', handler);
|
|
42
|
+
outsideCleanup = () => document.removeEventListener('mousedown', handler);
|
|
43
|
+
});
|
|
44
|
+
after(searchState).change((next) => {
|
|
45
|
+
onSearchChange?.(next);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
const toggle = (val) => {
|
|
49
|
+
const selected = currentState.get() ?? [];
|
|
50
|
+
const next = selected.includes(val)
|
|
51
|
+
? selected.filter((v) => v !== val)
|
|
52
|
+
: selected.concat(val);
|
|
53
|
+
currentState.set(next);
|
|
54
|
+
onChange?.(next);
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const normalizedData = after(data).compute((nextData) => normalizeData(nextData));
|
|
58
|
+
const filteredItems = after(normalizedData, searchState).compute(([items, query]) => {
|
|
59
|
+
const q = String(resolveValue(query) ?? '').toLowerCase().trim();
|
|
60
|
+
if (!q) return items;
|
|
61
|
+
return items.filter((item) => String(item.label ?? '').toLowerCase().includes(q));
|
|
62
|
+
});
|
|
63
|
+
const isEmpty = after(currentState, searchState).compute(([nextSelected, query]) => {
|
|
64
|
+
const list = resolveValue(nextSelected) ?? [];
|
|
65
|
+
const q = String(resolveValue(query) ?? '');
|
|
66
|
+
return (!list.length) && !q;
|
|
67
|
+
});
|
|
68
|
+
const isSearchable = after(searchable).compute((next) => !!next);
|
|
69
|
+
|
|
70
|
+
return Div(
|
|
71
|
+
{ ...rest, node: rootNode, className: cx('g-ui-select-multi-root', className) },
|
|
72
|
+
Div(
|
|
73
|
+
{
|
|
74
|
+
className: cx('g-ui-select-multi', classVar('g-ui-select-multi-size-', size, 'md')),
|
|
75
|
+
onClick: () => openState.set(true),
|
|
76
|
+
},
|
|
77
|
+
after(normalizedData, currentState).compute(([items, current]) => {
|
|
78
|
+
const list = resolveValue(current) ?? [];
|
|
79
|
+
return list.map((val) => {
|
|
80
|
+
const match = (items ?? []).find((entry) => entry.value === val);
|
|
81
|
+
const label = match?.label ?? val;
|
|
82
|
+
return Span(
|
|
83
|
+
{
|
|
84
|
+
className: 'g-ui-select-tag',
|
|
85
|
+
},
|
|
86
|
+
Span(
|
|
87
|
+
{
|
|
88
|
+
className: 'g-ui-select-tag-label',
|
|
89
|
+
onClick: (ev) => {
|
|
90
|
+
ev?.stopPropagation?.();
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
label
|
|
94
|
+
),
|
|
95
|
+
Span({
|
|
96
|
+
className: 'g-ui-select-tag-remove',
|
|
97
|
+
innerHTML: closeSvg,
|
|
98
|
+
onClick: (ev) => {
|
|
99
|
+
ev?.stopPropagation?.();
|
|
100
|
+
toggle(val);
|
|
101
|
+
},
|
|
102
|
+
})
|
|
103
|
+
);
|
|
104
|
+
});
|
|
105
|
+
}),
|
|
106
|
+
when(isEmpty, () => Span({ className: 'g-ui-select-multi-placeholder' }, placeholder ?? 'Select...')),
|
|
107
|
+
when(isSearchable, () =>
|
|
108
|
+
Input({
|
|
109
|
+
className: 'g-ui-select-multi-input',
|
|
110
|
+
value: searchState,
|
|
111
|
+
onInput: (ev) => searchState.set(ev.target?.value ?? ''),
|
|
112
|
+
onFocus: () => openState.set(true),
|
|
113
|
+
})
|
|
114
|
+
)
|
|
115
|
+
),
|
|
116
|
+
when(openState, () =>
|
|
117
|
+
Div(
|
|
118
|
+
{ className: 'g-ui-select-dropdown' },
|
|
119
|
+
after(filteredItems).compute((items) => {
|
|
120
|
+
if (!items.length) {
|
|
121
|
+
return Div({ className: 'g-ui-select-item' }, 'Nothing found');
|
|
122
|
+
}
|
|
123
|
+
return items.map((item) =>
|
|
124
|
+
Div(
|
|
125
|
+
{
|
|
126
|
+
className: cx(
|
|
127
|
+
'g-ui-select-item',
|
|
128
|
+
item.disabled && 'g-ui-select-item-disabled',
|
|
129
|
+
after(currentState).compute((current) => {
|
|
130
|
+
const list = resolveValue(current) ?? [];
|
|
131
|
+
return list.includes(item.value) ? 'g-ui-select-item-active' : '';
|
|
132
|
+
})
|
|
133
|
+
),
|
|
134
|
+
onClick: () => {
|
|
135
|
+
if (item.disabled) return;
|
|
136
|
+
toggle(item.value);
|
|
137
|
+
},
|
|
138
|
+
},
|
|
139
|
+
Span({
|
|
140
|
+
className: 'g-ui-select-item-check',
|
|
141
|
+
innerHTML: after(currentState).compute((current) => {
|
|
142
|
+
const list = resolveValue(current) ?? [];
|
|
143
|
+
return list.includes(item.value) ? checkedSvg : '';
|
|
144
|
+
}),
|
|
145
|
+
}),
|
|
146
|
+
Span({ className: 'g-ui-select-item-label' }, item.label)
|
|
147
|
+
)
|
|
148
|
+
);
|
|
149
|
+
})
|
|
150
|
+
)
|
|
151
|
+
)
|
|
152
|
+
);
|
|
153
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { A, Div, Span, when, after, state } from '@granularjs/core';
|
|
2
|
+
import { cx, splitPropsChildren, classVar, classFlag, resolveValue, toPx } from '../utils.js';
|
|
3
|
+
|
|
4
|
+
export function NavLink(...args) {
|
|
5
|
+
const { props, rawProps, children } = splitPropsChildren(args, { variant: 'subtle', childrenOffset: 24 });
|
|
6
|
+
const {
|
|
7
|
+
label,
|
|
8
|
+
description,
|
|
9
|
+
leftSection,
|
|
10
|
+
rightSection,
|
|
11
|
+
active,
|
|
12
|
+
disabled,
|
|
13
|
+
variant,
|
|
14
|
+
childrenOffset,
|
|
15
|
+
opened,
|
|
16
|
+
defaultOpened,
|
|
17
|
+
className,
|
|
18
|
+
padding,
|
|
19
|
+
...rest
|
|
20
|
+
} = props;
|
|
21
|
+
const { onClick, onChange } = rawProps;
|
|
22
|
+
const hasChildren = children.length > 0;
|
|
23
|
+
const openState = state(resolveValue(opened) ?? resolveValue(defaultOpened) ?? false);
|
|
24
|
+
after(opened).change((next) => {
|
|
25
|
+
const resolved = resolveValue(next);
|
|
26
|
+
if (resolved == null) return;
|
|
27
|
+
openState.set(!!resolved);
|
|
28
|
+
});
|
|
29
|
+
const childrenStyle = after(childrenOffset).compute((next) => ({
|
|
30
|
+
paddingLeft: toPx(resolveValue(next ?? 24)) ?? '24px',
|
|
31
|
+
}));
|
|
32
|
+
|
|
33
|
+
const handleClick = (ev) => {
|
|
34
|
+
if (resolveValue(disabled)) {
|
|
35
|
+
ev?.preventDefault?.();
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
if (hasChildren) {
|
|
39
|
+
const next = !openState.get();
|
|
40
|
+
openState.set(next);
|
|
41
|
+
onChange?.(next);
|
|
42
|
+
}
|
|
43
|
+
onClick?.(ev);
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
return Div(
|
|
47
|
+
{ className: cx('g-ui-navlink-root', className) },
|
|
48
|
+
A(
|
|
49
|
+
{
|
|
50
|
+
...rest,
|
|
51
|
+
className: cx(
|
|
52
|
+
'g-ui-navlink',
|
|
53
|
+
classVar('g-ui-navlink-variant-', variant, 'subtle'),
|
|
54
|
+
classFlag('g-ui-navlink-active', active),
|
|
55
|
+
classFlag('g-ui-navlink-disabled', disabled),
|
|
56
|
+
classVar('g-ui-navlink-padding-', padding, 'md')
|
|
57
|
+
),
|
|
58
|
+
onClick: handleClick,
|
|
59
|
+
},
|
|
60
|
+
when(leftSection, () => Span({ className: 'g-ui-navlink-section g-ui-navlink-left' }, leftSection)),
|
|
61
|
+
Div(
|
|
62
|
+
{ className: 'g-ui-navlink-body' },
|
|
63
|
+
when(label, () => Div({ className: 'g-ui-navlink-label' }, label)),
|
|
64
|
+
when(description, () => Div({ className: 'g-ui-navlink-description' }, description))
|
|
65
|
+
),
|
|
66
|
+
when(rightSection, () => Span({ className: 'g-ui-navlink-section g-ui-navlink-right' }, rightSection))
|
|
67
|
+
),
|
|
68
|
+
hasChildren
|
|
69
|
+
? when(openState, () => Div({ className: 'g-ui-navlink-children', style: childrenStyle }, children))
|
|
70
|
+
: null
|
|
71
|
+
);
|
|
72
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { Button, Div, Span, when, after } from '@granularjs/core';
|
|
2
|
+
import { cx, splitPropsChildren, resolveBool } from '../utils.js';
|
|
3
|
+
import { Loading } from './Loading.js';
|
|
4
|
+
import { closeSvg } from '../theme/icons.js';
|
|
5
|
+
|
|
6
|
+
export function Notification(...args) {
|
|
7
|
+
const { props, rawProps, children } = splitPropsChildren(args, { color: 'blue', withCloseButton: true });
|
|
8
|
+
const {
|
|
9
|
+
title,
|
|
10
|
+
color,
|
|
11
|
+
icon,
|
|
12
|
+
loading,
|
|
13
|
+
withCloseButton,
|
|
14
|
+
withBorder,
|
|
15
|
+
className,
|
|
16
|
+
style,
|
|
17
|
+
} = props;
|
|
18
|
+
const { onClose } = rawProps;
|
|
19
|
+
const showLoader = after(icon, loading).compute(([nextIcon, nextLoading]) => !nextIcon && !!nextLoading);
|
|
20
|
+
const withIcon = after(icon, loading).compute(([nextIcon, nextLoading]) => !!nextIcon || !!nextLoading);
|
|
21
|
+
return Div(
|
|
22
|
+
{
|
|
23
|
+
className: cx(
|
|
24
|
+
'g-ui-notification',
|
|
25
|
+
[withBorder, 'g-ui-notification-bordered'],
|
|
26
|
+
[color, (value) => `g-ui-notification-${value}`],
|
|
27
|
+
[withIcon, 'g-ui-notification-with-icon'],
|
|
28
|
+
className
|
|
29
|
+
),
|
|
30
|
+
},
|
|
31
|
+
when(icon, () => Span({ className: 'g-ui-notification-icon' }, icon)),
|
|
32
|
+
when(showLoader, () => Loading({ size: 'sm', className: 'g-ui-notification-loader' })),
|
|
33
|
+
Div(
|
|
34
|
+
{ className: 'g-ui-notification-body' },
|
|
35
|
+
when(title, () => Div({ className: 'g-ui-notification-title' }, title)),
|
|
36
|
+
children
|
|
37
|
+
),
|
|
38
|
+
when(withCloseButton, () =>
|
|
39
|
+
Button({ type: 'button', className: 'g-ui-notification-close', onClick: () => onClose?.() }, Span({ innerHTML: closeSvg }))
|
|
40
|
+
)
|
|
41
|
+
);
|
|
42
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { Div, when, after } from '@granularjs/core';
|
|
2
|
+
import { cx, splitPropsChildren, classVar, resolveValue } from '../utils.js';
|
|
3
|
+
import { Notification } from './Notification.js';
|
|
4
|
+
|
|
5
|
+
export function Notifications(...args) {
|
|
6
|
+
const { props, rawProps } = splitPropsChildren(args, { items: [], position: 'top-right' });
|
|
7
|
+
const { items, position, className, ...rest } = props;
|
|
8
|
+
const { onRemove } = rawProps;
|
|
9
|
+
const timers = new Map();
|
|
10
|
+
|
|
11
|
+
const getId = (item, index) => item?.id ?? item?.key ?? index;
|
|
12
|
+
|
|
13
|
+
const notifications = after(items).compute((nextItems) => {
|
|
14
|
+
const list = resolveValue(nextItems) ?? [];
|
|
15
|
+
const active = new Set();
|
|
16
|
+
list.forEach((item, index) => {
|
|
17
|
+
const id = getId(item, index);
|
|
18
|
+
active.add(id);
|
|
19
|
+
if (item?.autoClose && !timers.has(id)) {
|
|
20
|
+
const timeout = setTimeout(() => {
|
|
21
|
+
timers.delete(id);
|
|
22
|
+
onRemove?.(item);
|
|
23
|
+
}, item.autoClose);
|
|
24
|
+
timers.set(id, timeout);
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
for (const [id, handle] of timers.entries()) {
|
|
28
|
+
if (!active.has(id)) {
|
|
29
|
+
clearTimeout(handle);
|
|
30
|
+
timers.delete(id);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return list.map((item, index) =>
|
|
34
|
+
Notification(
|
|
35
|
+
{
|
|
36
|
+
key: getId(item, index),
|
|
37
|
+
title: item.title,
|
|
38
|
+
color: item.color,
|
|
39
|
+
icon: item.icon,
|
|
40
|
+
loading: item.loading,
|
|
41
|
+
withCloseButton: item.withCloseButton,
|
|
42
|
+
withBorder: item.withBorder,
|
|
43
|
+
onClose: () => {
|
|
44
|
+
item.onClose?.();
|
|
45
|
+
onRemove?.(item);
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
item.message ?? item.body ?? item.content ?? item.text
|
|
49
|
+
)
|
|
50
|
+
);
|
|
51
|
+
});
|
|
52
|
+
return Div(
|
|
53
|
+
{
|
|
54
|
+
...rest,
|
|
55
|
+
className: cx('g-ui-notifications', classVar('g-ui-notifications-position-', position, 'top-right'), className),
|
|
56
|
+
},
|
|
57
|
+
notifications
|
|
58
|
+
);
|
|
59
|
+
}
|