1 |
|
2 |
|
3 | var __rest = (this && this.__rest) || function (s, e) {
|
4 | var t = {};
|
5 | for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
6 | t[p] = s[p];
|
7 | if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
8 | for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
9 | if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
10 | t[p[i]] = s[p[i]];
|
11 | }
|
12 | return t;
|
13 | };
|
14 | import { UUID } from '@lumino/coreutils';
|
15 | import { Signal } from '@lumino/signaling';
|
16 | import React from 'react';
|
17 | import ReactDOM from 'react-dom';
|
18 | import badSvgstr from '../../style/debug/bad.svg';
|
19 | import blankSvgstr from '../../style/debug/blank.svg';
|
20 | import refreshSvgstr from '../../style/icons/toolbar/refresh.svg';
|
21 | import { LabIconStyle } from '../style';
|
22 | import { classes, getReactAttrs } from '../utils';
|
23 | export class LabIcon {
|
24 | |
25 |
|
26 |
|
27 | constructor({ name, svgstr, render, unrender, _loading = false }) {
|
28 | this._props = {};
|
29 | this._svgReplaced = new Signal(this);
|
30 | |
31 |
|
32 |
|
33 |
|
34 |
|
35 | this._svgElement = undefined;
|
36 | this._svgInnerHTML = undefined;
|
37 | this._svgReactAttrs = undefined;
|
38 | if (!(name && svgstr)) {
|
39 |
|
40 | console.error(`When defining a new LabIcon, name and svgstr must both be non-empty strings. name: ${name}, svgstr: ${svgstr}`);
|
41 | return badIcon;
|
42 | }
|
43 |
|
44 | this._loading = _loading;
|
45 |
|
46 | if (LabIcon._instances.has(name)) {
|
47 |
|
48 | const icon = LabIcon._instances.get(name);
|
49 | if (this._loading) {
|
50 |
|
51 | icon.svgstr = svgstr;
|
52 | this._loading = false;
|
53 | return icon;
|
54 | }
|
55 | else {
|
56 |
|
57 | if (LabIcon._debug) {
|
58 | console.warn(`Redefining previously loaded icon svgstr. name: ${name}, svgstrOld: ${icon.svgstr}, svgstr: ${svgstr}`);
|
59 | }
|
60 | icon.svgstr = svgstr;
|
61 | return icon;
|
62 | }
|
63 | }
|
64 | this.name = name;
|
65 | this.react = this._initReact(name);
|
66 | this.svgstr = svgstr;
|
67 |
|
68 | this._initRender({ render, unrender });
|
69 | LabIcon._instances.set(this.name, this);
|
70 | }
|
71 | |
72 |
|
73 |
|
74 | |
75 |
|
76 |
|
77 |
|
78 |
|
79 |
|
80 |
|
81 |
|
82 | static remove(container) {
|
83 |
|
84 | while (container.firstChild) {
|
85 | container.firstChild.remove();
|
86 | }
|
87 |
|
88 | container.className = '';
|
89 | return container;
|
90 | }
|
91 | |
92 |
|
93 |
|
94 |
|
95 |
|
96 |
|
97 |
|
98 |
|
99 |
|
100 | static resolve({ icon }) {
|
101 | if (icon instanceof LabIcon) {
|
102 |
|
103 | return icon;
|
104 | }
|
105 | if (typeof icon === 'string') {
|
106 |
|
107 | const resolved = LabIcon._instances.get(icon);
|
108 | if (resolved) {
|
109 | return resolved;
|
110 | }
|
111 |
|
112 | if (LabIcon._debug) {
|
113 |
|
114 | console.warn(`Lookup failed for icon, creating loading icon. icon: ${icon}`);
|
115 | }
|
116 |
|
117 |
|
118 | return new LabIcon({ name: icon, svgstr: refreshSvgstr, _loading: true });
|
119 | }
|
120 |
|
121 |
|
122 | return new LabIcon(icon);
|
123 | }
|
124 | |
125 |
|
126 |
|
127 |
|
128 |
|
129 |
|
130 |
|
131 |
|
132 |
|
133 |
|
134 |
|
135 |
|
136 |
|
137 |
|
138 |
|
139 |
|
140 |
|
141 |
|
142 |
|
143 |
|
144 |
|
145 | static resolveElement(_a) {
|
146 | var { icon, iconClass, fallback } = _a, props = __rest(_a, ["icon", "iconClass", "fallback"]);
|
147 | if (!Private.isResolvable(icon)) {
|
148 | if (!iconClass && fallback) {
|
149 |
|
150 | return fallback.element(props);
|
151 | }
|
152 |
|
153 | props.className = classes(iconClass, props.className);
|
154 |
|
155 | return Private.blankElement(props);
|
156 | }
|
157 | return LabIcon.resolve({ icon }).element(props);
|
158 | }
|
159 | |
160 |
|
161 |
|
162 |
|
163 |
|
164 |
|
165 |
|
166 |
|
167 |
|
168 |
|
169 |
|
170 |
|
171 |
|
172 |
|
173 |
|
174 |
|
175 |
|
176 |
|
177 |
|
178 |
|
179 |
|
180 | static resolveReact(_a) {
|
181 | var { icon, iconClass, fallback } = _a, props = __rest(_a, ["icon", "iconClass", "fallback"]);
|
182 | if (!Private.isResolvable(icon)) {
|
183 | if (!iconClass && fallback) {
|
184 |
|
185 | return React.createElement(fallback.react, Object.assign({}, props));
|
186 | }
|
187 |
|
188 | props.className = classes(iconClass, props.className);
|
189 |
|
190 | return React.createElement(Private.blankReact, Object.assign({}, props));
|
191 | }
|
192 | const resolved = LabIcon.resolve({ icon });
|
193 | return React.createElement(resolved.react, Object.assign({}, props));
|
194 | }
|
195 | |
196 |
|
197 |
|
198 | static resolveSvg({ name, svgstr }) {
|
199 | const svgDoc = new DOMParser().parseFromString(Private.svgstrShim(svgstr), 'image/svg+xml');
|
200 | const svgError = svgDoc.querySelector('parsererror');
|
201 |
|
202 | if (svgError) {
|
203 |
|
204 | const errmsg = `SVG HTML was malformed for LabIcon instance.\nname: ${name}, svgstr: ${svgstr}`;
|
205 | if (LabIcon._debug) {
|
206 |
|
207 | console.error(errmsg);
|
208 | return svgError;
|
209 | }
|
210 | else {
|
211 |
|
212 | console.warn(errmsg);
|
213 | return null;
|
214 | }
|
215 | }
|
216 | else {
|
217 |
|
218 | return svgDoc.documentElement;
|
219 | }
|
220 | }
|
221 | |
222 |
|
223 |
|
224 |
|
225 |
|
226 | static toggleDebug(debug) {
|
227 | LabIcon._debug = debug !== null && debug !== void 0 ? debug : !LabIcon._debug;
|
228 | }
|
229 | |
230 |
|
231 |
|
232 |
|
233 |
|
234 |
|
235 |
|
236 |
|
237 | bindprops(props) {
|
238 | const view = Object.create(this);
|
239 | view._props = props;
|
240 | view.react = view._initReact(view.name + '_bind');
|
241 | return view;
|
242 | }
|
243 | |
244 |
|
245 |
|
246 |
|
247 |
|
248 |
|
249 |
|
250 |
|
251 |
|
252 |
|
253 |
|
254 |
|
255 |
|
256 |
|
257 |
|
258 |
|
259 |
|
260 |
|
261 |
|
262 |
|
263 |
|
264 |
|
265 |
|
266 |
|
267 |
|
268 |
|
269 |
|
270 |
|
271 |
|
272 |
|
273 |
|
274 |
|
275 |
|
276 |
|
277 |
|
278 |
|
279 | element(props = {}) {
|
280 | var _a;
|
281 | let _b = Object.assign(Object.assign({}, this._props), props), { className, container, label, title, tag = 'div' } = _b, styleProps = __rest(_b, ["className", "container", "label", "title", "tag"]);
|
282 |
|
283 | const maybeSvgElement = container === null || container === void 0 ? void 0 : container.firstChild;
|
284 | if (((_a = maybeSvgElement === null || maybeSvgElement === void 0 ? void 0 : maybeSvgElement.dataset) === null || _a === void 0 ? void 0 : _a.iconId) === this._uuid) {
|
285 |
|
286 | return maybeSvgElement;
|
287 | }
|
288 |
|
289 | if (!this.svgElement) {
|
290 |
|
291 | return document.createElement('div');
|
292 | }
|
293 | let returnSvgElement = true;
|
294 | if (container) {
|
295 |
|
296 | while (container.firstChild) {
|
297 | container.firstChild.remove();
|
298 | }
|
299 | }
|
300 | else {
|
301 |
|
302 | container = document.createElement(tag);
|
303 | returnSvgElement = false;
|
304 | }
|
305 | if (label != null) {
|
306 | container.textContent = label;
|
307 | }
|
308 | Private.initContainer({ container, className, styleProps, title });
|
309 |
|
310 | const svgElement = this.svgElement.cloneNode(true);
|
311 | container.appendChild(svgElement);
|
312 | return returnSvgElement ? svgElement : container;
|
313 | }
|
314 | render(container, options) {
|
315 | var _a;
|
316 | let label = (_a = options === null || options === void 0 ? void 0 : options.children) === null || _a === void 0 ? void 0 : _a[0];
|
317 |
|
318 | if (typeof label !== 'string') {
|
319 | label = undefined;
|
320 | }
|
321 | this.element(Object.assign({ container,
|
322 | label }, options === null || options === void 0 ? void 0 : options.props));
|
323 | }
|
324 | get svgElement() {
|
325 | if (this._svgElement === undefined) {
|
326 | this._svgElement = this._initSvg({ uuid: this._uuid });
|
327 | }
|
328 | return this._svgElement;
|
329 | }
|
330 | get svgInnerHTML() {
|
331 | if (this._svgInnerHTML === undefined) {
|
332 | if (this.svgElement === null) {
|
333 |
|
334 | this._svgInnerHTML = null;
|
335 | }
|
336 | else {
|
337 | this._svgInnerHTML = this.svgElement.innerHTML;
|
338 | }
|
339 | }
|
340 | return this._svgInnerHTML;
|
341 | }
|
342 | get svgReactAttrs() {
|
343 | if (this._svgReactAttrs === undefined) {
|
344 | if (this.svgElement === null) {
|
345 |
|
346 | this._svgReactAttrs = null;
|
347 | }
|
348 | else {
|
349 | this._svgReactAttrs = getReactAttrs(this.svgElement, {
|
350 | ignore: ['data-icon-id']
|
351 | });
|
352 | }
|
353 | }
|
354 | return this._svgReactAttrs;
|
355 | }
|
356 | get svgstr() {
|
357 | return this._svgstr;
|
358 | }
|
359 | set svgstr(svgstr) {
|
360 | this._svgstr = svgstr;
|
361 |
|
362 | const uuid = UUID.uuid4();
|
363 | const uuidOld = this._uuid;
|
364 | this._uuid = uuid;
|
365 |
|
366 | this._svgElement = undefined;
|
367 | this._svgInnerHTML = undefined;
|
368 | this._svgReactAttrs = undefined;
|
369 |
|
370 | document
|
371 | .querySelectorAll(`[data-icon-id="${uuidOld}"]`)
|
372 | .forEach(oldSvgElement => {
|
373 | if (this.svgElement) {
|
374 | oldSvgElement.replaceWith(this.svgElement.cloneNode(true));
|
375 | }
|
376 | });
|
377 |
|
378 | this._svgReplaced.emit();
|
379 | }
|
380 | _initReact(displayName) {
|
381 | const component = React.forwardRef((props = {}, ref) => {
|
382 | const _a = Object.assign(Object.assign({}, this._props), props), { className, container, label, title, tag = 'div' } = _a, styleProps = __rest(_a, ["className", "container", "label", "title", "tag"]);
|
383 |
|
384 | const [, setId] = React.useState(this._uuid);
|
385 |
|
386 | React.useEffect(() => {
|
387 | const onSvgReplaced = () => {
|
388 | setId(this._uuid);
|
389 | };
|
390 | this._svgReplaced.connect(onSvgReplaced);
|
391 |
|
392 | return () => {
|
393 | this._svgReplaced.disconnect(onSvgReplaced);
|
394 | };
|
395 | });
|
396 |
|
397 | const Tag = tag;
|
398 |
|
399 | if (!(this.svgInnerHTML && this.svgReactAttrs)) {
|
400 |
|
401 | return React.createElement(React.Fragment, null);
|
402 | }
|
403 | const svgComponent = (React.createElement("svg", Object.assign({}, this.svgReactAttrs, { dangerouslySetInnerHTML: { __html: this.svgInnerHTML }, ref: ref })));
|
404 | if (container) {
|
405 | Private.initContainer({ container, className, styleProps, title });
|
406 | return (React.createElement(React.Fragment, null,
|
407 | svgComponent,
|
408 | label));
|
409 | }
|
410 | else {
|
411 | return (React.createElement(Tag, { className: classes(className, LabIconStyle.styleClass(styleProps)) },
|
412 | svgComponent,
|
413 | label));
|
414 | }
|
415 | });
|
416 | component.displayName = `LabIcon_${displayName}`;
|
417 | return component;
|
418 | }
|
419 | _initRender({ render, unrender }) {
|
420 | if (render) {
|
421 | this.render = render;
|
422 | if (unrender) {
|
423 | this.unrender = unrender;
|
424 | }
|
425 | }
|
426 | else if (unrender) {
|
427 | console.warn('In _initRender, ignoring unrender arg since render is undefined');
|
428 | }
|
429 | }
|
430 | _initSvg({ title, uuid } = {}) {
|
431 | const svgElement = LabIcon.resolveSvg(this);
|
432 | if (!svgElement) {
|
433 |
|
434 | return svgElement;
|
435 | }
|
436 | if (svgElement.tagName !== 'parsererror') {
|
437 |
|
438 | svgElement.dataset.icon = this.name;
|
439 | if (uuid) {
|
440 | svgElement.dataset.iconId = uuid;
|
441 | }
|
442 | if (title) {
|
443 | Private.setTitleSvg(svgElement, title);
|
444 | }
|
445 | }
|
446 | return svgElement;
|
447 | }
|
448 | }
|
449 | LabIcon._debug = false;
|
450 | LabIcon._instances = new Map();
|
451 | var Private;
|
452 | (function (Private) {
|
453 | function blankElement(_a) {
|
454 | var { className = '', container, label, title, tag = 'div' } = _a, styleProps = __rest(_a, ["className", "container", "label", "title", "tag"]);
|
455 | if ((container === null || container === void 0 ? void 0 : container.className) === className) {
|
456 |
|
457 | return container;
|
458 | }
|
459 | if (container) {
|
460 |
|
461 | while (container.firstChild) {
|
462 | container.firstChild.remove();
|
463 | }
|
464 | }
|
465 | else {
|
466 |
|
467 | container = document.createElement(tag);
|
468 | }
|
469 | if (label != null) {
|
470 | container.textContent = label;
|
471 | }
|
472 | Private.initContainer({ container, className, styleProps, title });
|
473 | return container;
|
474 | }
|
475 | Private.blankElement = blankElement;
|
476 | Private.blankReact = React.forwardRef((_a, ref) => {
|
477 | var { className = '', container, label, title, tag = 'div' } = _a, styleProps = __rest(_a, ["className", "container", "label", "title", "tag"]);
|
478 |
|
479 | const Tag = tag;
|
480 | if (container) {
|
481 | initContainer({ container, className, styleProps, title });
|
482 | return React.createElement(React.Fragment, null);
|
483 | }
|
484 | else {
|
485 |
|
486 | return (React.createElement(Tag, { className: classes(className, LabIconStyle.styleClass(styleProps)) },
|
487 | ref && blankIcon.react({ ref }),
|
488 | label));
|
489 | }
|
490 | });
|
491 | Private.blankReact.displayName = 'BlankReact';
|
492 | function initContainer({ container, className, styleProps, title }) {
|
493 | if (title != null) {
|
494 | container.title = title;
|
495 | }
|
496 | const styleClass = LabIconStyle.styleClass(styleProps);
|
497 | if (className != null) {
|
498 |
|
499 | const classResolved = classes(className, styleClass);
|
500 | container.className = classResolved;
|
501 | return classResolved;
|
502 | }
|
503 | else if (styleClass) {
|
504 |
|
505 | container.classList.add(styleClass);
|
506 | return styleClass;
|
507 | }
|
508 | else {
|
509 | return '';
|
510 | }
|
511 | }
|
512 | Private.initContainer = initContainer;
|
513 | function isResolvable(icon) {
|
514 | return !!(icon &&
|
515 | (typeof icon === 'string' ||
|
516 | (icon.name && icon.svgstr)));
|
517 | }
|
518 | Private.isResolvable = isResolvable;
|
519 | function setTitleSvg(svgNode, title) {
|
520 |
|
521 | const titleNodes = svgNode.getElementsByTagName('title');
|
522 | if (titleNodes.length) {
|
523 | titleNodes[0].textContent = title;
|
524 | }
|
525 | else {
|
526 | const titleNode = document.createElement('title');
|
527 | titleNode.textContent = title;
|
528 | svgNode.appendChild(titleNode);
|
529 | }
|
530 | }
|
531 | Private.setTitleSvg = setTitleSvg;
|
532 | |
533 |
|
534 |
|
535 |
|
536 |
|
537 |
|
538 |
|
539 |
|
540 |
|
541 |
|
542 |
|
543 |
|
544 | function svgstrShim(svgstr, strict = true) {
|
545 |
|
546 |
|
547 | const [, base64, raw] = decodeURIComponent(svgstr)
|
548 | .replace(/>\s*\n\s*</g, '><')
|
549 | .replace(/\s*\n\s*/g, ' ')
|
550 | .match(strict
|
551 | ?
|
552 | /^(?:data:.*?(;base64)?,)?(.*)/
|
553 | :
|
554 | /(?:(base64).*)?(<svg.*)/);
|
555 | // decode from base64, if needed
|
556 | return base64 ? atob(raw) : raw;
|
557 | }
|
558 | Private.svgstrShim = svgstrShim;
|
559 | /**
|
560 | * TODO: figure out story for independent Renderers.
|
561 | * Base implementation of IRenderer.
|
562 | */
|
563 | class Renderer {
|
564 | constructor(_icon, _rendererOptions) {
|
565 | this._icon = _icon;
|
566 | this._rendererOptions = _rendererOptions;
|
567 | }
|
568 | // eslint-disable-next-line
|
569 | render(container, options) { }
|
570 | }
|
571 | Private.Renderer = Renderer;
|
572 | /**
|
573 | * TODO: figure out story for independent Renderers.
|
574 | * Implementation of IRenderer that creates the icon svg node
|
575 | * as a DOM element.
|
576 | */
|
577 | class ElementRenderer extends Renderer {
|
578 | render(container, options) {
|
579 | var _a, _b;
|
580 | let label = (_a = options === null || options === void 0 ? void 0 : options.children) === null || _a === void 0 ? void 0 : _a[0];
|
581 | // narrow type of label
|
582 | if (typeof label !== 'string') {
|
583 | label = undefined;
|
584 | }
|
585 | this._icon.element(Object.assign(Object.assign({ container,
|
586 | label }, (_b = this._rendererOptions) === null || _b === void 0 ? void 0 : _b.props), options === null || options === void 0 ? void 0 : options.props));
|
587 | }
|
588 | }
|
589 | Private.ElementRenderer = ElementRenderer;
|
590 | /**
|
591 | * TODO: figure out story for independent Renderers.
|
592 | * Implementation of IRenderer that creates the icon svg node
|
593 | * as a React component.
|
594 | */
|
595 | class ReactRenderer extends Renderer {
|
596 | render(container, options) {
|
597 | var _a, _b;
|
598 | let label = (_a = options === null || options === void 0 ? void 0 : options.children) === null || _a === void 0 ? void 0 : _a[0];
|
599 | // narrow type of label
|
600 | if (typeof label !== 'string') {
|
601 | label = undefined;
|
602 | }
|
603 | ReactDOM.render(React.createElement(this._icon.react, Object.assign({ container: container, label: label }, Object.assign(Object.assign({}, (_b = this._rendererOptions) === null || _b === void 0 ? void 0 : _b.props), options === null || options === void 0 ? void 0 : options.props))), container);
|
604 | }
|
605 | unrender(container) {
|
606 | ReactDOM.unmountComponentAtNode(container);
|
607 | }
|
608 | }
|
609 | Private.ReactRenderer = ReactRenderer;
|
610 | })(Private || (Private = {}));
|
611 | // need to be at the bottom since constructor depends on Private
|
612 | export const badIcon = new LabIcon({
|
613 | name: 'ui-components:bad',
|
614 | svgstr: badSvgstr
|
615 | });
|
616 | export const blankIcon = new LabIcon({
|
617 | name: 'ui-components:blank',
|
618 | svgstr: blankSvgstr
|
619 | });
|
620 | //# sourceMappingURL=labicon.js.map |
\ | No newline at end of file |