@brandup/ui 1.0.1 → 1.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.
- package/README.md +14 -19
- package/dist/cjs/index.js +114 -123
- package/dist/cjs/index.js.map +1 -1
- package/dist/mjs/index.js +114 -120
- package/dist/mjs/index.js.map +1 -1
- package/dist/types.d.ts +41 -36
- package/package.json +2 -17
package/README.md
CHANGED
|
@@ -21,25 +21,24 @@ npm i @brandup/ui@latest
|
|
|
21
21
|
```
|
|
22
22
|
abstract class UIElement {
|
|
23
23
|
abstract typeName: string;
|
|
24
|
-
readonly element: HTMLElement;
|
|
24
|
+
readonly element: HTMLElement | undefined;
|
|
25
25
|
|
|
26
26
|
protected setElement(elem: HTMLElement): void;
|
|
27
27
|
|
|
28
|
-
protected defineEvent(eventName: string, eventOptions?:
|
|
28
|
+
protected defineEvent(eventName: string, eventOptions?: EventInit): void;
|
|
29
29
|
protected raiseEvent(eventName: string, eventArgs?: any): boolean;
|
|
30
30
|
|
|
31
31
|
addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;
|
|
32
32
|
removeEventListener(type: string, listener?: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void;
|
|
33
33
|
dispatchEvent(event: Event): boolean;
|
|
34
34
|
|
|
35
|
-
registerCommand(name: string, execute: CommandDelegate, canExecute
|
|
36
|
-
registerAsyncCommand(name: string, delegate: CommandAsyncDelegate): void;
|
|
35
|
+
registerCommand(name: string, execute: CommandDelegate, canExecute?: CommandCanExecuteDelegate): void;
|
|
37
36
|
hasCommand(name: string): boolean;
|
|
38
|
-
execCommand(name: string, elem: HTMLElement): CommandExecutionResult;
|
|
39
37
|
|
|
40
38
|
protected _onRenderElement(_elem: HTMLElement);
|
|
41
39
|
protected _onCanExecCommand(_name: string, _elem: HTMLElement): boolean;
|
|
42
40
|
|
|
41
|
+
onDestroy(callback: VoidFunction | UIElement | Element);
|
|
43
42
|
destroy(): void;
|
|
44
43
|
}
|
|
45
44
|
```
|
|
@@ -51,27 +50,23 @@ abstract class UIElement {
|
|
|
51
50
|
```
|
|
52
51
|
<button data-command="send">Send</button>
|
|
53
52
|
|
|
54
|
-
this.registerCommand("send", (
|
|
53
|
+
this.registerCommand("send", (context: CommandContext) => { context.target.innerHTML = "ok"; });
|
|
55
54
|
```
|
|
56
55
|
|
|
57
56
|
Так же можно регистрировать асинхронные команды:
|
|
58
57
|
|
|
59
58
|
```
|
|
60
|
-
this.
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
context.timeoutCallback = () => {
|
|
70
|
-
clearTimeout(t);
|
|
71
|
-
};
|
|
59
|
+
this.registerCommand("command1-async", (context: CommandContext) => {
|
|
60
|
+
return Promise<void>(resolve => {
|
|
61
|
+
context.target.innerHTML = "Loading...";
|
|
62
|
+
const t = window.setTimeout(() => {
|
|
63
|
+
context.target.innerHTML = "Ok";
|
|
64
|
+
resolve();
|
|
65
|
+
}, 2000);
|
|
66
|
+
});
|
|
72
67
|
});
|
|
73
68
|
```
|
|
74
69
|
|
|
75
|
-
Команды
|
|
70
|
+
Команды срабатывают по событию `click`.
|
|
76
71
|
|
|
77
72
|
Во время выполнения команды, у элемента добавляется стиль **executing**.
|
package/dist/cjs/index.js
CHANGED
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
const constants = {
|
|
4
|
+
ElemAttributeName: "uiElement",
|
|
5
|
+
ElemPropertyName: "uielement",
|
|
6
|
+
CommandAttributeName: "command",
|
|
7
|
+
CommandExecutingCssClassName: "executing",
|
|
8
|
+
CommandEventName: "uicommand"
|
|
9
|
+
};
|
|
10
|
+
|
|
7
11
|
class UIElement {
|
|
8
|
-
__element
|
|
9
|
-
__events
|
|
10
|
-
|
|
12
|
+
__element;
|
|
13
|
+
__events;
|
|
14
|
+
__commands;
|
|
15
|
+
__destroyCallbacks;
|
|
11
16
|
// Element members
|
|
12
17
|
get element() { return this.__element; }
|
|
13
18
|
setElement(elem) {
|
|
@@ -16,35 +21,33 @@ class UIElement {
|
|
|
16
21
|
if (this.__element || UIElement.hasElement(elem))
|
|
17
22
|
throw "UIElement already defined";
|
|
18
23
|
this.__element = elem;
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
this.defineEvent(
|
|
24
|
+
elem[constants.ElemPropertyName] = this;
|
|
25
|
+
elem.dataset[constants.ElemAttributeName] = this.typeName;
|
|
26
|
+
this.defineEvent(constants.CommandEventName, { cancelable: false, bubbles: true });
|
|
22
27
|
this._onRenderElement(elem);
|
|
23
28
|
}
|
|
24
29
|
// static members
|
|
25
30
|
static hasElement(elem) {
|
|
26
|
-
return !!elem.dataset[ElemAttributeName];
|
|
31
|
+
return !!elem.dataset[constants.ElemAttributeName];
|
|
27
32
|
}
|
|
28
33
|
// HTMLElement event members
|
|
29
34
|
defineEvent(eventName, eventOptions) {
|
|
35
|
+
if (!this.__events)
|
|
36
|
+
this.__events = {};
|
|
30
37
|
this.__events[eventName] = eventOptions ? eventOptions : null;
|
|
31
38
|
}
|
|
32
39
|
raiseEvent(eventName, eventArgs) {
|
|
33
|
-
if (!(eventName in this.__events))
|
|
40
|
+
if (!this.__events || !(eventName in this.__events))
|
|
34
41
|
throw new Error(`Not found event "${eventName}".`);
|
|
35
42
|
const eventOptions = this.__events[eventName];
|
|
36
43
|
const eventInit = {};
|
|
37
44
|
if (eventOptions) {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
eventInit.cancelable = eventOptions.cancelable;
|
|
42
|
-
if (eventOptions.composed)
|
|
43
|
-
eventInit.composed = eventOptions.composed;
|
|
45
|
+
eventInit.bubbles = eventOptions.bubbles;
|
|
46
|
+
eventInit.cancelable = eventOptions.cancelable;
|
|
47
|
+
eventInit.composed = eventOptions.composed;
|
|
44
48
|
}
|
|
45
|
-
eventInit.detail = eventArgs
|
|
46
|
-
|
|
47
|
-
return this.dispatchEvent(event);
|
|
49
|
+
eventInit.detail = eventArgs;
|
|
50
|
+
return this.dispatchEvent(new CustomEvent(eventName, eventInit));
|
|
48
51
|
}
|
|
49
52
|
addEventListener(type, listener, options) {
|
|
50
53
|
this.__element?.addEventListener(type, listener, options);
|
|
@@ -59,127 +62,112 @@ class UIElement {
|
|
|
59
62
|
}
|
|
60
63
|
// Command members
|
|
61
64
|
registerCommand(name, execute, canExecute) {
|
|
62
|
-
|
|
63
|
-
|
|
65
|
+
if (!this.__commands)
|
|
66
|
+
this.__commands = {};
|
|
67
|
+
const nornalizedName = name.toLowerCase();
|
|
68
|
+
if (nornalizedName in this.__commands)
|
|
69
|
+
throw new Error(`Command "${name}" already registered.`);
|
|
70
|
+
this.__commands[nornalizedName] = {
|
|
64
71
|
name: name,
|
|
65
72
|
execute,
|
|
66
|
-
canExecute
|
|
67
|
-
isExecuting: false
|
|
68
|
-
};
|
|
69
|
-
}
|
|
70
|
-
registerAsyncCommand(name, delegate, canExecute) {
|
|
71
|
-
name = this.verifyCommandName(name);
|
|
72
|
-
this.__commandHandlers[name] = {
|
|
73
|
-
name: name,
|
|
74
|
-
delegate,
|
|
75
|
-
canExecute,
|
|
76
|
-
isExecuting: false
|
|
73
|
+
canExecute
|
|
77
74
|
};
|
|
78
75
|
}
|
|
79
76
|
hasCommand(name) {
|
|
80
|
-
return name.toLowerCase() in this.
|
|
77
|
+
return this.__commands && name.toLowerCase() in this.__commands;
|
|
81
78
|
}
|
|
82
|
-
|
|
83
|
-
|
|
79
|
+
/** @internal */
|
|
80
|
+
__execCommand(name, target) {
|
|
81
|
+
if (!this.__element || !this.__commands)
|
|
84
82
|
throw new Error("UIElement is not set HTMLElement.");
|
|
85
83
|
const key = name.toLowerCase();
|
|
86
|
-
|
|
84
|
+
const command = this.__commands[key];
|
|
85
|
+
if (!command)
|
|
87
86
|
throw new Error(`Command "${name}" is not registered.`);
|
|
88
87
|
const context = {
|
|
89
|
-
target
|
|
90
|
-
uiElem: this
|
|
91
|
-
transparent: false
|
|
88
|
+
target,
|
|
89
|
+
uiElem: this
|
|
92
90
|
};
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
return { result: exports.CommandsExecStatus.NotAllow, context };
|
|
91
|
+
if (command.isExecuting)
|
|
92
|
+
return { status: "already", context };
|
|
93
|
+
command.isExecuting = true;
|
|
94
|
+
if (!this._onCanExecCommand(name, target)) {
|
|
95
|
+
delete command.isExecuting;
|
|
96
|
+
return { status: "disallow", context };
|
|
100
97
|
}
|
|
101
|
-
if (
|
|
102
|
-
|
|
103
|
-
return {
|
|
98
|
+
if (command.canExecute && !command.canExecute(context)) {
|
|
99
|
+
delete command.isExecuting;
|
|
100
|
+
return { status: "disallow", context };
|
|
104
101
|
}
|
|
105
|
-
this.raiseEvent(
|
|
106
|
-
name:
|
|
102
|
+
this.raiseEvent(constants.CommandEventName, {
|
|
103
|
+
name: command.name,
|
|
107
104
|
uiElem: this,
|
|
108
105
|
elem: this.__element
|
|
109
106
|
});
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
107
|
+
let isAsync;
|
|
108
|
+
try {
|
|
109
|
+
const commandResult = command.execute(context);
|
|
110
|
+
if (commandResult && commandResult instanceof Promise) {
|
|
111
|
+
isAsync = true;
|
|
112
|
+
target.classList.add(constants.CommandExecutingCssClassName);
|
|
113
|
+
commandResult
|
|
114
|
+
.finally(() => {
|
|
115
|
+
target.classList.remove(constants.CommandExecutingCssClassName);
|
|
116
|
+
delete command.isExecuting;
|
|
117
|
+
});
|
|
117
118
|
}
|
|
118
119
|
}
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
let timeoutId = 0;
|
|
123
|
-
const endFunc = () => {
|
|
124
|
-
handler.isExecuting = false;
|
|
125
|
-
elem.classList.remove(CommandExecutingCssClassName);
|
|
126
|
-
};
|
|
127
|
-
const asyncContext = {
|
|
128
|
-
target: elem,
|
|
129
|
-
uiElem: this,
|
|
130
|
-
transparent: context.transparent,
|
|
131
|
-
complate: () => {
|
|
132
|
-
clearTimeout(timeoutId);
|
|
133
|
-
endFunc();
|
|
134
|
-
}
|
|
135
|
-
};
|
|
136
|
-
const handlerResult = handler.delegate(asyncContext);
|
|
137
|
-
if (handlerResult && handlerResult instanceof Promise)
|
|
138
|
-
handlerResult.finally(() => asyncContext.complate());
|
|
139
|
-
if (handler.isExecuting && asyncContext.timeout) {
|
|
140
|
-
timeoutId = window.setTimeout(() => {
|
|
141
|
-
if (asyncContext.timeoutCallback)
|
|
142
|
-
asyncContext.timeoutCallback();
|
|
143
|
-
endFunc();
|
|
144
|
-
}, asyncContext.timeout);
|
|
145
|
-
}
|
|
146
|
-
context.transparent = asyncContext.transparent;
|
|
120
|
+
finally {
|
|
121
|
+
if (!isAsync)
|
|
122
|
+
delete command.isExecuting;
|
|
147
123
|
}
|
|
148
|
-
|
|
149
|
-
throw new Error("Not set command execute flow.");
|
|
150
|
-
return { result: exports.CommandsExecStatus.Success, context: context };
|
|
151
|
-
}
|
|
152
|
-
verifyCommandName(name) {
|
|
153
|
-
const key = name.toLowerCase();
|
|
154
|
-
if (key in this.__commandHandlers)
|
|
155
|
-
throw new Error(`Command "${name}" already registered.`);
|
|
156
|
-
return key;
|
|
157
|
-
}
|
|
158
|
-
_onRenderElement(_elem) {
|
|
159
|
-
return;
|
|
124
|
+
return { status: "success", context: context };
|
|
160
125
|
}
|
|
126
|
+
_onRenderElement(_elem) { }
|
|
161
127
|
_onCanExecCommand(_name, _elem) {
|
|
162
128
|
return true;
|
|
163
129
|
}
|
|
130
|
+
onDestroy(callback) {
|
|
131
|
+
if (!this.__element)
|
|
132
|
+
return;
|
|
133
|
+
if (!this.__destroyCallbacks)
|
|
134
|
+
this.__destroyCallbacks = [];
|
|
135
|
+
if (callback instanceof UIElement)
|
|
136
|
+
this.__destroyCallbacks.push(() => callback.destroy());
|
|
137
|
+
else if (callback instanceof Element)
|
|
138
|
+
this.__destroyCallbacks.push(() => callback.remove());
|
|
139
|
+
else
|
|
140
|
+
this.__destroyCallbacks.push(callback);
|
|
141
|
+
}
|
|
142
|
+
toString() {
|
|
143
|
+
return this.typeName;
|
|
144
|
+
}
|
|
164
145
|
destroy() {
|
|
165
146
|
const elem = this.__element;
|
|
166
147
|
if (!elem)
|
|
167
148
|
return;
|
|
168
|
-
|
|
169
|
-
delete elem.
|
|
170
|
-
delete
|
|
149
|
+
delete elem.dataset[constants.ElemAttributeName];
|
|
150
|
+
delete elem[constants.ElemPropertyName];
|
|
151
|
+
delete this.__element;
|
|
152
|
+
delete this.__events;
|
|
153
|
+
delete this.__commands;
|
|
154
|
+
if (this.__destroyCallbacks) {
|
|
155
|
+
this.__destroyCallbacks.map(callback => {
|
|
156
|
+
try {
|
|
157
|
+
callback();
|
|
158
|
+
}
|
|
159
|
+
catch (reason) {
|
|
160
|
+
console.error(`Error in call "${this.typeName}" destroy callback.`);
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
delete this.__destroyCallbacks;
|
|
164
|
+
}
|
|
171
165
|
}
|
|
172
166
|
}
|
|
173
|
-
exports.CommandsExecStatus = void 0;
|
|
174
|
-
(function (CommandsExecStatus) {
|
|
175
|
-
CommandsExecStatus[CommandsExecStatus["NotAllow"] = 1] = "NotAllow";
|
|
176
|
-
CommandsExecStatus[CommandsExecStatus["AlreadyExecuting"] = 2] = "AlreadyExecuting";
|
|
177
|
-
CommandsExecStatus[CommandsExecStatus["Success"] = 3] = "Success";
|
|
178
|
-
})(exports.CommandsExecStatus || (exports.CommandsExecStatus = {}));
|
|
179
167
|
const fundUiElementByCommand = (elem, commandName) => {
|
|
180
168
|
while (elem) {
|
|
181
|
-
if (elem.dataset[ElemAttributeName]) {
|
|
182
|
-
const uiElem = elem[ElemPropertyName];
|
|
169
|
+
if (elem.dataset[constants.ElemAttributeName]) {
|
|
170
|
+
const uiElem = elem[constants.ElemPropertyName];
|
|
183
171
|
if (uiElem.hasCommand(commandName))
|
|
184
172
|
return uiElem;
|
|
185
173
|
}
|
|
@@ -195,7 +183,7 @@ const fundUiElementByCommand = (elem, commandName) => {
|
|
|
195
183
|
const commandClickHandler = (e) => {
|
|
196
184
|
let commandElem = e.target;
|
|
197
185
|
while (commandElem) {
|
|
198
|
-
if (commandElem.dataset[CommandAttributeName])
|
|
186
|
+
if (commandElem.dataset[constants.CommandAttributeName])
|
|
199
187
|
break;
|
|
200
188
|
if (commandElem === e.currentTarget)
|
|
201
189
|
return;
|
|
@@ -203,27 +191,30 @@ const commandClickHandler = (e) => {
|
|
|
203
191
|
}
|
|
204
192
|
if (!commandElem)
|
|
205
193
|
return;
|
|
206
|
-
const commandName = commandElem.dataset[CommandAttributeName];
|
|
194
|
+
const commandName = commandElem.dataset[constants.CommandAttributeName];
|
|
207
195
|
if (!commandName)
|
|
208
196
|
throw new Error("Command data attribute is not have value.");
|
|
209
197
|
const uiElem = fundUiElementByCommand(commandElem, commandName);
|
|
210
|
-
if (uiElem
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
else {
|
|
214
|
-
const commandResult = uiElem.execCommand(commandName, commandElem);
|
|
215
|
-
if (commandResult.context.transparent)
|
|
198
|
+
if (uiElem) {
|
|
199
|
+
const result = uiElem.__execCommand(commandName, commandElem);
|
|
200
|
+
if (result.status == "success" && result.context.transparent)
|
|
216
201
|
return;
|
|
217
202
|
}
|
|
203
|
+
else
|
|
204
|
+
console.warn(`Not find handler for command "${commandName}".`);
|
|
218
205
|
e.preventDefault();
|
|
219
206
|
e.stopPropagation();
|
|
220
207
|
e.stopImmediatePropagation();
|
|
221
208
|
};
|
|
222
209
|
window.addEventListener("click", commandClickHandler, false);
|
|
223
210
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
211
|
+
HTMLElement.prototype.ui = function (factory) {
|
|
212
|
+
factory(this);
|
|
213
|
+
return this;
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
const UICONSTANTS = constants;
|
|
217
|
+
|
|
218
|
+
exports.UICONSTANTS = UICONSTANTS;
|
|
228
219
|
exports.UIElement = UIElement;
|
|
229
220
|
//# sourceMappingURL=index.js.map
|
package/dist/cjs/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
package/dist/mjs/index.js
CHANGED
|
@@ -1,11 +1,16 @@
|
|
|
1
|
-
const
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
const constants = {
|
|
2
|
+
ElemAttributeName: "uiElement",
|
|
3
|
+
ElemPropertyName: "uielement",
|
|
4
|
+
CommandAttributeName: "command",
|
|
5
|
+
CommandExecutingCssClassName: "executing",
|
|
6
|
+
CommandEventName: "uicommand"
|
|
7
|
+
};
|
|
8
|
+
|
|
5
9
|
class UIElement {
|
|
6
|
-
__element
|
|
7
|
-
__events
|
|
8
|
-
|
|
10
|
+
__element;
|
|
11
|
+
__events;
|
|
12
|
+
__commands;
|
|
13
|
+
__destroyCallbacks;
|
|
9
14
|
// Element members
|
|
10
15
|
get element() { return this.__element; }
|
|
11
16
|
setElement(elem) {
|
|
@@ -14,35 +19,33 @@ class UIElement {
|
|
|
14
19
|
if (this.__element || UIElement.hasElement(elem))
|
|
15
20
|
throw "UIElement already defined";
|
|
16
21
|
this.__element = elem;
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
this.defineEvent(
|
|
22
|
+
elem[constants.ElemPropertyName] = this;
|
|
23
|
+
elem.dataset[constants.ElemAttributeName] = this.typeName;
|
|
24
|
+
this.defineEvent(constants.CommandEventName, { cancelable: false, bubbles: true });
|
|
20
25
|
this._onRenderElement(elem);
|
|
21
26
|
}
|
|
22
27
|
// static members
|
|
23
28
|
static hasElement(elem) {
|
|
24
|
-
return !!elem.dataset[ElemAttributeName];
|
|
29
|
+
return !!elem.dataset[constants.ElemAttributeName];
|
|
25
30
|
}
|
|
26
31
|
// HTMLElement event members
|
|
27
32
|
defineEvent(eventName, eventOptions) {
|
|
33
|
+
if (!this.__events)
|
|
34
|
+
this.__events = {};
|
|
28
35
|
this.__events[eventName] = eventOptions ? eventOptions : null;
|
|
29
36
|
}
|
|
30
37
|
raiseEvent(eventName, eventArgs) {
|
|
31
|
-
if (!(eventName in this.__events))
|
|
38
|
+
if (!this.__events || !(eventName in this.__events))
|
|
32
39
|
throw new Error(`Not found event "${eventName}".`);
|
|
33
40
|
const eventOptions = this.__events[eventName];
|
|
34
41
|
const eventInit = {};
|
|
35
42
|
if (eventOptions) {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
eventInit.cancelable = eventOptions.cancelable;
|
|
40
|
-
if (eventOptions.composed)
|
|
41
|
-
eventInit.composed = eventOptions.composed;
|
|
43
|
+
eventInit.bubbles = eventOptions.bubbles;
|
|
44
|
+
eventInit.cancelable = eventOptions.cancelable;
|
|
45
|
+
eventInit.composed = eventOptions.composed;
|
|
42
46
|
}
|
|
43
|
-
eventInit.detail = eventArgs
|
|
44
|
-
|
|
45
|
-
return this.dispatchEvent(event);
|
|
47
|
+
eventInit.detail = eventArgs;
|
|
48
|
+
return this.dispatchEvent(new CustomEvent(eventName, eventInit));
|
|
46
49
|
}
|
|
47
50
|
addEventListener(type, listener, options) {
|
|
48
51
|
this.__element?.addEventListener(type, listener, options);
|
|
@@ -57,127 +60,112 @@ class UIElement {
|
|
|
57
60
|
}
|
|
58
61
|
// Command members
|
|
59
62
|
registerCommand(name, execute, canExecute) {
|
|
60
|
-
|
|
61
|
-
|
|
63
|
+
if (!this.__commands)
|
|
64
|
+
this.__commands = {};
|
|
65
|
+
const nornalizedName = name.toLowerCase();
|
|
66
|
+
if (nornalizedName in this.__commands)
|
|
67
|
+
throw new Error(`Command "${name}" already registered.`);
|
|
68
|
+
this.__commands[nornalizedName] = {
|
|
62
69
|
name: name,
|
|
63
70
|
execute,
|
|
64
|
-
canExecute
|
|
65
|
-
isExecuting: false
|
|
66
|
-
};
|
|
67
|
-
}
|
|
68
|
-
registerAsyncCommand(name, delegate, canExecute) {
|
|
69
|
-
name = this.verifyCommandName(name);
|
|
70
|
-
this.__commandHandlers[name] = {
|
|
71
|
-
name: name,
|
|
72
|
-
delegate,
|
|
73
|
-
canExecute,
|
|
74
|
-
isExecuting: false
|
|
71
|
+
canExecute
|
|
75
72
|
};
|
|
76
73
|
}
|
|
77
74
|
hasCommand(name) {
|
|
78
|
-
return name.toLowerCase() in this.
|
|
75
|
+
return this.__commands && name.toLowerCase() in this.__commands;
|
|
79
76
|
}
|
|
80
|
-
|
|
81
|
-
|
|
77
|
+
/** @internal */
|
|
78
|
+
__execCommand(name, target) {
|
|
79
|
+
if (!this.__element || !this.__commands)
|
|
82
80
|
throw new Error("UIElement is not set HTMLElement.");
|
|
83
81
|
const key = name.toLowerCase();
|
|
84
|
-
|
|
82
|
+
const command = this.__commands[key];
|
|
83
|
+
if (!command)
|
|
85
84
|
throw new Error(`Command "${name}" is not registered.`);
|
|
86
85
|
const context = {
|
|
87
|
-
target
|
|
88
|
-
uiElem: this
|
|
89
|
-
transparent: false
|
|
86
|
+
target,
|
|
87
|
+
uiElem: this
|
|
90
88
|
};
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
return { result: CommandsExecStatus.NotAllow, context };
|
|
89
|
+
if (command.isExecuting)
|
|
90
|
+
return { status: "already", context };
|
|
91
|
+
command.isExecuting = true;
|
|
92
|
+
if (!this._onCanExecCommand(name, target)) {
|
|
93
|
+
delete command.isExecuting;
|
|
94
|
+
return { status: "disallow", context };
|
|
98
95
|
}
|
|
99
|
-
if (
|
|
100
|
-
|
|
101
|
-
return {
|
|
96
|
+
if (command.canExecute && !command.canExecute(context)) {
|
|
97
|
+
delete command.isExecuting;
|
|
98
|
+
return { status: "disallow", context };
|
|
102
99
|
}
|
|
103
|
-
this.raiseEvent(
|
|
104
|
-
name:
|
|
100
|
+
this.raiseEvent(constants.CommandEventName, {
|
|
101
|
+
name: command.name,
|
|
105
102
|
uiElem: this,
|
|
106
103
|
elem: this.__element
|
|
107
104
|
});
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
105
|
+
let isAsync;
|
|
106
|
+
try {
|
|
107
|
+
const commandResult = command.execute(context);
|
|
108
|
+
if (commandResult && commandResult instanceof Promise) {
|
|
109
|
+
isAsync = true;
|
|
110
|
+
target.classList.add(constants.CommandExecutingCssClassName);
|
|
111
|
+
commandResult
|
|
112
|
+
.finally(() => {
|
|
113
|
+
target.classList.remove(constants.CommandExecutingCssClassName);
|
|
114
|
+
delete command.isExecuting;
|
|
115
|
+
});
|
|
115
116
|
}
|
|
116
117
|
}
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
let timeoutId = 0;
|
|
121
|
-
const endFunc = () => {
|
|
122
|
-
handler.isExecuting = false;
|
|
123
|
-
elem.classList.remove(CommandExecutingCssClassName);
|
|
124
|
-
};
|
|
125
|
-
const asyncContext = {
|
|
126
|
-
target: elem,
|
|
127
|
-
uiElem: this,
|
|
128
|
-
transparent: context.transparent,
|
|
129
|
-
complate: () => {
|
|
130
|
-
clearTimeout(timeoutId);
|
|
131
|
-
endFunc();
|
|
132
|
-
}
|
|
133
|
-
};
|
|
134
|
-
const handlerResult = handler.delegate(asyncContext);
|
|
135
|
-
if (handlerResult && handlerResult instanceof Promise)
|
|
136
|
-
handlerResult.finally(() => asyncContext.complate());
|
|
137
|
-
if (handler.isExecuting && asyncContext.timeout) {
|
|
138
|
-
timeoutId = window.setTimeout(() => {
|
|
139
|
-
if (asyncContext.timeoutCallback)
|
|
140
|
-
asyncContext.timeoutCallback();
|
|
141
|
-
endFunc();
|
|
142
|
-
}, asyncContext.timeout);
|
|
143
|
-
}
|
|
144
|
-
context.transparent = asyncContext.transparent;
|
|
118
|
+
finally {
|
|
119
|
+
if (!isAsync)
|
|
120
|
+
delete command.isExecuting;
|
|
145
121
|
}
|
|
146
|
-
|
|
147
|
-
throw new Error("Not set command execute flow.");
|
|
148
|
-
return { result: CommandsExecStatus.Success, context: context };
|
|
149
|
-
}
|
|
150
|
-
verifyCommandName(name) {
|
|
151
|
-
const key = name.toLowerCase();
|
|
152
|
-
if (key in this.__commandHandlers)
|
|
153
|
-
throw new Error(`Command "${name}" already registered.`);
|
|
154
|
-
return key;
|
|
155
|
-
}
|
|
156
|
-
_onRenderElement(_elem) {
|
|
157
|
-
return;
|
|
122
|
+
return { status: "success", context: context };
|
|
158
123
|
}
|
|
124
|
+
_onRenderElement(_elem) { }
|
|
159
125
|
_onCanExecCommand(_name, _elem) {
|
|
160
126
|
return true;
|
|
161
127
|
}
|
|
128
|
+
onDestroy(callback) {
|
|
129
|
+
if (!this.__element)
|
|
130
|
+
return;
|
|
131
|
+
if (!this.__destroyCallbacks)
|
|
132
|
+
this.__destroyCallbacks = [];
|
|
133
|
+
if (callback instanceof UIElement)
|
|
134
|
+
this.__destroyCallbacks.push(() => callback.destroy());
|
|
135
|
+
else if (callback instanceof Element)
|
|
136
|
+
this.__destroyCallbacks.push(() => callback.remove());
|
|
137
|
+
else
|
|
138
|
+
this.__destroyCallbacks.push(callback);
|
|
139
|
+
}
|
|
140
|
+
toString() {
|
|
141
|
+
return this.typeName;
|
|
142
|
+
}
|
|
162
143
|
destroy() {
|
|
163
144
|
const elem = this.__element;
|
|
164
145
|
if (!elem)
|
|
165
146
|
return;
|
|
166
|
-
|
|
167
|
-
delete elem.
|
|
168
|
-
delete
|
|
147
|
+
delete elem.dataset[constants.ElemAttributeName];
|
|
148
|
+
delete elem[constants.ElemPropertyName];
|
|
149
|
+
delete this.__element;
|
|
150
|
+
delete this.__events;
|
|
151
|
+
delete this.__commands;
|
|
152
|
+
if (this.__destroyCallbacks) {
|
|
153
|
+
this.__destroyCallbacks.map(callback => {
|
|
154
|
+
try {
|
|
155
|
+
callback();
|
|
156
|
+
}
|
|
157
|
+
catch (reason) {
|
|
158
|
+
console.error(`Error in call "${this.typeName}" destroy callback.`);
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
delete this.__destroyCallbacks;
|
|
162
|
+
}
|
|
169
163
|
}
|
|
170
164
|
}
|
|
171
|
-
var CommandsExecStatus;
|
|
172
|
-
(function (CommandsExecStatus) {
|
|
173
|
-
CommandsExecStatus[CommandsExecStatus["NotAllow"] = 1] = "NotAllow";
|
|
174
|
-
CommandsExecStatus[CommandsExecStatus["AlreadyExecuting"] = 2] = "AlreadyExecuting";
|
|
175
|
-
CommandsExecStatus[CommandsExecStatus["Success"] = 3] = "Success";
|
|
176
|
-
})(CommandsExecStatus || (CommandsExecStatus = {}));
|
|
177
165
|
const fundUiElementByCommand = (elem, commandName) => {
|
|
178
166
|
while (elem) {
|
|
179
|
-
if (elem.dataset[ElemAttributeName]) {
|
|
180
|
-
const uiElem = elem[ElemPropertyName];
|
|
167
|
+
if (elem.dataset[constants.ElemAttributeName]) {
|
|
168
|
+
const uiElem = elem[constants.ElemPropertyName];
|
|
181
169
|
if (uiElem.hasCommand(commandName))
|
|
182
170
|
return uiElem;
|
|
183
171
|
}
|
|
@@ -193,7 +181,7 @@ const fundUiElementByCommand = (elem, commandName) => {
|
|
|
193
181
|
const commandClickHandler = (e) => {
|
|
194
182
|
let commandElem = e.target;
|
|
195
183
|
while (commandElem) {
|
|
196
|
-
if (commandElem.dataset[CommandAttributeName])
|
|
184
|
+
if (commandElem.dataset[constants.CommandAttributeName])
|
|
197
185
|
break;
|
|
198
186
|
if (commandElem === e.currentTarget)
|
|
199
187
|
return;
|
|
@@ -201,23 +189,29 @@ const commandClickHandler = (e) => {
|
|
|
201
189
|
}
|
|
202
190
|
if (!commandElem)
|
|
203
191
|
return;
|
|
204
|
-
const commandName = commandElem.dataset[CommandAttributeName];
|
|
192
|
+
const commandName = commandElem.dataset[constants.CommandAttributeName];
|
|
205
193
|
if (!commandName)
|
|
206
194
|
throw new Error("Command data attribute is not have value.");
|
|
207
195
|
const uiElem = fundUiElementByCommand(commandElem, commandName);
|
|
208
|
-
if (uiElem
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
else {
|
|
212
|
-
const commandResult = uiElem.execCommand(commandName, commandElem);
|
|
213
|
-
if (commandResult.context.transparent)
|
|
196
|
+
if (uiElem) {
|
|
197
|
+
const result = uiElem.__execCommand(commandName, commandElem);
|
|
198
|
+
if (result.status == "success" && result.context.transparent)
|
|
214
199
|
return;
|
|
215
200
|
}
|
|
201
|
+
else
|
|
202
|
+
console.warn(`Not find handler for command "${commandName}".`);
|
|
216
203
|
e.preventDefault();
|
|
217
204
|
e.stopPropagation();
|
|
218
205
|
e.stopImmediatePropagation();
|
|
219
206
|
};
|
|
220
207
|
window.addEventListener("click", commandClickHandler, false);
|
|
221
208
|
|
|
222
|
-
|
|
209
|
+
HTMLElement.prototype.ui = function (factory) {
|
|
210
|
+
factory(this);
|
|
211
|
+
return this;
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
const UICONSTANTS = constants;
|
|
215
|
+
|
|
216
|
+
export { UICONSTANTS, UIElement };
|
|
223
217
|
//# sourceMappingURL=index.js.map
|
package/dist/mjs/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
package/dist/types.d.ts
CHANGED
|
@@ -1,61 +1,66 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
1
|
+
interface UiConstants {
|
|
2
|
+
readonly ElemAttributeName: string;
|
|
3
|
+
readonly ElemPropertyName: string;
|
|
4
|
+
readonly CommandAttributeName: string;
|
|
5
|
+
readonly CommandExecutingCssClassName: string;
|
|
6
|
+
readonly CommandEventName: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
type CommandExecuteFunction = (context: CommandContext) => void | Promise<void | any>;
|
|
10
|
+
type CommandCanExecuteFunction = (context: CommandContext) => boolean;
|
|
8
11
|
declare abstract class UIElement {
|
|
9
|
-
private __element
|
|
10
|
-
private __events
|
|
11
|
-
private
|
|
12
|
+
private __element?;
|
|
13
|
+
private __events?;
|
|
14
|
+
private __commands?;
|
|
15
|
+
private __destroyCallbacks;
|
|
12
16
|
abstract typeName: string;
|
|
13
|
-
get element(): HTMLElement |
|
|
17
|
+
get element(): HTMLElement | undefined;
|
|
14
18
|
protected setElement(elem: HTMLElement): void;
|
|
15
19
|
static hasElement(elem: HTMLElement): boolean;
|
|
16
|
-
protected defineEvent(eventName: string, eventOptions?:
|
|
20
|
+
protected defineEvent(eventName: string, eventOptions?: EventInit): void;
|
|
17
21
|
protected raiseEvent<T = {}>(eventName: string, eventArgs?: T): boolean;
|
|
18
22
|
addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;
|
|
19
23
|
removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void;
|
|
20
24
|
dispatchEvent(event: Event): boolean;
|
|
21
|
-
registerCommand(name: string, execute:
|
|
22
|
-
|
|
23
|
-
hasCommand(name: string): boolean;
|
|
24
|
-
execCommand(name: string, elem: HTMLElement): CommandExecutionResult;
|
|
25
|
-
private verifyCommandName;
|
|
25
|
+
registerCommand(name: string, execute: CommandExecuteFunction, canExecute?: CommandCanExecuteFunction): void;
|
|
26
|
+
hasCommand(name: string): boolean | undefined;
|
|
26
27
|
protected _onRenderElement(_elem: HTMLElement): void;
|
|
27
28
|
protected _onCanExecCommand(_name: string, _elem: HTMLElement): boolean;
|
|
29
|
+
onDestroy(callback: VoidFunction | UIElement | Element): void;
|
|
30
|
+
toString(): string;
|
|
28
31
|
destroy(): void;
|
|
29
32
|
}
|
|
30
|
-
interface EventOptions {
|
|
31
|
-
bubbles?: boolean;
|
|
32
|
-
cancelable?: boolean;
|
|
33
|
-
composed?: boolean;
|
|
34
|
-
}
|
|
35
33
|
interface CommandEventArgs {
|
|
36
34
|
name: string;
|
|
37
35
|
uiElem: UIElement;
|
|
38
36
|
elem: HTMLElement;
|
|
39
37
|
}
|
|
40
38
|
interface CommandContext {
|
|
41
|
-
/**
|
|
39
|
+
/** HTMLElement on which the command is executed */
|
|
42
40
|
target: HTMLElement;
|
|
41
|
+
/** UIElement in which the command handler is registered. */
|
|
43
42
|
uiElem: UIElement;
|
|
44
|
-
|
|
43
|
+
/** Don't stop the click event chain of target. */
|
|
44
|
+
transparent?: boolean;
|
|
45
45
|
}
|
|
46
|
-
interface
|
|
47
|
-
|
|
46
|
+
interface CommandResult {
|
|
47
|
+
status: CommandExecStatus;
|
|
48
48
|
context: CommandContext;
|
|
49
49
|
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
50
|
+
type CommandExecStatus = "disallow" | "already" | "success";
|
|
51
|
+
|
|
52
|
+
declare global {
|
|
53
|
+
interface HTMLElement {
|
|
54
|
+
ui(factory: (elem: HTMLElement) => UIElement): HTMLElement;
|
|
55
|
+
}
|
|
56
|
+
interface Node {
|
|
57
|
+
readonly uielement: UIElement | undefined;
|
|
58
|
+
}
|
|
59
|
+
interface HTMLElementEventMap {
|
|
60
|
+
"uicommand": CustomEvent<CommandEventArgs>;
|
|
61
|
+
}
|
|
59
62
|
}
|
|
60
63
|
|
|
61
|
-
|
|
64
|
+
declare const UICONSTANTS: UiConstants;
|
|
65
|
+
|
|
66
|
+
export { type CommandCanExecuteFunction, type CommandContext, type CommandEventArgs, type CommandExecStatus, type CommandExecuteFunction, type CommandResult, UICONSTANTS, UIElement };
|
package/package.json
CHANGED
|
@@ -22,27 +22,12 @@
|
|
|
22
22
|
"email": "it@brandup.online"
|
|
23
23
|
},
|
|
24
24
|
"license": "Apache-2.0",
|
|
25
|
-
"version": "1.0.
|
|
25
|
+
"version": "1.0.3",
|
|
26
26
|
"main": "dist/cjs/index.js",
|
|
27
27
|
"module": "dist/mjs/index.js",
|
|
28
28
|
"types": "dist/types.d.ts",
|
|
29
|
-
"devDependencies": {
|
|
30
|
-
"@rollup/plugin-commonjs": "^25.0.8",
|
|
31
|
-
"@rollup/plugin-node-resolve": "^15.2.3",
|
|
32
|
-
"@rollup/plugin-terser": "^0.4.4",
|
|
33
|
-
"rollup": "^4.19.0",
|
|
34
|
-
"rollup-plugin-dts": "^6.1.1",
|
|
35
|
-
"rollup-plugin-peer-deps-external": "^2.2.4",
|
|
36
|
-
"rollup-plugin-typescript2": "^0.36.0",
|
|
37
|
-
"tslib": "^2.6.3",
|
|
38
|
-
"typescript": "^5.5.3"
|
|
39
|
-
},
|
|
40
29
|
"files": [
|
|
41
30
|
"dist",
|
|
42
31
|
"README.md"
|
|
43
|
-
]
|
|
44
|
-
"scripts": {
|
|
45
|
-
"build": "rollup -c --bundleConfigAsCjs",
|
|
46
|
-
"watch": "rollup -c -w --bundleConfigAsCjs"
|
|
47
|
-
}
|
|
32
|
+
]
|
|
48
33
|
}
|