@nebularstreams/libmui 2.0.2 → 2.0.3

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.
@@ -0,0 +1,346 @@
1
+ import m from "mithril";
2
+
3
+ export function defineWidget(view, oninit, oncreate, onremove, onupdate) {
4
+ return {
5
+ ...view && {view},
6
+ ...oninit && {oninit},
7
+ ...oncreate && {oncreate},
8
+ ...onupdate && {onupdate},
9
+ ...onremove && {onremove}
10
+ }
11
+ }
12
+
13
+
14
+ // ------------------------------------------------------------------------
15
+
16
+ function getAttrs(attrs, parentAttrs, panelInitialParams) {
17
+ const data = ((typeof attrs === "function") ? attrs(parentAttrs) : attrs) || {};
18
+ if (panelInitialParams) data.initialParams = panelInitialParams;
19
+ return data;
20
+ }
21
+
22
+ const
23
+
24
+ isCurrent = ({currentIndex}, index) => currentIndex === index,
25
+
26
+ Icon = {
27
+ view: ({attrs}) => m(
28
+ `i.icon.${attrs.pack || "fas"}.fa-${attrs.icon || "cog"}${attrs.size ? ".fa-" + attrs.size + "x" : ""}`,
29
+ {
30
+ title: attrs.icontitle || undefined,
31
+ role: "presentation",
32
+ onclick: attrs.onclick || null, ...attrs.iconClass && {class: attrs.iconClass}
33
+ }
34
+ )
35
+ },
36
+
37
+ Button = {
38
+ view: ({attrs, state, children}) => m((attrs.download || attrs.link) ? "a.button" : "button", {
39
+ ...attrs.link && {href: attrs.link},
40
+ ...attrs.download && {download: attrs.download},
41
+ ...attrs.target && {target: attrs.target},
42
+ "aria-label": attrs["aria-label"] || attrs.title || attrs.text,
43
+ title: attrs.title || attrs["aria-label"],
44
+ class: (attrs.class || "") + (state.busy ? " busy" : ""),
45
+ ...state.error && {"data-error": state.error.message},
46
+ ...attrs.onclick && {
47
+ onclick: (event) => {
48
+ clearTimeout(state.debounce);
49
+ state.debounce = setTimeout(() => {
50
+ const result = attrs.onclick(event, attrs);
51
+ if (result && result.then && result.catch) {
52
+ state.busy = true;
53
+ m.redraw();
54
+ result
55
+ .then(() => {
56
+ state.busy = false;
57
+ m.redraw();
58
+ })
59
+ .catch((error) => {
60
+ state.busy = false;
61
+ state.error = error;
62
+ m.redraw();
63
+ });
64
+ }
65
+ }, 25);
66
+ }
67
+ }
68
+ }, [
69
+ ...(attrs.icon && !attrs.right) ? [m(Icon, attrs)] : [],
70
+ ...attrs.text ? [m("span.pad-1.pad-h", {class: attrs.textClass || null}, attrs.text)] : [],
71
+ ...(attrs.icon && attrs.right) ? [m(Icon, attrs)] : [],
72
+ ...children
73
+ ])
74
+ },
75
+
76
+ ButtonNew = {
77
+ view: ({attrs, state, children}) => m((attrs.download || attrs.link) ? "a.button" : "button", {
78
+ ...attrs.link && {href: attrs.link},
79
+ ...attrs.download && {rel: "nofollow", download: attrs.download},
80
+ ...attrs.target && {target: attrs.target},
81
+ "aria-label": attrs["aria-label"] || attrs.title || attrs.text,
82
+ title: attrs.title || attrs["aria-label"],
83
+ class: (attrs.class || "") + (state.busy ? " busy" : ""),
84
+ ...state.error && {"data-error": state.error.message},
85
+ ...attrs.onClick && {
86
+ onclick: (event) => {
87
+ clearTimeout(state.debounce);
88
+ state.debounce = setTimeout(() => {
89
+ const result = attrs.onClick(event, attrs);
90
+ if (result && result.then && result.catch) {
91
+ state.busy = true;
92
+ m.redraw();
93
+ result
94
+ .then(() => {
95
+ state.busy = false;
96
+ m.redraw();
97
+ })
98
+ .catch((error) => {
99
+ state.busy = false;
100
+ state.error = error;
101
+ m.redraw();
102
+ });
103
+ }
104
+ }, 25);
105
+ }
106
+ }
107
+ }, [
108
+ ...(attrs.icon && !attrs.right) ? [m(Icon, attrs)] : [],
109
+ ...attrs.text ? [m("span.pad-1.pad-h", {class: attrs.textClass || null}, attrs.text)] : [],
110
+ ...(attrs.icon && attrs.right) ? [m(Icon, attrs)] : [],
111
+ ...children
112
+ ])
113
+ },
114
+
115
+ Empty = {
116
+
117
+ view: ({attrs, children}) => m(
118
+ ".flexcol.flexcenter.al-center.grows.emptywidget",
119
+ {
120
+ ...attrs.class && {class: attrs.class},
121
+ ...attrs.key && {key: attrs.key},
122
+ role: "presentation"
123
+ },
124
+ [
125
+ m(Icon, attrs),
126
+ ...attrs.message
127
+ ? [
128
+ m(".marg-2.marg-v",
129
+ {
130
+ role: "status",
131
+ class: attrs.size > 4 ? "font-150" : "font-100",
132
+ "aria-label": attrs.message
133
+ },
134
+ attrs.message.split("\n")
135
+ .map((str, idx, all) => all.length === 1 ? str : m("p.msg" + idx, str))
136
+ )
137
+ ]
138
+ : [],
139
+ ...children
140
+ ? [m(".flexcol.additional", children)]
141
+ : []
142
+ ]
143
+ )
144
+ },
145
+
146
+ setItem = (attrs, state, index, params) => {
147
+
148
+
149
+ const
150
+ lastIndex = state.currentIndex,
151
+ lastParams = state.currentParams;
152
+
153
+ if (index === -1) {
154
+ Object.assign(state, {
155
+ current: null,
156
+ currentIndex: -1,
157
+ currentParams: []
158
+ });
159
+ } else {
160
+ Object.assign(state, {
161
+ current: attrs.items[index] || null,
162
+ currentIndex: state.current ? index : -1,
163
+ currentParams: params || []
164
+ });
165
+ }
166
+
167
+ if (attrs.onSelected) attrs.onSelected(state.current, state.currentParams, state.currentIndex !== lastIndex, lastParams);
168
+
169
+ m.redraw();
170
+ },
171
+
172
+ Selector = {
173
+
174
+ oninit: ({attrs, state}) => {
175
+
176
+ state = attrs.state || state;
177
+ state.setItem = (index, ...params) => setItem(attrs, attrs.state || state, index, params);
178
+
179
+ const
180
+ hasState = !isNaN(state.currentIndex),
181
+ selected = hasState ? state.currentIndex : attrs.selected,
182
+ currentIndex = (attrs.items && attrs.items.length)
183
+ ? (selected !== undefined && attrs.items[selected])
184
+ ? selected
185
+ : attrs.noinitial ? -1 : 0
186
+ : -1,
187
+ current = currentIndex !== -1 ? attrs.items[currentIndex] : null;
188
+
189
+ Object.assign(state, {currentIndex, current});
190
+
191
+ },
192
+
193
+ onupdate: ({attrs, state}) => {
194
+ (attrs.state || state).setItem = (index, ...params) => setItem(attrs, attrs.state || state, index, params);
195
+ },
196
+
197
+ view: ({attrs, state}) => {
198
+
199
+ state = attrs.state || state;
200
+ const {current} = state;
201
+
202
+ return m(
203
+ (attrs.element || "widget") + ".selector" + (attrs.horizontal ? ".flexrow" : ".flexcol") + (attrs.className ? "." + attrs.className : ""),
204
+ {
205
+ ...attrs.key && {key: attrs.key},
206
+ class: (state.current ? "hascurrent" : "empty") + (attrs.class ? " " + attrs.class : "")
207
+ },
208
+ [
209
+ ...((!attrs.hideonly || (attrs.items.length > 1)) && (!attrs.hidesel))
210
+ ? [
211
+ /** navigation bar */
212
+ m(
213
+ "nav.toolbar.fullwidth" + (attrs.horizontal ? ".flexcol" : ".flexrow"),
214
+ {
215
+ "aria-label": attrs["aria-label"] || attrs.title || "selector",
216
+ class: attrs.toolbarClass
217
+ },
218
+ attrs.items.map((item, index) => m(Button, {
219
+ class: (item.class ? item.class + " " : "") + (isCurrent(state, index) ? "accent" : "nel"),
220
+ icon: item.icon || null,
221
+ ...item.title && {title: item.title},
222
+ ...item["aria-label"] && {"aria-label": item["aria-label"]},
223
+ ...item.text && {text: item.text},
224
+ onclick: () => {
225
+ const t = Date.now();
226
+ if (t - state.clickts < 200) return;
227
+ state.clickts = Date.now();
228
+ state.bodyHidden = false;
229
+ state.setItem(index);
230
+ }
231
+ }))
232
+ )]
233
+ : [],
234
+ ...current
235
+ ? [
236
+ ...(attrs.toggle && !state.bodyHidden)
237
+ ? [
238
+ m(ButtonNew, {
239
+ class: "selectornotch " + (state.bodyHidden ? "bodyhidden" : "bodyshown"),
240
+ icon: state.bodyHidden ? "caret-up" : "caret-down",
241
+ title: state.bodyHidden ? "expand panel" : "contract panel",
242
+ onClick: () => {
243
+ state.bodyHidden = !state.bodyHidden;
244
+ m.redraw();
245
+ }
246
+ })
247
+ ] : [],
248
+ ...(!attrs.toggle || !state.bodyHidden)
249
+ ? [
250
+ /** body */
251
+ m(".grows.flexcol.ohidden", {class: attrs.bodyClass, role: "presentation"}, [
252
+ m(
253
+ current.widget,
254
+ getAttrs(current.attrs, attrs, state.currentParams),
255
+ current.children
256
+ )
257
+ ])
258
+ ]
259
+ : [],
260
+ ...attrs.closeButton
261
+ ? [
262
+ m(Button, {
263
+ class: "closebutton absolute toright",
264
+ icon: "times",
265
+ onclick: () => {
266
+ state.setItem(-1);
267
+ },
268
+ "aria-label": "close item"
269
+ })] : []
270
+ ]
271
+ : m(Empty, attrs)
272
+ ]
273
+ );
274
+ }
275
+ };
276
+
277
+ const ICONS = {
278
+ info: "info",
279
+ error: "exclamation-triangle",
280
+ success: "check"
281
+ };
282
+
283
+ function Notifications(TOAST_SHORT = 5000, altContainer) {
284
+
285
+ let
286
+ mNotifications = [],
287
+ mTimer;
288
+
289
+ function render(container, n) {
290
+ m.render(
291
+ container, m(
292
+ Button, {
293
+ class: "notification " + n.type,
294
+ text: n.text,
295
+ icon: ICONS[n.type] || "info",
296
+ size: 1,
297
+ role: "status",
298
+ // eslint-disable-next-line no-use-before-define
299
+ onclick: next
300
+ }
301
+ )
302
+ );
303
+ }
304
+
305
+ function next() {
306
+
307
+ const
308
+ container = altContainer || document.getElementById("notification-container");
309
+
310
+ if (mNotifications.length) {
311
+ const n = mNotifications.splice(0, 1)[0];
312
+ render(container, n);
313
+ clearTimeout(mTimer);
314
+ mTimer = setTimeout(next, n.timer || TOAST_SHORT);
315
+ } else {
316
+ m.render(container, "");
317
+ mTimer = null;
318
+ }
319
+ }
320
+
321
+ return {
322
+ add: (text, type) => {
323
+ mNotifications.push({
324
+ text,
325
+ type
326
+ });
327
+ if (!mTimer) next();
328
+ },
329
+ clear: () => {
330
+ mNotifications = [];
331
+ next();
332
+ }
333
+
334
+ };
335
+ }
336
+
337
+ const Notification = new Notifications();
338
+
339
+ export {
340
+ Empty,
341
+ Icon,
342
+ Selector,
343
+ Button,
344
+ ButtonNew,
345
+ Notification
346
+ }
@@ -0,0 +1,167 @@
1
+ import m from "mithril";
2
+ import {ButtonNew, defineWidget} from "./Basic";
3
+
4
+ const
5
+ globalState = {},
6
+ CARDIE_CLOSED = 0;
7
+
8
+ function setCardStatus(cardid, expanded = CARDIE_CLOSED) {
9
+ if (cardid) {
10
+ const [uniq, subname] = cardid.split("-");
11
+ if (expanded && uniq && subname) {
12
+ Object.keys(globalState)
13
+ .forEach((key) => {
14
+ if (key.startsWith(uniq + "-")) {
15
+ globalState[key] = CARDIE_CLOSED;
16
+ }
17
+ });
18
+ }
19
+ globalState[cardid] = expanded;
20
+ }
21
+ }
22
+
23
+ const CardieWidget = defineWidget(
24
+ ({attrs, state, children}) => {
25
+
26
+ const
27
+
28
+ {
29
+ nofold,
30
+ cardid,
31
+ size,
32
+ title,
33
+ action,
34
+ collapsedAction,
35
+ collapsedClass,
36
+ expandedClass,
37
+ cols,
38
+ element,
39
+ busy,
40
+ expandedView,
41
+ collapsedView,
42
+ titleWidget: titWidget,
43
+ titleAttrs,
44
+ titleClass,
45
+ bodyClass,
46
+ titleExpanded,
47
+ titleCollapsed
48
+ } = attrs,
49
+
50
+ collapsed = nofold
51
+ ? false
52
+ : cardid
53
+ ? !globalState[cardid]
54
+ : !!state.collapsed,
55
+
56
+ items = (!collapsed)
57
+ ? (
58
+ expandedView
59
+ ? expandedView(attrs, state)
60
+ : (children || [])
61
+ )
62
+ : collapsedView ? collapsedView(attrs, state) : [],
63
+
64
+ titleWidget = titWidget
65
+ ? m(titWidget, titleAttrs || {})
66
+ : title ? m("span.ellipsis.ohidden.unfold", title) : undefined,
67
+
68
+ buttonClass = busy
69
+ ? "fa-sync"
70
+ : `fa-caret-down ${collapsed ? "iscollapsed" : "isexpanded"}`;
71
+
72
+ return items.length || !attrs.skipEmpty
73
+ ? m(
74
+ (element || "ul"),
75
+ {
76
+ class: "cardie ohidden noshrink "
77
+ + (cols ? "flexcol " : "flexrow wrap ")
78
+ + (attrs.class ? " " + attrs.class : "")
79
+ + (nofold
80
+ ? " expanded"
81
+ : ` ${collapsed ? "collapsed " + (collapsedClass || "") : "expanded " + (expandedClass || "")}`)
82
+ + (size ? " marg-" + size : " marg-2")
83
+ + (titWidget ? " withwidget" : "")
84
+ + (busy ? " busycard" : "")
85
+ },
86
+ [
87
+
88
+ ...titleWidget
89
+ ? [
90
+ m(
91
+ ".title.grows.fabf.flexrow.pointer.unfold",
92
+ nofold
93
+ ? {
94
+ class: titleClass || "pad-2"
95
+ }
96
+ : {
97
+ class: `${titleClass || "pad-2"} ${buttonClass || ""}`,
98
+
99
+ title: collapsed
100
+ ? (titleCollapsed || "Click to expand the options")
101
+ : (titleExpanded || titleCollapsed || "Click to collapse the options"),
102
+
103
+ onclick: (e) => {
104
+
105
+ if (e.target.classList.contains("unfold")) {
106
+ if (e.shiftKey && attrs.onShiftOpen) {
107
+ attrs.onShiftOpen();
108
+ } else if (attrs.cardid) {
109
+ setCardStatus(cardid, collapsed); // !state
110
+ } else {
111
+ state.collapsed = !state.collapsed;
112
+ }
113
+
114
+ m.redraw();
115
+ }
116
+ }
117
+ },
118
+ action && (nofold || !collapsed)
119
+ ? [
120
+ m(ButtonNew, action),
121
+ titleWidget
122
+ ]
123
+ : collapsedAction && collapsed
124
+ ? [
125
+ m(ButtonNew, collapsedAction),
126
+ titleWidget
127
+ ]
128
+ : titleWidget
129
+ )
130
+ ]
131
+ : [],
132
+
133
+ ...items.length
134
+ ? [
135
+ m(".flexbreak"),
136
+ m(
137
+ ".cardiebody.fadein-filter.grows",
138
+ {
139
+ class: (cols ? "flexcol " : "flexrow wrap") + " " + (bodyClass || "")
140
+ },
141
+ ...items
142
+ )
143
+ ] : [],
144
+
145
+ ...(attrs.alwaysView
146
+ ? [attrs.alwaysView({attrs, state})]
147
+ : [])
148
+
149
+ ]
150
+ ) : [];
151
+ },
152
+ ({attrs, state}) => {
153
+ if (!attrs.cardid) {
154
+ state.collapsed = attrs.nofold
155
+ ? false
156
+ : attrs.cardid
157
+ ? !globalState[attrs.cardid]
158
+ : !!attrs.collapsed;
159
+ }
160
+ },
161
+ undefined,
162
+ undefined,
163
+ undefined,
164
+ {setCardStatus}
165
+ );
166
+
167
+ export default CardieWidget;
@@ -0,0 +1,112 @@
1
+ import m from "mithril";
2
+ import defineWidget from "../util/widgets.utils";
3
+ import {confirm} from "../util/fuckingconfirm";
4
+ import {ButtonNew} from "./Basic"
5
+ import {promptOptions} from "./OverflowUtils";
6
+
7
+ function storyOverflow(attrs) {
8
+
9
+ const
10
+ {
11
+ title,
12
+ items: maybeFunctionItems,
13
+ itemsKey,
14
+ itemsText,
15
+ field,
16
+ onchange,
17
+ textNew,
18
+ textHint,
19
+ resolve,
20
+ icon
21
+ } = attrs,
22
+ source = typeof maybeFunctionItems === "function" ? maybeFunctionItems() : maybeFunctionItems,
23
+ items = itemsKey ? typeof itemsKey === "function" ? itemsKey() : source[itemsKey] : source;
24
+
25
+ return promptOptions(items, onchange, {title, field, icon, textNew, textHint, resolve});
26
+ }
27
+
28
+ const
29
+ ComboPrompt = defineWidget(
30
+ ({attrs}) => m(
31
+ ".flexcol.combocontainer.grows", {
32
+ class: (attrs.outerClass || "") + " " + (attrs.label ? "witharia" : ""),
33
+ ...attrs.label && {"aria-label": attrs.label},
34
+ title: attrs.hint
35
+ }, [
36
+
37
+ m(
38
+ ".flexrow.combowidget.bg-inputs",
39
+ {
40
+ class: (attrs.value ? "yesvalue " : "novalue ") + (attrs.class || "")
41
+ },
42
+ [
43
+ m(ButtonNew, {
44
+ class: "transp combovalue grows flexrow " + (attrs.buttonClass || "") + (attrs.innerLabel ? " witharia" : ""),
45
+ textClass: "grows " + attrs.textClass,
46
+ iconClass: "noshrink",
47
+ ...attrs.innerLabel && {"aria-label": attrs.innerLabel},
48
+ onClick: (e) => {
49
+ if (e.shiftKey) {
50
+ attrs.onchange(undefined, e);
51
+ } else if (!attrs.locked) {
52
+ storyOverflow(attrs);
53
+ }
54
+ },
55
+ ...attrs.value
56
+ ? {
57
+ icon: attrs.value?.icon || attrs.icon,
58
+ text: attrs.field || attrs.fieldTitle
59
+ ? attrs.value[attrs.fieldTitle || attrs.field]
60
+ : attrs.itemsText
61
+ ? attrs.itemsText[attrs.value] || attrs.value
62
+ : attrs.value
63
+ }
64
+ : {
65
+ icon: attrs.emptyIcon || (attrs.emptyText ? "" : "plus"),
66
+ text: attrs.emptyText || ""
67
+ }
68
+ }),
69
+ attrs.value && !attrs.nodelete
70
+ ? [
71
+ m(
72
+ ButtonNew,
73
+ {
74
+ class: "clrcombo pad-2 pad-h transp " + (attrs.closeClass || "scaff-left"),
75
+ onClick: (e) => {
76
+
77
+ const {field} = attrs;
78
+ if (attrs.confirm && !e.shiftKey && !e.metaKey && !e.ctrlKey && !e.altKey) {
79
+ const msg = attrs.confirm();
80
+ if (msg) {
81
+ confirm(msg, () => {
82
+ attrs.onchange(field ? undefined : "", e);
83
+ });
84
+ } else {
85
+ attrs.onchange(field ? undefined : "", e);
86
+ }
87
+ } else {
88
+ attrs.onchange(field ? undefined : "", e);
89
+ }
90
+ m.redraw();
91
+ /*
92
+ if (attrs.confirm && !e.shiftKey && !e.metaKey && !e.ctrlKey && !e.altKey) {
93
+ confirm(attrs.confirm, () => {
94
+ attrs.onchange(attrs.field ? undefined : "", e);
95
+ });
96
+ } else {
97
+ attrs.onchange(attrs.field ? undefined : "", e);
98
+ }
99
+ m.redraw();
100
+ */
101
+ },
102
+ icon: attrs.removeIcon || "times",
103
+ title: attrs.removeHint || "remove this item"
104
+ }
105
+ )
106
+ ]
107
+ : []
108
+ ]
109
+ )])
110
+ );
111
+
112
+ export default ComboPrompt;