@tylertech/forge-core 2.2.2 → 2.3.1

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.
@@ -1,4 +1,7 @@
1
1
  import { replaceElement, isArray, removeAllChildren, walkUpUntil } from '../utils';
2
+ export const CUSTOM_ELEMENT_STYLES_PROPERTY = '_forgeElementStyles';
3
+ /** Whether the browser supports constructable stylesheets */
4
+ export const supportsConstructableStyleSheets = window.ShadowRoot && 'adoptedStyleSheets' in Document.prototype && 'replace' in CSSStyleSheet.prototype;
2
5
  /**
3
6
  * Recursively defines a component as a custom elements and all of its dependencies.
4
7
  * @param component The component to import.
@@ -83,11 +86,14 @@ export function attachLightTemplate(componentInstance, template) {
83
86
  * @param {string} elementName The name of the element the shadow root is to be attached to.
84
87
  * @param {string} template The shadow root template HTML string.
85
88
  * @param {string | string[]} styles The shadow root styles string to be encapsulated by this shadow root.
86
- * @param {boolean} [delegatesFocus=false] Should the component delagate focus.
89
+ * @param {boolean} [delegatesFocus=false] Should the component delegate focus.
87
90
  */
88
91
  export function attachShadowTemplate(componentInstance, template, styles, delegatesFocus = false) {
89
- const templateElement = prepareShadowTemplate(template, styles);
92
+ const templateElement = prepareShadowTemplate(template);
90
93
  componentInstance.attachShadow({ mode: 'open', delegatesFocus });
94
+ if (styles) {
95
+ setShadowStyles(componentInstance, styles);
96
+ }
91
97
  setShadowTemplate(componentInstance, templateElement);
92
98
  }
93
99
  /**
@@ -101,10 +107,13 @@ export function replaceShadowTemplate(componentInstance, template, styles) {
101
107
  if (!componentInstance.shadowRoot) {
102
108
  throw new Error('This element does not contain a shadow root. Did you mean to call `attachShadowTemplate`?');
103
109
  }
104
- const templateElement = prepareShadowTemplate(template, styles);
110
+ const templateElement = prepareShadowTemplate(template);
105
111
  if (componentInstance.shadowRoot.children.length) {
106
112
  removeAllChildren(componentInstance.shadowRoot);
107
113
  }
114
+ if (styles) {
115
+ setShadowStyles(componentInstance, styles, { force: true });
116
+ }
108
117
  setShadowTemplate(componentInstance, templateElement);
109
118
  }
110
119
  /**
@@ -115,10 +124,16 @@ export function replaceShadowTemplate(componentInstance, template, styles) {
115
124
  */
