UNPKG

3 kBPlain TextView Raw
1// Copyright (c) Jupyter Development Team.
2// Distributed under the terms of the Modified BSD License.
3
4import { caretDownEmptyIcon } from '../icon';
5
6/**
7 * A namespace for node styling.
8 */
9export namespace Styling {
10 /**
11 * Style a node and its child elements with the default tag names.
12 *
13 * @param node - The base node.
14 *
15 * @param className - The optional CSS class to add to styled nodes.
16 */
17 export function styleNode(node: HTMLElement, className = ''): void {
18 styleNodeByTag(node, 'select', className);
19 styleNodeByTag(node, 'textarea', className);
20 styleNodeByTag(node, 'input', className);
21 styleNodeByTag(node, 'button', className);
22 }
23
24 /**
25 * Style a node and its elements that have a given tag name.
26 *
27 * @param node - The base node.
28 *
29 * @param tagName - The html tag name to style.
30 *
31 * @param className - The optional CSS class to add to styled nodes.
32 */
33 export function styleNodeByTag(
34 node: HTMLElement,
35 tagName: string,
36 className = ''
37 ): void {
38 if (node.localName === tagName) {
39 node.classList.add('jp-mod-styled');
40 }
41 if (node.localName === 'select') {
42 const multiple = node.hasAttribute('multiple');
43 wrapSelect(node as HTMLSelectElement, multiple);
44 }
45 const nodes = node.getElementsByTagName(tagName);
46 for (let i = 0; i < nodes.length; i++) {
47 const child = nodes[i];
48 child.classList.add('jp-mod-styled');
49 if (className) {
50 child.classList.add(className);
51 }
52 if (tagName === 'select') {
53 const multiple = child.hasAttribute('multiple');
54 wrapSelect(child as HTMLSelectElement, multiple);
55 }
56 }
57 }
58
59 /**
60 * Wrap a select node.
61 */
62 export function wrapSelect(
63 node: HTMLSelectElement,
64 multiple?: boolean
65 ): HTMLElement {
66 const wrapper = document.createElement('div');
67 wrapper.classList.add('jp-select-wrapper');
68 node.addEventListener('focus', Private.onFocus);
69 node.addEventListener('blur', Private.onFocus);
70 node.classList.add('jp-mod-styled');
71 if (node.parentElement) {
72 node.parentElement.replaceChild(wrapper, node);
73 }
74 wrapper.appendChild(node);
75
76 if (multiple) {
77 wrapper.classList.add('multiple');
78 } else {
79 // add the icon node
80 wrapper.appendChild(
81 caretDownEmptyIcon.element({
82 tag: 'span',
83 stylesheet: 'select',
84 right: '8px',
85 top: '5px',
86 width: '18px'
87 })
88 );
89 }
90
91 return wrapper;
92 }
93}
94
95/**
96 * The namespace for module private data.
97 */
98namespace Private {
99 /**
100 * Handle a focus event on a styled select.
101 */
102 export function onFocus(event: FocusEvent): void {
103 const target = event.target as Element;
104 const parent = target.parentElement;
105 if (!parent) {
106 return;
107 }
108 if (event.type === 'focus') {
109 parent.classList.add('jp-mod-focused');
110 } else {
111 parent.classList.remove('jp-mod-focused');
112 }
113 }
114}