@hypericon/mico 0.3.10 → 0.3.12

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 CHANGED
@@ -15,7 +15,7 @@ $ npm i @hypericon/mico
15
15
 
16
16
  ## Usage
17
17
 
18
- **[View the usage and examples documentation here.](https://hypericon-mico.netlify.app)**
18
+ **[View usage documentation and examples here.](https://hypericon-mico.netlify.app)**
19
19
 
20
20
  ### Example
21
21
 
@@ -6,3 +6,4 @@ export * from "./markdown-editor";
6
6
  export * from "./menu";
7
7
  export * from "./pagination";
8
8
  export * from "./table";
9
+ export * from "./tree";
@@ -32,4 +32,4 @@ __exportStar(require("./pagination"), exports);
32
32
  __exportStar(require("./table"), exports);
33
33
  // export * from "./toast";
34
34
  // export * from "./tooltip";
35
- // export * from "./tree";
35
+ __exportStar(require("./tree"), exports);
@@ -37,7 +37,7 @@ exports.Pagination = {
37
37
  }, [
38
38
  pagination_settings_1.PAGINATION_DEFAULT_SETTINGS.iconComponents.first
39
39
  ? (0, mithril_1.default)(pagination_settings_1.PAGINATION_DEFAULT_SETTINGS.iconComponents.first)
40
- : (0, mithril_1.default)(icon_1.Icon, { fas: "backward-step" }),
40
+ : (0, mithril_1.default)(icon_1.Icon, { fas: "angles-left" }),
41
41
  ]) : [],
42
42
  (attrs.prev !== undefined) ? (0, mithril_1.default)("button.mico-pagination-button", {
43
43
  disabled: attrs.isAtStart,
@@ -64,7 +64,7 @@ exports.Pagination = {
64
64
  }, [
65
65
  pagination_settings_1.PAGINATION_DEFAULT_SETTINGS.iconComponents.last
66
66
  ? (0, mithril_1.default)(pagination_settings_1.PAGINATION_DEFAULT_SETTINGS.iconComponents.last)
67
- : (0, mithril_1.default)(icon_1.Icon, { fas: "forward-step" }),
67
+ : (0, mithril_1.default)(icon_1.Icon, { fas: "angles-right" }),
68
68
  ]) : [],
69
69
  (layout === "middle" || layout === "left") ? (0, mithril_1.default)("", { style: "flex: 1;" }) : [],
70
70
  ]);