116
125
  export function prepareShadowTemplate(template, styles) {
117
126
  const templateElement = parseTemplateString(template);
127
+ // Deprecated in favor of `setShadowStyles()`, leaving for backwards compatibility
128
+ // TODO: remove this in next major version, and remove `styles` argument above
118
129
  if (styles) {
119
130
  styles = styles instanceof Array ? styles : [styles];
120
131
  const styleElement = document.createElement('style');
121
- styleElement.type = 'text/css';
132
+ // eslint-disable-next-line @typescript-eslint/dot-notation
133
+ const nonce = window['forgeNonce'];
134
+ if (nonce) {
135
+ styleElement.setAttribute('nonce', nonce);
136
+ }
122
137
  styleElement.textContent = styles.join(' ');
123
138
  templateElement.content.appendChild(styleElement);
124
139
  }
@@ -132,6 +147,45 @@ export function prepareShadowTemplate(template, styles) {
132
147
  export function setShadowTemplate(componentInstance, templateElement) {
133
148
  componentInstance.shadowRoot.appendChild(templateElement.content.cloneNode(true));
134
149
  }
150
+ /**
151
+ * Applies styles to the shadow root of the provided element instance.
152
+ * @param {T} componentInstance A component instance.
153
+ * @param {string | string[]} styles The styles to be applied to the shadow root.
154
+ * @param options Options for setting the styles.
155
+ */
156
+ export function setShadowStyles(componentInstance, styles, { force } = { force: false }) {
157
+ const ctor = componentInstance.constructor;
158
+ if (!componentInstance.shadowRoot || !styles) {
159
+ if (supportsConstructableStyleSheets) {
160
+ if (ctor[CUSTOM_ELEMENT_STYLES_PROPERTY]) {
161
+ ctor[CUSTOM_ELEMENT_STYLES_PROPERTY] = [];
162
+ }
163
+ if (componentInstance.shadowRoot) {
164
+ componentInstance.shadowRoot.adoptedStyleSheets = [];
165
+ }
166
+ }
167
+ return;
168
+ }
169
+ styles = styles instanceof Array ? styles : [styles];
170
+ if (supportsConstructableStyleSheets) {
171
+ if (force || !ctor[CUSTOM_ELEMENT_STYLES_PROPERTY]) {
172
+ const sheet = new CSSStyleSheet();
173
+ sheet.replaceSync(styles.join(' '));
174
+ ctor[CUSTOM_ELEMENT_STYLES_PROPERTY] = [sheet];
175
+ }
176
+ componentInstance.shadowRoot.adoptedStyleSheets = ctor[CUSTOM_ELEMENT_STYLES_PROPERTY];
177
+ }
178
+ else {
179
+ const styleElement = document.createElement('style');
180
+ // eslint-disable-next-line @typescript-eslint/dot-notation
181
+ const nonce = window['forgeNonce'];
182
+ if (nonce) {
183
+ styleElement.setAttribute('nonce', nonce);
184
+ }
185
+ styleElement.textContent = styles.join(' ');
186
+ componentInstance.shadowRoot.appendChild(styleElement);
187
+ }
188
+ }
135
189
  /**
136
190
  * Copies style rules from the provided document stylesheets collection to the provided shadow root stylesheet.
137
191
  * @param {Document} fromDocument The document to find the style sheets in.
@@ -637,11 +637,12 @@ export function deepQuerySelectorAll(rootElement, selectors, checkRootElement =
637
637
  }
638
638
  /**
639
639
  * Gets the currently focused element within the document by also traversing shadow roots.
640
+ * @param {Document} doc The document to get the active element from. Defaults to the current document.
640
641
  * @returns {Element}
641
642
  */
642
- export function getActiveElement() {
643
- const activeElement = document.activeElement;
644
- if (!activeElement || activeElement === document.body) {
643
+ export function getActiveElement(doc = document) {
644
+ const activeElement = doc.activeElement;
645
+ if (!activeElement || activeElement === doc.body) {
645
646
  return activeElement;
646
647
  }
647
648
  return getActiveShadowElement(activeElement);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tylertech/forge-core",
3
- "version": "2.2.2",
3
+ "version": "2.3.1",
4
4
  "description": "A library of core web utilities that support Forge Web Component libraries.",
5
5
  "author": "Tyler Technologies, Inc.",
6
6
  "license": "Apache-2.0",
@@ -16,4 +16,4 @@
16
16
  "@floating-ui/dom": "^0.5.0",
17
17
  "tslib": "^2.3.1"
18
18
  }
19
- }
19
+ }
@@ -1,3 +1,6 @@
1
+ export declare const CUSTOM_ELEMENT_STYLES_PROPERTY = "_forgeElementStyles";
2
+ /** Whether the browser supports constructable stylesheets */
3
+ export declare const supportsConstructableStyleSheets: boolean;
1
4
  /**
2
5
  * Recursively defines a component as a custom elements and all of its dependencies.
3
6
  * @param component The component to import.
@@ -46,7 +49,7 @@ export declare function attachLightTemplate<T extends HTMLElement>(componentInst
46
49
  * @param {string} elementName The name of the element the shadow root is to be attached to.
47
50
  * @param {string} template The shadow root template HTML string.
48
51
  * @param {string | string[]} styles The shadow root styles string to be encapsulated by this shadow root.
49
- * @param {boolean} [delegatesFocus=false] Should the component delagate focus.
52
+ * @param {boolean} [delegatesFocus=false] Should the component delegate focus.
50
53
  */
51
54
  export declare function attachShadowTemplate<T extends HTMLElement>(componentInstance: T, template: string, styles?: string | string[], delegatesFocus?: boolean): void;
52
55
  /**
@@ -70,6 +73,15 @@ export declare function prepareShadowTemplate(template: string, styles?: string
70
73
  * @param {HTMLTemplateElement} templateElement A template element to be cloned.
71
74
  */
72
75
  export declare function setShadowTemplate<T extends HTMLElement>(componentInstance: T, templateElement: HTMLTemplateElement): void;
76
+ /**
77
+ * Applies styles to the shadow root of the provided element instance.
78
+ * @param {T} componentInstance A component instance.
79
+ * @param {string | string[]} styles The styles to be applied to the shadow root.
80
+ * @param options Options for setting the styles.
81
+ */
82
+ export declare function setShadowStyles<T extends HTMLElement>(componentInstance: T, styles: string | string[], { force }?: {
83
+ force: boolean;
84
+ }): void;
73
85
  /**
74
86
  * Copies style rules from the provided document stylesheets collection to the provided shadow root stylesheet.
75
87
  * @param {Document} fromDocument The document to find the style sheets in.
@@ -2,6 +2,12 @@ declare global {
2
2
  interface Window {
3
3
  __forgeFlags__autoDefine: any;
4
4
  }
5
+ interface ShadowRoot {
6
+ adoptedStyleSheets: CSSStyleSheet[];
7
+ }
8
+ interface CSSStyleSheet {
9
+ replaceSync(cssText: string): void;
10
+ }
5
11
  }
6
12
  export interface ICustomElementConfig {
7
13
  /** The name of the custom element tag. */
@@ -211,9 +211,10 @@ export declare function matchesSelectors(el: Element | Node, selectors: string |
211
211
  export declare function deepQuerySelectorAll(rootElement: Element, selectors: string | string[], checkRootElement?: boolean): Element[];
212
212
  /**
213
213
  * Gets the currently focused element within the document by also traversing shadow roots.
214
+ * @param {Document} doc The document to get the active element from. Defaults to the current document.
214
215
  * @returns {Element}
215
216
  */
216
- export declare function getActiveElement(): Element;
217
+ export declare function getActiveElement(doc?: Document): Element;
217
218
  /**
218
219
  * Gets the active element within the provided elements shadow root. If the element
219
220
  * does not have a shadow root, the provided element is returned.