@@ -0,0 +1,44 @@
1
+ import m from "mithril";
2
+ type SelectOption = m.Vnode<{
3
+ /** The "value" of the option */
4
+ value: string;
5
+ /** Optionally disable selection of the option */
6
+ disabled?: boolean;
7
+ /** The displayed value as text, to enable the user to select the value by label with the keyboard */
8
+ text?: string;
9
+ }, any>;
10
+ /**
11
+ * A dropdown select box that takes Mithril Vnodes as options
12
+ */
13
+ export declare const Select: m.Component<{
14
+ /**
15
+ * The list of vnodes to display as options. Attrs:
16
+ * - `value` (required)
17
+ * - `disabled` (optional)
18
+ * - `text` (optional)
19
+ */
20
+ options: SelectOption[];
21
+ /** The selected value to highlight */
22
+ value: string;
23
+ /** Function called with the option value when an option is selected */
24
+ oninput?: (value: string) => any;
25
+ onfocus?: (ev: FocusEvent) => any;
26
+ onblur?: (ev: FocusEvent) => any;
27
+ /** Space-separated CSS classes to apply to the component */
28
+ class?: string;
29
+ /** CSS styles to apply to the component */
30
+ style?: string;
31
+ placeholder?: string | m.Vnode<any, any>;
32
+ disabled?: boolean;
33
+ multiple?: boolean;
34
+ size?: number;
35
+ /** Optionally specify an ID in the parent, otherwise one is created internally */
36
+ id?: string;
37
+ }, {
38
+ id: string;
39
+ /** The last position of the select within the viewport */
40
+ lastViewportRect: DOMRect;
41
+ /** The current value of the disabled attr */
42
+ disabled: boolean | undefined;
43
+ }>;
44
+ export {};
@@ -0,0 +1,114 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.Select = void 0;
7
+ const mithril_1 = __importDefault(require("mithril"));
8
+ const select_options_manager_1 = require("./select-options-manager");
9
+ const optsPanelMgr = new select_options_manager_1.SelectOptionsManager();
10
+ /**
11
+ * A dropdown select box that takes Mithril Vnodes as options
12
+ */
13
+ exports.Select = {
14
+ oninit({ attrs }) {
15
+ this.id = attrs.id || `select-${Math.random().toString().replace(".", "")}`;
16
+ this.lastViewportRect = new DOMRect();
17
+ this.disabled = this.disabled;
18
+ },
19
+ onupdate({ dom }) {
20
+ var _a;
21
+ // Store the last known position of the component within the viewport
22
+ // to determine if the options should open upwards or downwards
23
+ const rect = dom.getBoundingClientRect();
24
+ rect.y += (((_a = dom.ownerDocument.defaultView) === null || _a === void 0 ? void 0 : _a.pageYOffset) || 0);
25
+ // const top = dom.getBoundingClientRect().top + (dom.ownerDocument.defaultView?.pageYOffset || 0);
26
+ this.lastViewportRect = rect;
27
+ optsPanelMgr.move(this.id, this.lastViewportRect);
28
+ },
29
+ view({ attrs }) {
30
+ var _a;
31
+ this.disabled = attrs.disabled;
32
+ const select = (value) => {
33
+ var _a;
34
+ if (this.disabled)
35
+ return;
36
+ (_a = attrs.oninput) === null || _a === void 0 ? void 0 : _a.call(attrs, value);
37
+ // this.isOpen = false;
38
+ // attrs.onclose?.();
39
+ optsPanelMgr.dismiss();
40
+ };
41
+ const open = (options, value) => {
42
+ if (this.disabled)
43
+ return;
44
+ optsPanelMgr.open({
45
+ id: this.id,
46
+ options: options,
47
+ selectRect: this.lastViewportRect,
48
+ selectedIndex: options.findIndex(o => o.attrs.value === (value || attrs.value)),
49
+ select: (value) => {
50
+ select(value);
51
+ },
52
+ onDismiss: () => {
53
+ var _a;
54
+ (_a = document.getElementById(this.id)) === null || _a === void 0 ? void 0 : _a.focus();
55
+ },
56
+ });
57
+ };
58
+ const dismiss = () => {
59
+ // this.isOpen = false;
60
+ optsPanelMgr.dismiss();
61
+ // attrs.onclose?.();
62
+ };
63
+ // The main "select box" div
64
+ return (0, mithril_1.default)(".input.no-select", {
65
+ id: this.id,
66
+ tabindex: 0,
67
+ style: `height: 2rem; ${attrs.style || ""} padding-top: 0; padding-bottom: 0; position: relative;`,
68
+ class: `${attrs.class || ""} flex items-center ${attrs.disabled ? "disabled" : ""} ${attrs.disabled ? "cursor-auto" : "cursor-pointer"}`,
69
+ onclick: (ev) => {
70
+ // ev.stopPropagation();
71
+ if (!optsPanelMgr.isOpen(this.id))
72
+ open(attrs.options, attrs.value);
73
+ // ? this.dismiss()
74
+ // :
75
+ },
76
+ onkeydown: (ev) => {
77
+ if (ev.code === "Enter") {
78
+ open(attrs.options, attrs.value);
79
+ }
80
+ },
81
+ onfocus: (ev) => { var _a; return (_a = attrs.onfocus) === null || _a === void 0 ? void 0 : _a.call(attrs, ev); },
82
+ onblur: (ev) => {
83
+ var _a;
84
+ if (!optsPanelMgr.isOpen(this.id)) {
85
+ (_a = attrs.onblur) === null || _a === void 0 ? void 0 : _a.call(attrs, ev);
86
+ }
87
+ }
88
+ }, [
89
+ // The selected option within the select box div
90
+ (0, mithril_1.default)(".flex-1.overflow-x-hidden", {}, [
91
+ (0, mithril_1.default)(".overflow-x-hidden", {
92
+ key: attrs.value,
93
+ }, [
94
+ (_a = attrs.options.find(o => o.attrs.value === attrs.value)) !== null && _a !== void 0 ? _a : ((attrs.placeholder && typeof attrs.placeholder === "string")
95
+ ? (0, mithril_1.default)("span.hint", attrs.placeholder)
96
+ : attrs.placeholder),
97
+ ]),
98
+ ]),
99
+ // Open/close down/up arrow in the input box
100
+ (0, mithril_1.default)(".ml-1", {
101
+ class: this.disabled ? "text-gray-500" : "",
102
+ }, [
103
+ // optsPanelSvc.isOpen(this.id)
104
+ // ? m(".h-full.flex.items-center", { key: "up" }, m("i.fas.fa-caret-up"))
105
+ // : m(".h-full.flex.items-center", { key: "down" }, m("i.fas.fa-caret-down")),
106
+ (0, mithril_1.default)(".h-full.flex.items-center", [
107
+ optsPanelMgr.isOpen(this.id)
108
+ ? (0, mithril_1.default)(`i.fas.fa-caret-up`)
109
+ : (0, mithril_1.default)(`i.fas.fa-caret-down`),
110
+ ]),
111
+ ]),
112
+ ]);
113
+ },
114
+ };
@@ -0,0 +1,21 @@
1
+ /// <reference types="node" />
2
+ import m from "mithril";
3
+ import { Subscription } from "rxjs";
4
+ export declare const SelectOptionsPanel: m.Component<{}, {
5
+ subs: Subscription;
6
+ isOpen: boolean;
7
+ /** If the options panel should open upwards */
8
+ isReversed: boolean;
9
+ /** If the select input is narrow and near the right viewport edge, anchor the options panel on the right edge */
10
+ isAnchoredRight: boolean;
11
+ highlightedIndex: number;
12
+ ensureVisible: (index: number, up: boolean) => void;
13
+ /** A current string "searched for" when the user types characters when the panel is open */
14
+ textSearch: string;
15
+ /** A timer used to clear the text search */
16
+ keyIdleTimer: NodeJS.Timeout | undefined;
17
+ /** On pressing a letter key, update the text search and possibly select a new option */
18
+ handleTextSearch: (ev: KeyboardEvent) => void;
19
+ /** Clear the text search string and idle timer */
20
+ clearTextSearch: () => void;
21
+ }>;
@@ -0,0 +1,174 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.SelectOptionsPanel = void 0;
7
+ const mithril_1 = __importDefault(require("mithril"));
8
+ const rxjs_1 = require("rxjs");
9
+ const constants_1 = require("./constants");
10
+ const select_options_manager_1 = require("./select-options-manager");
11
+ // const logger = getLogger("SelectOptionsPanel");
12
+ const DEBUG = false;
13
+ const optsPanelMgr = new select_options_manager_1.SelectOptionsManager();
14
+ const MIN_OPTIONS_PANEL_WIDTH = 200;
15
+ const OPTIONS_PANEL_ID = "select-options-panel";
16
+ const TEXT_IDLE_TIMEOUT = 1000;
17
+ exports.SelectOptionsPanel = {
18
+ oninit() {
19
+ this.subs = new rxjs_1.Subscription();
20
+ this.isOpen = false;
21
+ this.isReversed = false;
22
+ this.isAnchoredRight = false;
23
+ this.highlightedIndex = 0;
24
+ // this.highlightIndex = (index) => {
25
+ // this.highlightedIndex = index;
26
+ // this.ensureVisible(index, false);
27
+ // };
28
+ this.ensureVisible = (index, up) => {
29
+ const panel = document.getElementById(OPTIONS_PANEL_ID);
30
+ const container = panel === null || panel === void 0 ? void 0 : panel.children.item(0);
31
+ const optionElems = container === null || container === void 0 ? void 0 : container.children;
32
+ const highlightedElem = optionElems === null || optionElems === void 0 ? void 0 : optionElems.item(index);
33
+ if (up) {
34
+ if (panel.getBoundingClientRect().top > highlightedElem.getBoundingClientRect().top) {
35
+ highlightedElem === null || highlightedElem === void 0 ? void 0 : highlightedElem.scrollIntoView(true);
36
+ }
37
+ }
38
+ else {
39
+ if (panel.getBoundingClientRect().bottom < highlightedElem.getBoundingClientRect().bottom) {
40
+ highlightedElem === null || highlightedElem === void 0 ? void 0 : highlightedElem.scrollIntoView(false);
41
+ }
42
+ }
43
+ // const highlightedRect = highlightedElem?.getBoundingClientRect();
44
+ // if (highlightedRect) {
45
+ // logger.debug("ensure visible rect:", highlightedRect);
46
+ // panel?.scr
47
+ // }
48
+ };
49
+ this.subs.add(optsPanelMgr.panel$.subscribe(panelDef => {
50
+ if (panelDef) {
51
+ // If the available space is less than the approximate size of the options panel, open upwards instead
52
+ this.isReversed = (window.innerHeight - panelDef.selectRect.bottom) < Math.min((panelDef.options.length * constants_1.OPTION_APPROX_HEIGHT), constants_1.MAX_OPTION_PANEL_HEIGHT);
53
+ // TODO: calculate is anchored right
54
+ if (!this.isOpen) {
55
+ DEBUG && console.log("opening select options panel");
56
+ const index = panelDef.selectedIndex;
57
+ this.highlightedIndex = (index !== undefined && index !== -1) ? index : 0;
58
+ this.isOpen = true;
59
+ setTimeout(() => {
60
+ this.ensureVisible(this.highlightedIndex, false);
61
+ }, 100);
62
+ }
63
+ }
64
+ else {
65
+ this.isOpen = false;
66
+ }
67
+ mithril_1.default.redraw();
68
+ }));
69
+ this.textSearch = "";
70
+ this.handleTextSearch = (ev) => {
71
+ if (optsPanelMgr.panel === undefined)
72
+ return;
73
+ const key = ev.key.toLowerCase();
74
+ this.textSearch += key;
75
+ // If the idle timeout elapses, clear the current text search
76
+ if (this.keyIdleTimer) {
77
+ clearTimeout(this.keyIdleTimer);
78
+ }
79
+ this.keyIdleTimer = setTimeout(() => {
80
+ this.clearTextSearch();
81
+ }, TEXT_IDLE_TIMEOUT);
82
+ // Find the index of the option whose text search string starts with the current search term
83
+ const index = optsPanelMgr.panel.options.findIndex(o => {
84
+ return o.attrs.text && o.attrs.text.toLowerCase().startsWith(this.textSearch);
85
+ });
86
+ if (index >= 0 && index <= optsPanelMgr.panel.options.length - 1) {
87
+ const up = this.highlightedIndex > index;
88
+ this.highlightedIndex = index;
89
+ this.ensureVisible(this.highlightedIndex, up);
90
+ }
91
+ };
92
+ this.clearTextSearch = () => {
93
+ if (this.keyIdleTimer) {
94
+ clearTimeout(this.keyIdleTimer);
95
+ }
96
+ this.keyIdleTimer = undefined;
97
+ this.textSearch = "";
98
+ };
99
+ },
100
+ onremove() {
101
+ this.subs.unsubscribe();
102
+ },
103
+ onupdate() {
104
+ var _a;
105
+ (_a = document.getElementById(OPTIONS_PANEL_ID)) === null || _a === void 0 ? void 0 : _a.focus();
106
+ },
107
+ view() {
108
+ if (optsPanelMgr.panel === undefined || !this.isOpen)
109
+ return;
110
+ const panel = optsPanelMgr.panel;
111
+ const rect = panel.selectRect;
112
+ const width = Math.max(rect.width, MIN_OPTIONS_PANEL_WIDTH);
113
+ DEBUG && console.log("Drawing select options panel for select rect:", rect);
114
+ return (0, mithril_1.default)(".bg-white.text-dark.dark:bg-black.dark:text-white.shadow-lg.border.border-grey-500", {
115
+ id: OPTIONS_PANEL_ID,
116
+ tabindex: -1,
117
+ style: `position: fixed; left: ${rect.left}px; width: ${width}px; ${this.isReversed ? `bottom: ${document.documentElement.clientHeight - rect.top}px;` : `top: ${rect.bottom}px;`}
118
+ min-width: ${MIN_OPTIONS_PANEL_WIDTH}px; max-height: ${constants_1.MAX_OPTION_PANEL_HEIGHT}px; overflow-y: auto; z-index: 100; outline: none;`,
119
+ onkeydown: (ev) => {
120
+ if (ev.code === "ArrowUp") {
121
+ this.highlightedIndex = Math.max(this.highlightedIndex - 1, 0);
122
+ this.clearTextSearch();
123
+ this.ensureVisible(this.highlightedIndex, true);
124
+ ev.preventDefault(); // Navigating items in the list scrolls the page
125
+ }
126
+ else if (ev.code === "ArrowDown") {
127
+ this.highlightedIndex = Math.min(this.highlightedIndex + 1, panel.options.length - 1);
128
+ this.clearTextSearch();
129
+ this.ensureVisible(this.highlightedIndex, false);
130
+ ev.preventDefault(); // Navigating items in the list scrolls the page
131
+ }
132
+ else if (ev.code === "Enter") {
133
+ optsPanelMgr.selectIndex(this.highlightedIndex);
134
+ }
135
+ else if (ev.code === "Escape") {
136
+ optsPanelMgr.dismiss();
137
+ ev.stopPropagation();
138
+ }
139
+ else if (/^[a-z]$/i.test(ev.key)) { // a-z
140
+ this.handleTextSearch(ev);
141
+ }
142
+ },
143
+ onfocusout: (ev) => {
144
+ optsPanelMgr.dismiss();
145
+ },
146
+ }, [
147
+ // The container of the option buttons, may be larger than the displayed panel
148
+ (0, mithril_1.default)("", {
149
+ style: ``,
150
+ class: `flex flex-col items-stretch`,
151
+ }, [
152
+ ...optsPanelMgr.panel.options.map((optionVnode, optionIndex) => {
153
+ const value = optionVnode.attrs.value;
154
+ const disabled = optionVnode.attrs.disabled === true;
155
+ // An option value button
156
+ return (0, mithril_1.default)(`button`, {
157
+ key: value,
158
+ disabled,
159
+ class: `px-2 py-1 flex items-start ${this.highlightedIndex === optionIndex ? "bg-gray-200 dark:bg-black-light" : ""}`,
160
+ onclick: (ev) => {
161
+ ev.stopPropagation();
162
+ if (disabled)
163
+ return;
164
+ // this.select(value);
165
+ optsPanelMgr.selectIndex(optionIndex);
166
+ },
167
+ onmousedown: (ev) => ev.preventDefault(),
168
+ onmouseover: (ev) => this.highlightedIndex = optionIndex,
169
+ }, optionVnode);
170
+ }),
171
+ ]),
172
+ ]);
173
+ }
174
+ };
@@ -0,0 +1,2 @@
1
+ export declare const MAX_OPTION_PANEL_HEIGHT: 320;
2
+ export declare const OPTION_APPROX_HEIGHT: 30;
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.OPTION_APPROX_HEIGHT = exports.MAX_OPTION_PANEL_HEIGHT = void 0;
4
+ exports.MAX_OPTION_PANEL_HEIGHT = 320;
5
+ exports.OPTION_APPROX_HEIGHT = 30;
@@ -0,0 +1,2 @@
1
+ export * from "./Select";
2
+ export * from "./SelectOptionsPanel";
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./Select"), exports);
18
+ __exportStar(require("./SelectOptionsPanel"), exports);
19
+ // export * from "./select-options-panel-manager";
@@ -0,0 +1,39 @@
1
+ import m from "mithril";
2
+ export type SelectOptionsPanelDef = {
3
+ /** */
4
+ id: string;
5
+ /** The bounding client rect of the select input element within the viewport */
6
+ selectRect: DOMRect;
7
+ /** The list of vnodes to display as options */
8
+ options: m.Vnode<{
9
+ /** The "value" of the option */
10
+ value: string;
11
+ /** Optionally disable selection of the option */
12
+ disabled?: boolean;
13
+ /** The displayed value as text, to enable the user to select the value by label with the keyboard */
14
+ text?: string;
15
+ }, any>[];
16
+ /** */
17
+ selectedIndex: number;
18
+ /** Function to notify the select input of the slected value in the options panel */
19
+ select: (value: string) => void;
20
+ /** Function called after the options panel has been dismissed */
21
+ onDismiss?: () => void;
22
+ };
23
+ /**
24
+ * Manager providing the link between custom select box input components
25
+ * and the layout-level select options panel
26
+ */
27
+ export declare class SelectOptionsManager {
28
+ private _panel;
29
+ panel$: import("rxjs").Observable<SelectOptionsPanelDef | undefined>;
30
+ get panel(): SelectOptionsPanelDef | undefined;
31
+ private lastPanel;
32
+ constructor();
33
+ open(optionsPanel: SelectOptionsPanelDef): void;
34
+ move(id: string, selectRect: DOMRect): void;
35
+ isOpen(selectId: string): boolean;
36
+ selectIndex(index: number): void;
37
+ selectValue(value: any): void;
38
+ dismiss(): void;
39
+ }
@@ -0,0 +1,67 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SelectOptionsManager = void 0;
4
+ const rxjs_1 = require("rxjs");
5
+ const REOPEN_TIMEOUT = 100;
6
+ /**
7
+ * Manager providing the link between custom select box input components
8
+ * and the layout-level select options panel
9
+ */
10
+ class SelectOptionsManager {
11
+ get panel() { return this._panel.value; }
12
+ constructor() {
13
+ this._panel = new rxjs_1.BehaviorSubject(undefined);
14
+ this.panel$ = this._panel.asObservable();
15
+ this.lastPanel = {
16
+ id: "",
17
+ timestamp: 0,
18
+ };
19
+ }
20
+ open(optionsPanel) {
21
+ // Don't reopen the same panel within the reopen timeout
22
+ if (this.lastPanel.id === optionsPanel.id) {
23
+ if (this.lastPanel.timestamp + REOPEN_TIMEOUT > Date.now())
24
+ return;
25
+ }
26
+ this._panel.next(optionsPanel);
27
+ }
28
+ move(id, selectRect) {
29
+ var _a;
30
+ if (id && this.panel && id === ((_a = this.panel) === null || _a === void 0 ? void 0 : _a.id)) {
31
+ const panel = this.panel;
32
+ if (panel.selectRect.top !== selectRect.top || panel.selectRect.left !== selectRect.left) {
33
+ panel.selectRect = selectRect;
34
+ this._panel.next(panel);
35
+ }
36
+ }
37
+ }
38
+ isOpen(selectId) {
39
+ var _a;
40
+ return ((_a = this.panel) === null || _a === void 0 ? void 0 : _a.id) === selectId;
41
+ }
42
+ selectIndex(index) {
43
+ if (this.panel) {
44
+ if (this.panel.options[index].attrs.disabled) {
45
+ // Noop
46
+ }
47
+ else {
48
+ this.panel.select(this.panel.options[index].attrs.value);
49
+ }
50
+ this.dismiss();
51
+ }
52
+ }
53
+ selectValue(value) {
54
+ if (this.panel) {
55
+ this.panel.select(value);
56
+ this.dismiss();
57
+ }
58
+ }
59
+ dismiss() {
60
+ var _a, _b, _c, _d;
61
+ (_b = (_a = this.panel) === null || _a === void 0 ? void 0 : _a.onDismiss) === null || _b === void 0 ? void 0 : _b.call(_a);
62
+ this.lastPanel.id = (_d = (_c = this.panel) === null || _c === void 0 ? void 0 : _c.id) !== null && _d !== void 0 ? _d : "";
63
+ this.lastPanel.timestamp = Date.now();
64
+ this._panel.next(undefined);
65
+ }
66
+ }
67
+ exports.SelectOptionsManager = SelectOptionsManager;
File without changes
@@ -0,0 +1,204 @@
1
+ "use strict";
2
+ // import { container } from "tsyringe";
3
+ // import { getLogger } from "~common/services/get-logger";
4
+ // import { Logger, LogLevel } from "~common/services/logger.service";
5
+ // import { MobileModeService } from "~common/services/mobile-mode.service";
6
+ // const mobileMode = container.resolve(MobileModeService);
7
+ // export interface TooltipOpts {
8
+ // /** Specify the position of the tooltip */
9
+ // pos: TooltipPos,
10
+ // /** Override the default delay in milliseconds */
11
+ // delay: number,
12
+ // /** Increase the maximum width and height of the tooltip */
13
+ // jumbo: boolean,
14
+ // /** The tooltip should be attached to the document instead of the moused-over element */
15
+ // attachToBody: boolean,
16
+ // }
17
+ // export type TooltipPos = "cursor" | "top" | "bottom" | "left" | "right" | "follow";
18
+ // const DEFAULT_POS: TooltipPos = "cursor";
19
+ // const DEFAULT_DELAY = 500;
20
+ // const TOOLTIP_CLASS = "hypertable-tooltip";
21
+ // const MAX_WIDTH_PX = 250;
22
+ // const MAX_HEIGHT_PX = 100;
23
+ // const MAX_WIDTH_JUMBO_PX = 400;
24
+ // const MAX_HEIGHT_JUMBO_PX = 300;
25
+ // const logger = getLogger("Tooltip");
26
+ // // logger.logLevel = LogLevel.Debug;
27
+ // /**
28
+ // * Display a tooltip using the given mouse event.
29
+ // * Call this function from an element's "mouseenter" event.
30
+ // *
31
+ // * e.g.:
32
+ // * ```
33
+ // * onmouseenter: (ev: MouseEvent) => tooltip(ev, "Howdy!"),
34
+ // * ```
35
+ // * @param ev
36
+ // * @param tooltip Nothing is displayed if the tooltip text is empty
37
+ // * @param opts
38
+ // * @returns
39
+ // */
40
+ // export function tooltip(ev: MouseEvent, tooltip: string, opts?: Partial<TooltipOpts>) {
41
+ // // Tooltips don't work properly in mobile mode
42
+ // if (mobileMode.isMob) return;
43
+ // // If there is no tooltip, don't bother
44
+ // if (!tooltip) return;
45
+ // const target = ev.currentTarget as HTMLElement | null;
46
+ // const eventTarget = ev.target as HTMLElement | null;
47
+ // logger.debug(`target is ${target?.tagName}`);
48
+ // logger.debug(`event target is ${eventTarget?.tagName}`);
49
+ // if (!target) {
50
+ // return logger.debug("No target; returning from tooltip()");
51
+ // }
52
+ // // if (target?.classList.contains(TOOLTIP_CLASS) || eventTarget?.classList.contains(TOOLTIP_CLASS)) {
53
+ // // return logger.debug("Target is tooltip div; returning from tooltip()");
54
+ // // }
55
+ // // This helps avoid re-adding tooltips when mousing over the tooltip itself
56
+ // let hasTooltip = false;
57
+ // target.childNodes.forEach((child, key, parent) => {
58
+ // if ((child as HTMLElement)?.classList?.contains(TOOLTIP_CLASS)) {
59
+ // hasTooltip = true;
60
+ // }
61
+ // });
62
+ // if (hasTooltip) {
63
+ // return logger.debug("Target already has tooltip; returning from tooltip()");
64
+ // }
65
+ // const mousePos = { x: ev.x, y: ev.y };
66
+ // const latestPosFn = (ev: MouseEvent) => {
67
+ // mousePos.x = ev.x;
68
+ // mousePos.y = ev.y;
69
+ // };
70
+ // target.addEventListener("mousemove", latestPosFn);
71
+ // const timeout = setTimeout(() => {
72
+ // displayTooltip({ ev, target, tooltip, opts, mousePos });
73
+ // }, opts?.delay ?? DEFAULT_DELAY);
74
+ // const clearFn = (ev: MouseEvent) => {
75
+ // logger.debug("Clearing tooltip timeout");
76
+ // clearTimeout(timeout);
77
+ // target.removeEventListener("mouseleave", clearFn);
78
+ // target.removeEventListener("mousemove", latestPosFn);
79
+ // };
80
+ // target.addEventListener("mouseleave", clearFn);
81
+ // }
82
+ // /**
83
+ // *
84
+ // * @param value
85
+ // * @param opts
86
+ // * @returns
87
+ // */
88
+ // export function tooltipSpread(value: string, opts?: Partial<TooltipOpts>) {
89
+ // return {
90
+ // onmouseenter: (ev: MouseEvent) => tooltip(ev, value, opts),
91
+ // };
92
+ // }
93
+ // /**
94
+ // * TODO: consider adding arrows to top/bottom/left/right positioned tooltips
95
+ // * e.g.: https://getbootstrap.com/docs/3.4/javascript/#tooltips
96
+ // */
97
+ // function displayTooltip(settings: {
98
+ // ev: MouseEvent,
99
+ // target: HTMLElement,
100
+ // tooltip: string,
101
+ // mousePos: { x: number, y: number, }
102
+ // opts?: Partial<TooltipOpts>,
103
+ // }) {
104
+ // const { ev, target, tooltip, opts, mousePos } = settings;
105
+ // const cursorOffsetX = 5;
106
+ // const cursorOffsetY = 20;
107
+ // const pos = opts?.pos ?? DEFAULT_POS;
108
+ // let targetRect = target.getBoundingClientRect();
109
+ // // targetRect = new DOMRect(targetRect.x, targetRect.y, targetRect.width, targetRect.height);
110
+ // let targetCenter = {
111
+ // x: targetRect.left + (targetRect.width / 2),
112
+ // y: targetRect.top + (targetRect.height / 2),
113
+ // };
114
+ // const maxWidth = opts?.jumbo === true ? MAX_WIDTH_JUMBO_PX : MAX_WIDTH_PX;
115
+ // const maxHeight = opts?.jumbo === true ? MAX_HEIGHT_JUMBO_PX : MAX_HEIGHT_PX;
116
+ // logger.debug(`display tooltip "${tooltip}" on element ${target.tagName}`);
117
+ // const tooltipEl = document.createElement("div");
118
+ // tooltipEl.innerText = tooltip;
119
+ // tooltipEl.classList.value = "px-2 py-1 text-xs text-dark font-normal rounded border border-primary bg-white fade-in-100 no-select";
120
+ // tooltipEl.classList.add(TOOLTIP_CLASS);
121
+ // tooltipEl.style.position = "fixed";
122
+ // tooltipEl.style.maxWidth = `${maxWidth}px`;
123
+ // tooltipEl.style.maxHeight = `${maxHeight}px`;
124
+ // tooltipEl.style.zIndex = "1000";
125
+ // tooltipEl.style.whiteSpace = "normal";
126
+ // tooltipEl.style.opacity = "1";
127
+ // tooltipEl.style.textAlign = "left";
128
+ // tooltipEl.style.overflow = "hidden";
129
+ // tooltipEl.setAttribute("role", "tooltip");
130
+ // // // Add the tooltip before positioning it to read its dimensions
131
+ // // console.log(`tooltipEl width: ${tooltipEl.clientWidth}`);
132
+ // // console.log(`tooltipEl height: ${tooltipEl.clientHeight}`);
133
+ // if (settings.opts?.attachToBody === true) {
134
+ // document.body.append(tooltipEl);
135
+ // } else {
136
+ // target.append(tooltipEl);
137
+ // }
138
+ // // console.log(`tooltipEl width: ${tooltipEl.clientWidth}`);
139
+ // // console.log(`tooltipEl height: ${tooltipEl.clientHeight}`);
140
+ // if (pos === "cursor" || pos === "follow") {
141
+ // tooltipEl.style.top = `${mousePos.y + cursorOffsetY}px`;
142
+ // if (mousePos.x < window.innerWidth - maxWidth) {
143
+ // tooltipEl.style.left = `${mousePos.x + cursorOffsetX}px`;
144
+ // } else {
145
+ // tooltipEl.style.right = `${document.documentElement.clientWidth - mousePos.x - cursorOffsetX}px`;
146
+ // }
147
+ // } else if (pos === "top") {
148
+ // tooltipEl.style.bottom = `${window.innerHeight - targetRect.top}px`; // - window.scrollY
149
+ // tooltipEl.style.left = `${targetCenter.x - tooltipEl.clientWidth / 2}px`;
150
+ // } else if (pos === "bottom") {
151
+ // tooltipEl.style.top = `${targetRect.bottom}px`; // + window.scrollY
152
+ // tooltipEl.style.left = `${targetCenter.x - tooltipEl.clientWidth / 2}px`;
153
+ // } else if (pos === "left") {
154
+ // tooltipEl.style.right = `${document.documentElement.clientWidth - targetRect.left}px`;
155
+ // tooltipEl.style.top = `${targetCenter.y - tooltipEl.clientHeight / 2}px`;
156
+ // } else if (pos === "right") {
157
+ // tooltipEl.style.left = `${targetRect.right}px`;
158
+ // tooltipEl.style.top = `${targetCenter.y - tooltipEl.clientHeight / 2}px`;
159
+ // }
160
+ // if (pos === "follow") {
161
+ // const followFn = (ev: MouseEvent) => {
162
+ // tooltipEl.style.top = `${ev.y + cursorOffsetY}px`; // + window.scrollY
163
+ // if (mousePos.x < window.innerWidth - maxWidth) {
164
+ // tooltipEl.style.left = `${ev.x + cursorOffsetX}px`;
165
+ // tooltipEl.style.right = "";
166
+ // } else {
167
+ // tooltipEl.style.right = `${document.documentElement.clientWidth - ev.x - cursorOffsetX}px`;
168
+ // tooltipEl.style.left = "";
169
+ // }
170
+ // };
171
+ // const cleanupFn = () => {
172
+ // target.removeEventListener("mousemove", followFn);
173
+ // target.removeEventListener("mouseleave", cleanupFn);
174
+ // }
175
+ // target.addEventListener("mousemove", followFn);
176
+ // target.addEventListener("mouseleave", cleanupFn);
177
+ // }
178
+ // logger.debug(`displaying tooltip at: ${tooltipEl.style.left}, ${tooltipEl.style.top}`);
179
+ // const removeFn = () => {
180
+ // logger.debug(`Removing tooltip ${tooltip} from el ${target.tagName}`);
181
+ // tooltipEl.onanimationend = (ev: AnimationEvent) => {
182
+ // logger.debug(ev.animationName);
183
+ // if (settings.opts?.attachToBody === true) {
184
+ // document.body.removeChild(tooltipEl);
185
+ // } else {
186
+ // target.removeChild(tooltipEl);
187
+ // }
188
+ // }
189
+ // tooltipEl.classList.replace("fade-in-100", "fade-out-100");
190
+ // target.removeEventListener("mouseleave", removeFn);
191
+ // tooltipEl.removeEventListener("mouseenter", removeFn);
192
+ // }
193
+ // target.addEventListener("mouseleave", removeFn);
194
+ // tooltipEl.addEventListener("mouseenter", removeFn);
195
+ // if (settings.opts?.attachToBody === true) {
196
+ // const bodyListener = (ev: MouseEvent) => {
197
+ // if (ev.target && !target.contains(ev.target as any)) {
198
+ // removeFn();
199
+ // document.body.removeEventListener("mousemove", bodyListener);
200
+ // }
201
+ // };
202
+ // document.body.addEventListener("mousemove", bodyListener);
203
+ // }
204
+ // }
@@ -0,0 +1,76 @@
1
+ import m from "mithril";
2
+ import { TreeNode } from "./tree-node.interface";
3
+ /**
4
+ * An untyped tree component (see `createTree<T>()`)
5
+ */
6
+ export declare const Tree: m.Component<{
7
+ /** The root list of nodes to display in the tree (circular references will crash) */
8
+ nodes: TreeNode<any>[];
9
+ /** Enable drag and drop reordering */
10
+ enableReordering?: boolean | undefined;
11
+ /** Enable expanding/collapsing of nodes with children */
12
+ enableToggling?: boolean | undefined;
13
+ /** Enable debug logging of certain events */
14
+ enableLogging?: boolean | undefined;
15
+ /** Function called when a node with children is toggled */
16
+ ontoggle?: ((node: TreeNode<any>, expanded: boolean, recursive: boolean) => void) | undefined;
17
+ /** Function called when a node label is clicked */
18
+ onclick?: ((node: TreeNode<any>) => void) | undefined;
19
+ /** Function called when a node has been dragged to a new position in the tree */
20
+ onreorder?: ((moved: any, target: any, above: boolean) => void) | undefined;
21
+ /** Optional message to display in defined but empty children lists */
22
+ emptyChildrenMessage?: string | undefined;
23
+ /** Optionally override the default icon for a collapsed folder */
24
+ collapsedIcon?: m.Vnode<any, {}> | undefined;
25
+ /** Optionally override the default icon for an expanded folder */
26
+ expandedIcon?: m.Vnode<any, {}> | undefined;
27
+ /** Optionally enable toggling of expansion nodes by clicking their labels */
28
+ toggleExpansionWithLabel?: boolean | undefined;
29
+ /** Optional CSS classes to apply to the root div */
30
+ class?: string | undefined;
31
+ /** Optional CSS styles to apply to the root div */
32
+ style?: string | undefined;
33
+ }, {
34
+ isReordering: boolean;
35
+ thisEl: Element;
36
+ targetRect: DOMRect | undefined;
37
+ targettingTop: boolean;
38
+ }>;
39
+ /**
40
+ * Build a component to display nested nodes in a tree.
41
+ *
42
+ * Nodes with children are collapsable, and nodes can be arbitrarily reordered.
43
+ */
44
+ export declare function createTree<TNodeData extends object = {}>(): m.Component<{
45
+ /** The root list of nodes to display in the tree (circular references will crash) */
46
+ nodes: TreeNode<TNodeData>[];
47
+ /** Enable drag and drop reordering */
48
+ enableReordering?: boolean | undefined;
49
+ /** Enable expanding/collapsing of nodes with children */
50
+ enableToggling?: boolean | undefined;
51
+ /** Enable debug logging of certain events */
52
+ enableLogging?: boolean | undefined;
53
+ /** Function called when a node with children is toggled */
54
+ ontoggle?: ((node: TreeNode<TNodeData>, expanded: boolean, recursive: boolean) => void) | undefined;
55
+ /** Function called when a node label is clicked */
56
+ onclick?: ((node: TreeNode<TNodeData>) => void) | undefined;
57
+ /** Function called when a node has been dragged to a new position in the tree */
58
+ onreorder?: ((moved: TNodeData, target: TNodeData, above: boolean) => void) | undefined;
59
+ /** Optional message to display in defined but empty children lists */
60
+ emptyChildrenMessage?: string | undefined;
61
+ /** Optionally override the default icon for a collapsed folder */
62
+ collapsedIcon?: m.Vnode<any, {}> | undefined;
63
+ /** Optionally override the default icon for an expanded folder */
64
+ expandedIcon?: m.Vnode<any, {}> | undefined;
65
+ /** Optionally enable toggling of expansion nodes by clicking their labels */
66
+ toggleExpansionWithLabel?: boolean | undefined;
67
+ /** Optional CSS classes to apply to the root div */
68
+ class?: string | undefined;
69
+ /** Optional CSS styles to apply to the root div */
70
+ style?: string | undefined;
71
+ }, {
72
+ isReordering: boolean;
73
+ thisEl: Element;
74
+ targetRect: DOMRect | undefined;
75
+ targettingTop: boolean;
76
+ }>;
@@ -0,0 +1,210 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.createTree = exports.Tree = void 0;
7
+ const utils_1 = require("@hypericon/utils");
8
+ const mithril_1 = __importDefault(require("mithril"));
9
+ const icon_1 = require("../icon");
10
+ const DATA_TRANSFER_TYPE = "application/tree-data";
11
+ /**
12
+ * An untyped tree component (see `createTree<T>()`)
13
+ */
14
+ exports.Tree = createTree();
15
+ /**
16
+ * Build a component to display nested nodes in a tree.
17
+ *
18
+ * Nodes with children are collapsable, and nodes can be arbitrarily reordered.
19
+ */
20
+ function createTree() {
21
+ // /** Reference to the TreeItem currently being dragged */
22
+ // let draggedTreeItem: TreeItem<T> | undefined = undefined;
23
+ const Tree = {
24
+ oninit() {
25
+ this.isReordering = false;
26
+ },
27
+ oncreate({ dom }) {
28
+ this.thisEl = dom;
29
+ },
30
+ view({ attrs }) {
31
+ var _a, _b;
32
+ const nodes = attrs.nodes;
33
+ return (0, mithril_1.default)(".mico-tree", {
34
+ style: `${(_a = attrs.style) !== null && _a !== void 0 ? _a : ""} position: relative;`,
35
+ class: `${(_b = attrs.class) !== null && _b !== void 0 ? _b : ""}`
36
+ }, [
37
+ (0, mithril_1.default)("ul", nodes.map((node, i) => {
38
+ return (0, mithril_1.default)(TreeNode, {
39
+ // key: i, // item.key,
40
+ node,
41
+ enableReordering: attrs.enableReordering,
42
+ enableToggling: attrs.enableToggling,
43
+ enableLogging: attrs.enableLogging,
44
+ ontoggle: attrs.ontoggle,
45
+ onclick: attrs.onclick,
46
+ reorder: attrs.onreorder,
47
+ emptyChildrenMessage: attrs.emptyChildrenMessage,
48
+ collapsedIcon: attrs.collapsedIcon,
49
+ expandedIcon: attrs.expandedIcon,
50
+ toggleExpansionWithLabel: attrs.toggleExpansionWithLabel,
51
+ });
52
+ })),
53
+ ]);
54
+ }
55
+ };
56
+ const TreeNode = {
57
+ oninit({ attrs }) {
58
+ const { enableReordering } = attrs;
59
+ this.isReorderingOver = false;
60
+ this.handleClick = (ev, node) => {
61
+ var _a;
62
+ (_a = attrs.onclick) === null || _a === void 0 ? void 0 : _a.call(attrs, node);
63
+ };
64
+ this.handleDragStart = (ev, node) => {
65
+ if (!enableReordering)
66
+ return;
67
+ if (!ev.dataTransfer)
68
+ return;
69
+ attrs.enableLogging && console.log(`start dragging`, node.data);
70
+ ev.dataTransfer.setData(DATA_TRANSFER_TYPE, JSON.stringify(node.data));
71
+ ev.dataTransfer.effectAllowed = "move";
72
+ // attrs.reorderstart?.();
73
+ };
74
+ this.handleDragOver = (ev, node) => {
75
+ ev.preventDefault();
76
+ if (!enableReordering)
77
+ return;
78
+ // attrs.enableLogging && console.log(`on dragover`, ev.dataTransfer?.getData(DATA_TRANSFER_TYPE), "over", item.data);
79
+ // ev.target;
80
+ // ev.x;
81
+ const overDiv = ev.currentTarget;
82
+ const targetRect = overDiv.getBoundingClientRect();
83
+ const topToMouse = ev.clientY - targetRect.top;
84
+ const isAbove = topToMouse < (targetRect.height - topToMouse);
85
+ this.isTargettingAbove = isAbove;
86
+ this.isReorderingOver = true;
87
+ // attrs.reorderover?.(overDiv, ev.clientY);
88
+ };
89
+ this.handleDrag = (ev, node) => {
90
+ // attrs.enableLogging && console.log(`on drag`, item.data);
91
+ };
92
+ this.handleDragLeave = (ev, node) => {
93
+ this.isReorderingOver = false;
94
+ };
95
+ this.handleDrop = (ev, node) => {
96
+ var _a;
97
+ ev.preventDefault();
98
+ if (!enableReordering)
99
+ return;
100
+ if (ev.dataTransfer) {
101
+ ev.dataTransfer.dropEffect = "move";
102
+ const movedNodeData = JSON.parse(ev.dataTransfer.getData(DATA_TRANSFER_TYPE));
103
+ attrs.enableLogging && console.log(`on drop`, movedNodeData, "on", node.data);
104
+ // attrs.reorderfinish?.();
105
+ const droppedOnSelf = (0, utils_1.compareObjects)(movedNodeData, node.data);
106
+ if (!droppedOnSelf) {
107
+ (_a = attrs.reorder) === null || _a === void 0 ? void 0 : _a.call(attrs, movedNodeData, node.data, this.isTargettingAbove);
108
+ }
109
+ }
110
+ this.isReorderingOver = false;
111
+ };
112
+ this.handleDragEnd = (ev, node) => {
113
+ var _a;
114
+ attrs.enableLogging && console.log(`drag end, dropEffect:`, (_a = ev.dataTransfer) === null || _a === void 0 ? void 0 : _a.dropEffect);
115
+ // "move" on success, "none" on failure/cancellation
116
+ // attrs.reorderfinish?.();
117
+ this.isReorderingOver = false;
118
+ };
119
+ },
120
+ view({ attrs }) {
121
+ const { node, enableToggling, enableReordering, emptyChildrenMessage } = attrs;
122
+ const canHaveChildren = node.children !== undefined;
123
+ const hasChildren = node.children !== undefined && (node.children.length > 0);
124
+ const expanded = canHaveChildren && node.expanded;
125
+ const collapsed = canHaveChildren && !node.expanded;
126
+ const targettingBackgroundClass = this.isReorderingOver ? (this.isTargettingAbove ? "mico-tree-reorder-above" : "mico-tree-reorder-below") : "";
127
+ return (0, mithril_1.default)("li.mico-tree-node", {
128
+ // key: item.key,
129
+ }, [
130
+ // The items's label (either a string or Vnode) and expand/collapse control (if there are children)
131
+ (0, mithril_1.default)("", {
132
+ style: `display: flex; align-items: stretch; max-width: 100%; overflow-x: hidden; ${(attrs.toggleExpansionWithLabel && enableToggling) ? `cursor: pointer;` : ``}`,
133
+ }, [
134
+ expanded ? (0, mithril_1.default)("button.mico-tree-toggle", {
135
+ style: `width: 1.5rem; flex: none;`,
136
+ class: enableToggling ? `hover:text-primary` : `text-view-icon`,
137
+ disabled: !enableToggling,
138
+ onclick: (ev) => {
139
+ var _a;
140
+ if (!enableToggling)
141
+ return;
142
+ node.expanded = false;
143
+ (_a = attrs.ontoggle) === null || _a === void 0 ? void 0 : _a.call(attrs, node, false, ev.altKey);
144
+ },
145
+ }, attrs.expandedIcon || (0, mithril_1.default)(icon_1.Icon, { fas: "chevron-down" })) : undefined,
146
+ collapsed ? (0, mithril_1.default)("button.mico-tree-toggle", {
147
+ style: `width: 1.5rem; flex: none;`,
148
+ class: enableToggling ? `hover:text-primary` : `text-view-icon`,
149
+ disabled: !enableToggling,
150
+ onclick: (ev) => {
151
+ var _a;
152
+ if (!enableToggling)
153
+ return;
154
+ node.expanded = true;
155
+ (_a = attrs.ontoggle) === null || _a === void 0 ? void 0 : _a.call(attrs, node, true, ev.altKey);
156
+ },
157
+ }, attrs.collapsedIcon || (0, mithril_1.default)(icon_1.Icon, { fas: "chevron-right" })) : undefined,
158
+ (0, mithril_1.default)("", {
159
+ class: `${targettingBackgroundClass}`,
160
+ style: "position: relative; flex: 1 1 0%; display: flex; flex-direction: column; overflow-x: hidden;",
161
+ draggable: enableReordering,
162
+ onclick: (ev) => {
163
+ var _a;
164
+ this.handleClick(ev, node);
165
+ // If labels should toggle expansion, toggle expansion
166
+ if (attrs.toggleExpansionWithLabel && enableToggling) {
167
+ node.expanded = !expanded;
168
+ (_a = attrs.ontoggle) === null || _a === void 0 ? void 0 : _a.call(attrs, node, !expanded, ev.altKey);
169
+ }
170
+ },
171
+ ondragstart: (ev) => this.handleDragStart(ev, node),
172
+ ondrag: (ev) => this.handleDrag(ev, node),
173
+ ondragover: (ev) => this.handleDragOver(ev, node),
174
+ ondragleave: (ev) => this.handleDragLeave(ev, node),
175
+ ondrop: (ev) => this.handleDrop(ev, node),
176
+ ondragend: (ev) => this.handleDragEnd(ev, node),
177
+ }, [
178
+ (typeof node.label === "string") ? (0, mithril_1.default)("span", node.label) : node.label,
179
+ ]),
180
+ ]),
181
+ // Any nested children within the item
182
+ (expanded) ? (0, mithril_1.default)("ul", [
183
+ ...(node.children || []).map((child, i) => {
184
+ return (0, mithril_1.default)(TreeNode, {
185
+ // key: child.key,
186
+ node: child,
187
+ enableToggling: attrs.enableToggling,
188
+ enableReordering: attrs.enableReordering,
189
+ ontoggle: attrs.ontoggle,
190
+ onclick: attrs.onclick,
191
+ reorder: attrs.reorder,
192
+ // reorderstart: attrs.reorderstart,
193
+ // reorderover: attrs.reorderover,
194
+ // reorderfinish: attrs.reorderfinish,
195
+ emptyChildrenMessage: attrs.emptyChildrenMessage,
196
+ collapsedIcon: attrs.collapsedIcon,
197
+ expandedIcon: attrs.expandedIcon,
198
+ toggleExpansionWithLabel: attrs.toggleExpansionWithLabel,
199
+ });
200
+ }),
201
+ (canHaveChildren && !hasChildren && emptyChildrenMessage !== undefined)
202
+ ? (0, mithril_1.default)("span.mico-tree-empty-children", emptyChildrenMessage)
203
+ : [],
204
+ ]) : [],
205
+ ]);
206
+ }
207
+ };
208
+ return Tree;
209
+ }
210
+ exports.createTree = createTree;
@@ -0,0 +1,3 @@
1
+ export * from "./Tree";
2
+ export * from "./tree-node.interface";
3
+ export * from "./tree-utils";
@@ -1 +1,19 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./Tree"), exports);
18
+ __exportStar(require("./tree-node.interface"), exports);
19
+ __exportStar(require("./tree-utils"), exports);
@@ -0,0 +1,7 @@
1
+ import m from "mithril";
2
+ export interface TreeNode<T extends object = {}> {
3
+ label: string | m.Vnode<any>;
4
+ expanded: boolean;
5
+ children?: TreeNode<T>[];
6
+ data: T;
7
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,11 @@
1
+ import { TreeNode } from "./tree-node.interface";
2
+ /**
3
+ * Recursively find the list of all descendants of the given node.
4
+ *
5
+ * The given node itself is not included in the list of descendants.
6
+ *
7
+ * Performs a depth-first traversal.
8
+ * @param node
9
+ * @returns
10
+ */
11
+ export declare function allTreeDescendants<T extends object = {}>(node: TreeNode<T>): T[];
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.allTreeDescendants = void 0;
4
+ /**
5
+ * Recursively find the list of all descendants of the given node.
6
+ *
7
+ * The given node itself is not included in the list of descendants.
8
+ *
9
+ * Performs a depth-first traversal.
10
+ * @param node
11
+ * @returns
12
+ */
13
+ function allTreeDescendants(node) {
14
+ var _a;
15
+ const descendants = [];
16
+ for (const child of (_a = node.children) !== null && _a !== void 0 ? _a : []) {
17
+ descendants.push(child.data);
18
+ descendants.push(...allTreeDescendants(child));
19
+ }
20
+ return descendants;
21
+ }
22
+ exports.allTreeDescendants = allTreeDescendants;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hypericon/mico",
3
- "version": "0.3.10",
3
+ "version": "0.3.12",
4
4
  "description": "UI components and utilities with Mithril",
5
5
  "author": "Ed Johnson, Hypericon Ltd",
6
6
  "license": "MIT",