@vaadin/component-base
Advanced tools
+44
-10
@@ -10,5 +10,18 @@ { | ||
| { | ||
| "kind": "variable", | ||
| "kind": "mixin", | ||
| "description": "", | ||
| "name": "DelegateStateMixin", | ||
| "description": "A mixin to delegate properties and attributes to a target element." | ||
| "members": [], | ||
| "attributes": [], | ||
| "parameters": [ | ||
| { | ||
| "name": "superclass" | ||
| } | ||
| ], | ||
| "mixins": [ | ||
| { | ||
| "name": "dedupeMixin", | ||
| "package": "@open-wc/dedupe-mixin" | ||
| } | ||
| ] | ||
| } | ||
@@ -95,3 +108,3 @@ ], | ||
| "kind": "mixin", | ||
| "description": "A mixin that allows to set partial I18N properties.", | ||
| "description": "A mixin that allows to set partial I18N properties.\n\nSubclasses provide their default values by overriding the\n`defaultI18n` static getter:\n\n```js\nstatic get defaultI18n() {\n return { foo: 'Foo', bar: 'Bar' };\n}\n```", | ||
| "name": "I18nMixin", | ||
@@ -122,5 +135,2 @@ "members": [ | ||
| { | ||
| "name": "defaultI18n" | ||
| }, | ||
| { | ||
| "name": "superClass" | ||
@@ -229,5 +239,17 @@ } | ||
| { | ||
| "kind": "variable", | ||
| "kind": "mixin", | ||
| "description": "A mixin that uses a ResizeObserver to listen to host size changes.", | ||
| "name": "ResizeMixin", | ||
| "description": "A mixin that uses a ResizeObserver to listen to host size changes." | ||
| "members": [], | ||
| "parameters": [ | ||
| { | ||
| "name": "superclass" | ||
| } | ||
| ], | ||
| "mixins": [ | ||
| { | ||
| "name": "dedupeMixin", | ||
| "package": "@open-wc/dedupe-mixin" | ||
| } | ||
| ] | ||
| } | ||
@@ -251,5 +273,17 @@ ], | ||
| { | ||
| "kind": "variable", | ||
| "kind": "mixin", | ||
| "description": "Mixin to insert styles into the outer scope to handle slotted components.\nThis is useful e.g. to hide native `<input type=\"number\">` controls.", | ||
| "name": "SlotStylesMixin", | ||
| "description": "Mixin to insert styles into the outer scope to handle slotted components.\nThis is useful e.g. to hide native `<input type=\"number\">` controls." | ||
| "members": [], | ||
| "parameters": [ | ||
| { | ||
| "name": "superclass" | ||
| } | ||
| ], | ||
| "mixins": [ | ||
| { | ||
| "name": "dedupeMixin", | ||
| "package": "@open-wc/dedupe-mixin" | ||
| } | ||
| ] | ||
| } | ||
@@ -256,0 +290,0 @@ ], |
+4
-4
| { | ||
| "name": "@vaadin/component-base", | ||
| "version": "25.2.0-alpha9", | ||
| "version": "25.2.0-beta1", | ||
| "publishConfig": { | ||
@@ -41,4 +41,4 @@ "access": "public" | ||
| "devDependencies": { | ||
| "@vaadin/chai-plugins": "25.2.0-alpha9", | ||
| "@vaadin/test-runner-commands": "25.2.0-alpha9", | ||
| "@vaadin/chai-plugins": "25.2.0-beta1", | ||
| "@vaadin/test-runner-commands": "25.2.0-beta1", | ||
| "@vaadin/testing-helpers": "^2.0.0", | ||
@@ -48,3 +48,3 @@ "sinon": "^21.0.2" | ||
| "customElements": "custom-elements.json", | ||
| "gitHead": "a38a03e8a8be45821f39c14054c63634dafe08d0" | ||
| "gitHead": "471a23f60d1eb725f98a33f62cb9664d9c0a4163" | ||
| } |
@@ -6,3 +6,2 @@ /** | ||
| */ | ||
| import type { ReactiveController } from 'lit'; | ||
| import type { Cache } from './cache.js'; | ||
@@ -27,6 +26,3 @@ import type { getFlatIndexByPath, getFlatIndexContext, getItemContext } from './helpers.js'; | ||
| */ | ||
| export class DataProviderController< | ||
| TItem, | ||
| TDataProviderParams extends Record<string, unknown>, | ||
| > implements ReactiveController { | ||
| export class DataProviderController<TItem, TDataProviderParams extends Record<string, unknown>> extends EventTarget { | ||
| /** | ||
@@ -99,6 +95,2 @@ * The controller host element. | ||
| hostConnected(): void; | ||
| hostDisconnected(): void; | ||
| /** | ||
@@ -105,0 +97,0 @@ * Whether the root cache or any of its decendant caches have pending requests. |
@@ -108,3 +108,3 @@ /** | ||
| const subCache = cache.getSubCache(levelIndex); | ||
| if (subCache && subCache.flatSize > 0 && subIndexes.length) { | ||
| if (subCache?.flatSize > 0 && subIndexes.length) { | ||
| return getFlatIndexByPath(subCache, subIndexes, flatIndex + flatIndexOnLevel + 1); | ||
@@ -111,0 +111,0 @@ } |
+1
-1
@@ -16,3 +16,3 @@ /** | ||
| export function defineCustomElement(CustomElement, version = '25.2.0-alpha9') { | ||
| export function defineCustomElement(CustomElement, version = '25.2.0-beta1') { | ||
| Object.defineProperty(CustomElement, 'version', { | ||
@@ -19,0 +19,0 @@ get() { |
@@ -10,117 +10,116 @@ /** | ||
| * A mixin to delegate properties and attributes to a target element. | ||
| * | ||
| * @polymerMixin | ||
| */ | ||
| export const DelegateStateMixin = dedupeMixin( | ||
| (superclass) => | ||
| class DelegateStateMixinClass extends superclass { | ||
| static get properties() { | ||
| return { | ||
| /** | ||
| * A target element to which attributes and properties are delegated. | ||
| * @protected | ||
| */ | ||
| stateTarget: { | ||
| type: Object, | ||
| observer: '_stateTargetChanged', | ||
| }, | ||
| }; | ||
| } | ||
| const DelegateStateMixinImplementation = (superclass) => { | ||
| return class DelegateStateMixinClass extends superclass { | ||
| static get properties() { | ||
| return { | ||
| /** | ||
| * A target element to which attributes and properties are delegated. | ||
| * @protected | ||
| */ | ||
| stateTarget: { | ||
| type: Object, | ||
| observer: '_stateTargetChanged', | ||
| }, | ||
| }; | ||
| } | ||
| /** | ||
| * An array of the host attributes to delegate to the target element. | ||
| */ | ||
| static get delegateAttrs() { | ||
| return []; | ||
| } | ||
| /** | ||
| * An array of the host attributes to delegate to the target element. | ||
| */ | ||
| static get delegateAttrs() { | ||
| return []; | ||
| } | ||
| /** | ||
| * An array of the host properties to delegate to the target element. | ||
| */ | ||
| static get delegateProps() { | ||
| return []; | ||
| } | ||
| /** | ||
| * An array of the host properties to delegate to the target element. | ||
| */ | ||
| static get delegateProps() { | ||
| return []; | ||
| } | ||
| /** @protected */ | ||
| ready() { | ||
| super.ready(); | ||
| /** @protected */ | ||
| ready() { | ||
| super.ready(); | ||
| this._createDelegateAttrsObserver(); | ||
| this._createDelegatePropsObserver(); | ||
| } | ||
| this._createDelegateAttrsObserver(); | ||
| this._createDelegatePropsObserver(); | ||
| } | ||
| /** @protected */ | ||
| _stateTargetChanged(target) { | ||
| if (target) { | ||
| this._ensureAttrsDelegated(); | ||
| this._ensurePropsDelegated(); | ||
| } | ||
| /** @protected */ | ||
| _stateTargetChanged(target) { | ||
| if (target) { | ||
| this._ensureAttrsDelegated(); | ||
| this._ensurePropsDelegated(); | ||
| } | ||
| } | ||
| /** @protected */ | ||
| _createDelegateAttrsObserver() { | ||
| this._createMethodObserver(`_delegateAttrsChanged(${this.constructor.delegateAttrs.join(', ')})`); | ||
| } | ||
| /** @protected */ | ||
| _createDelegateAttrsObserver() { | ||
| this._createMethodObserver(`_delegateAttrsChanged(${this.constructor.delegateAttrs.join(', ')})`); | ||
| } | ||
| /** @protected */ | ||
| _createDelegatePropsObserver() { | ||
| this._createMethodObserver(`_delegatePropsChanged(${this.constructor.delegateProps.join(', ')})`); | ||
| } | ||
| /** @protected */ | ||
| _createDelegatePropsObserver() { | ||
| this._createMethodObserver(`_delegatePropsChanged(${this.constructor.delegateProps.join(', ')})`); | ||
| } | ||
| /** @protected */ | ||
| _ensureAttrsDelegated() { | ||
| this.constructor.delegateAttrs.forEach((name) => { | ||
| this._delegateAttribute(name, this[name]); | ||
| }); | ||
| } | ||
| /** @protected */ | ||
| _ensureAttrsDelegated() { | ||
| this.constructor.delegateAttrs.forEach((name) => { | ||
| this._delegateAttribute(name, this[name]); | ||
| }); | ||
| } | ||
| /** @protected */ | ||
| _ensurePropsDelegated() { | ||
| this.constructor.delegateProps.forEach((name) => { | ||
| this._delegateProperty(name, this[name]); | ||
| }); | ||
| /** @protected */ | ||
| _ensurePropsDelegated() { | ||
| this.constructor.delegateProps.forEach((name) => { | ||
| this._delegateProperty(name, this[name]); | ||
| }); | ||
| } | ||
| /** @protected */ | ||
| _delegateAttrsChanged(...values) { | ||
| this.constructor.delegateAttrs.forEach((name, index) => { | ||
| this._delegateAttribute(name, values[index]); | ||
| }); | ||
| } | ||
| /** @protected */ | ||
| _delegatePropsChanged(...values) { | ||
| this.constructor.delegateProps.forEach((name, index) => { | ||
| this._delegateProperty(name, values[index]); | ||
| }); | ||
| } | ||
| /** @protected */ | ||
| _delegateAttribute(name, value) { | ||
| if (!this.stateTarget) { | ||
| return; | ||
| } | ||
| /** @protected */ | ||
| _delegateAttrsChanged(...values) { | ||
| this.constructor.delegateAttrs.forEach((name, index) => { | ||
| this._delegateAttribute(name, values[index]); | ||
| }); | ||
| if (name === 'invalid') { | ||
| this._delegateAttribute('aria-invalid', value ? 'true' : false); | ||
| } | ||
| /** @protected */ | ||
| _delegatePropsChanged(...values) { | ||
| this.constructor.delegateProps.forEach((name, index) => { | ||
| this._delegateProperty(name, values[index]); | ||
| }); | ||
| if (typeof value === 'boolean') { | ||
| this.stateTarget.toggleAttribute(name, value); | ||
| } else if (value) { | ||
| this.stateTarget.setAttribute(name, value); | ||
| } else { | ||
| this.stateTarget.removeAttribute(name); | ||
| } | ||
| } | ||
| /** @protected */ | ||
| _delegateAttribute(name, value) { | ||
| if (!this.stateTarget) { | ||
| return; | ||
| } | ||
| if (name === 'invalid') { | ||
| this._delegateAttribute('aria-invalid', value ? 'true' : false); | ||
| } | ||
| if (typeof value === 'boolean') { | ||
| this.stateTarget.toggleAttribute(name, value); | ||
| } else if (value) { | ||
| this.stateTarget.setAttribute(name, value); | ||
| } else { | ||
| this.stateTarget.removeAttribute(name); | ||
| } | ||
| /** @protected */ | ||
| _delegateProperty(name, value) { | ||
| if (!this.stateTarget) { | ||
| return; | ||
| } | ||
| /** @protected */ | ||
| _delegateProperty(name, value) { | ||
| if (!this.stateTarget) { | ||
| return; | ||
| } | ||
| this.stateTarget[name] = value; | ||
| } | ||
| }; | ||
| }; | ||
| this.stateTarget[name] = value; | ||
| } | ||
| }, | ||
| ); | ||
| export const DelegateStateMixin = dedupeMixin(DelegateStateMixinImplementation); |
+0
-2
@@ -36,4 +36,2 @@ /** | ||
| * A mixin to handle `dir` attribute based on the one set on the `<html>` element. | ||
| * | ||
| * @polymerMixin | ||
| */ | ||
@@ -40,0 +38,0 @@ export const DirMixin = (superClass) => |
@@ -34,6 +34,2 @@ /** | ||
| /** | ||
| * @polymerMixin | ||
| * @mixes DirMixin | ||
| */ | ||
| export const ElementMixin = (superClass) => | ||
@@ -40,0 +36,0 @@ class VaadinElementMixin extends DirMixin(superClass) { |
+5
-5
@@ -124,3 +124,3 @@ /** | ||
| // Allow undefined for testing events | ||
| let buttons = ev.buttons === undefined ? 1 : ev.buttons; | ||
| let buttons = ev.buttons ?? 1; | ||
| if (ev instanceof window.MouseEvent && !MOUSE_HAS_BUTTONS) { | ||
@@ -133,3 +133,3 @@ buttons = MOUSE_WHICH_TO_BUTTONS[ev.which] || 0; | ||
| // Allow undefined for testing events | ||
| const button = ev.button === undefined ? 0 : ev.button; | ||
| const button = ev.button ?? 0; | ||
| // Ev.button is 0 in mousedown/mouseup/click for left button activation | ||
@@ -237,3 +237,3 @@ return button === 0; | ||
| // if there is not a shadowroot, exit the loop | ||
| while (next && next.shadowRoot && !window.ShadyDOM) { | ||
| while (next?.shadowRoot && !window.ShadyDOM) { | ||
| // If there is a node at x/y in the shadowroot, look deeper | ||
@@ -454,3 +454,3 @@ const oldNext = next; | ||
| gd = gobj[dep]; | ||
| if (gd && gd[name]) { | ||
| if (gd?.[name]) { | ||
| gd[name] = (gd[name] || 1) - 1; | ||
@@ -538,3 +538,3 @@ gd._count = (gd._count || 1) - 1; | ||
| const preventer = detail.preventer || detail.sourceEvent; | ||
| if (preventer && preventer.preventDefault) { | ||
| if (preventer?.preventDefault) { | ||
| preventer.preventDefault(); | ||
@@ -541,0 +541,0 @@ } |
@@ -11,4 +11,3 @@ /** | ||
| */ | ||
| export declare function I18nMixin<I, T extends Constructor<HTMLElement>>( | ||
| defaultI18n: I, | ||
| export declare function I18nMixin<T extends Constructor<HTMLElement>, I = unknown>( | ||
| superclass: T, | ||
@@ -15,0 +14,0 @@ ): Constructor<I18nMixinClass<I>> & T; |
+21
-4
@@ -38,5 +38,12 @@ /** | ||
| * | ||
| * @polymerMixin | ||
| * Subclasses provide their default values by overriding the | ||
| * `defaultI18n` static getter: | ||
| * | ||
| * ```js | ||
| * static get defaultI18n() { | ||
| * return { foo: 'Foo', bar: 'Bar' }; | ||
| * } | ||
| * ``` | ||
| */ | ||
| export const I18nMixin = (defaultI18n, superClass) => | ||
| export const I18nMixin = (superClass) => | ||
| class I18nMixinClass extends superClass { | ||
@@ -59,6 +66,16 @@ static get properties() { | ||
| /** | ||
| * Default I18N values. Must be overridden by subclasses with actual defaults. | ||
| * | ||
| * @protected | ||
| * @return {Object} | ||
| */ | ||
| static get defaultI18n() { | ||
| return {}; | ||
| } | ||
| constructor() { | ||
| super(); | ||
| this.i18n = deepMerge({}, defaultI18n); | ||
| this.i18n = deepMerge({}, this.constructor.defaultI18n); | ||
| } | ||
@@ -85,4 +102,4 @@ | ||
| this.__customI18n = value; | ||
| this.__effectiveI18n = deepMerge({}, defaultI18n, this.__customI18n); | ||
| this.__effectiveI18n = deepMerge({}, this.constructor.defaultI18n, this.__customI18n); | ||
| } | ||
| }; |
@@ -10,4 +10,2 @@ /** | ||
| * by setting the `overlayClass` property or `overlay-class` attribute. | ||
| * | ||
| * @polymerMixin | ||
| */ | ||
@@ -14,0 +12,0 @@ export const OverlayClassMixin = (superclass) => |
@@ -62,3 +62,3 @@ /** | ||
| if (options && options.reflectToAttribute) { | ||
| if (options?.reflectToAttribute) { | ||
| options.reflect = true; | ||
@@ -65,0 +65,0 @@ } |
+50
-52
@@ -29,67 +29,65 @@ /** | ||
| * A mixin that uses a ResizeObserver to listen to host size changes. | ||
| * | ||
| * @polymerMixin | ||
| */ | ||
| export const ResizeMixin = dedupeMixin( | ||
| (superclass) => | ||
| class ResizeMixinClass extends superclass { | ||
| /** | ||
| * When true, the parent element resize will be also observed. | ||
| * Override this getter and return `true` to enable this. | ||
| * | ||
| * @protected | ||
| */ | ||
| get _observeParent() { | ||
| return false; | ||
| } | ||
| const ResizeMixinImplementation = (superclass) => | ||
| class ResizeMixinClass extends superclass { | ||
| /** | ||
| * When true, the parent element resize will be also observed. | ||
| * Override this getter and return `true` to enable this. | ||
| * | ||
| * @protected | ||
| */ | ||
| get _observeParent() { | ||
| return false; | ||
| } | ||
| /** @protected */ | ||
| connectedCallback() { | ||
| super.connectedCallback(); | ||
| observer.observe(this); | ||
| /** @protected */ | ||
| connectedCallback() { | ||
| super.connectedCallback(); | ||
| observer.observe(this); | ||
| if (this._observeParent) { | ||
| const parent = this.parentNode instanceof ShadowRoot ? this.parentNode.host : this.parentNode; | ||
| if (this._observeParent) { | ||
| const parent = this.parentNode instanceof ShadowRoot ? this.parentNode.host : this.parentNode; | ||
| if (!parent.resizables) { | ||
| parent.resizables = new Set(); | ||
| observer.observe(parent); | ||
| } | ||
| if (!parent.resizables) { | ||
| parent.resizables = new Set(); | ||
| observer.observe(parent); | ||
| } | ||
| parent.resizables.add(this); | ||
| this.__parent = parent; | ||
| } | ||
| parent.resizables.add(this); | ||
| this.__parent = parent; | ||
| } | ||
| } | ||
| /** @protected */ | ||
| disconnectedCallback() { | ||
| super.disconnectedCallback(); | ||
| observer.unobserve(this); | ||
| /** @protected */ | ||
| disconnectedCallback() { | ||
| super.disconnectedCallback(); | ||
| observer.unobserve(this); | ||
| const parent = this.__parent; | ||
| if (this._observeParent && parent) { | ||
| const resizables = parent.resizables; | ||
| const parent = this.__parent; | ||
| if (this._observeParent && parent) { | ||
| const resizables = parent.resizables; | ||
| if (resizables) { | ||
| resizables.delete(this); | ||
| if (resizables) { | ||
| resizables.delete(this); | ||
| if (resizables.size === 0) { | ||
| observer.unobserve(parent); | ||
| } | ||
| if (resizables.size === 0) { | ||
| observer.unobserve(parent); | ||
| } | ||
| } | ||
| this.__parent = null; | ||
| } | ||
| this.__parent = null; | ||
| } | ||
| } | ||
| /** | ||
| * A handler invoked on host resize. By default, it does nothing. | ||
| * Override the method to implement your own behavior. | ||
| * | ||
| * @protected | ||
| */ | ||
| _onResize(_contentRect) { | ||
| // To be implemented. | ||
| } | ||
| }, | ||
| ); | ||
| /** | ||
| * A handler invoked on host resize. By default, it does nothing. | ||
| * Override the method to implement your own behavior. | ||
| * | ||
| * @protected | ||
| */ | ||
| _onResize(_contentRect) { | ||
| // To be implemented. | ||
| } | ||
| }; | ||
| export const ResizeMixin = dedupeMixin(ResizeMixinImplementation); |
@@ -226,3 +226,3 @@ /** | ||
| if (newNodes && newNodes.length > 0) { | ||
| if (newNodes?.length > 0) { | ||
| if (this.multiple) { | ||
@@ -229,0 +229,0 @@ // Remove default node if exists |
@@ -8,7 +8,15 @@ /** | ||
| /** | ||
| * A helper for observing slot changes. | ||
| * A helper for observing slot or shadow-root changes. | ||
| * | ||
| * When `target` is an `HTMLSlotElement`, the observer listens for `slotchange` | ||
| * on the slot itself and diffs `target.assignedNodes({ flatten: true })`. | ||
| * | ||
| * When `target` is a `ShadowRoot`, the observer listens for `slotchange` events | ||
| * bubbling to it and diffs the **union** of `assignedNodes({ flatten: true })` | ||
| * every descendant `<slot>`. Cross-slot reassignment of the same node does | ||
| * not change the union and therefore fires no callback. | ||
| */ | ||
| export class SlotObserver { | ||
| constructor( | ||
| slot: HTMLSlotElement, | ||
| target: HTMLSlotElement | DocumentFragment, | ||
| callback: (info: { addedNodes: Node[]; currentNodes: Node[]; movedNodes: Node[]; removedNodes: Node[] }) => void, | ||
@@ -18,2 +26,4 @@ forceInitial?: boolean, | ||
| readonly target: HTMLSlotElement | DocumentFragment; | ||
| /** | ||
@@ -20,0 +30,0 @@ * Activates an observer. This method is automatically called when |
+63
-23
@@ -8,13 +8,21 @@ /** | ||
| /** | ||
| * A helper for observing slot changes. | ||
| * A helper for observing slot or shadow-root changes. | ||
| * | ||
| * When `target` is an `HTMLSlotElement`, the observer listens for `slotchange` | ||
| * on the slot itself and diffs `target.assignedNodes({ flatten: true })`. | ||
| * | ||
| * When `target` is a `ShadowRoot`, the observer listens for `slotchange` events | ||
| * bubbling to it and diffs the **union** of `assignedNodes({ flatten: true })` | ||
| * across every descendant `<slot>`. Cross-slot reassignment of the same node | ||
| * does not change the union and therefore fires no callback. | ||
| */ | ||
| export class SlotObserver { | ||
| constructor(slot, callback, forceInitial) { | ||
| /** @type HTMLSlotElement */ | ||
| this.slot = slot; | ||
| constructor(target, callback, forceInitial) { | ||
| /** @type {HTMLSlotElement | DocumentFragment} */ | ||
| this.target = target; | ||
| /** @type Function */ | ||
| /** @type {Function} */ | ||
| this.callback = callback; | ||
| /** @type boolean */ | ||
| /** @type {boolean} */ | ||
| this.forceInitial = forceInitial; | ||
@@ -25,2 +33,5 @@ | ||
| /** @type {boolean} */ | ||
| this._isSlot = target instanceof HTMLSlotElement; | ||
| this._connected = false; | ||
@@ -43,3 +54,3 @@ this._scheduled = false; | ||
| connect() { | ||
| this.slot.addEventListener('slotchange', this._boundSchedule); | ||
| this.target.addEventListener('slotchange', this._boundSchedule); | ||
| this._connected = true; | ||
@@ -54,3 +65,3 @@ } | ||
| disconnect() { | ||
| this.slot.removeEventListener('slotchange', this._boundSchedule); | ||
| this.target.removeEventListener('slotchange', this._boundSchedule); | ||
| this._connected = false; | ||
@@ -84,24 +95,53 @@ } | ||
| /** @private */ | ||
| _processNodes() { | ||
| const currentNodes = this.slot.assignedNodes({ flatten: true }); | ||
| _collectNodes() { | ||
| const slots = this._isSlot ? [this.target] : [...this.target.querySelectorAll('slot')]; | ||
| return [...new Set(slots.flatMap((slot) => slot.assignedNodes({ flatten: true })))]; | ||
| } | ||
| let addedNodes = []; | ||
| const removedNodes = []; | ||
| /** @private */ | ||
| _groupNodesBySlot(nodes) { | ||
| const map = new Map(); | ||
| nodes.forEach((node) => { | ||
| const slot = node.assignedSlot; | ||
| map.set(slot, map.get(slot) ?? []); | ||
| map.get(slot).push(node); | ||
| }); | ||
| return map; | ||
| } | ||
| /** | ||
| * Collect moved nodes reordered within its current slot, | ||
| * but not those that are assigned to different slot. | ||
| * | ||
| * @private | ||
| */ | ||
| _collectMovedNodes(currentNodes) { | ||
| const currentPerSlot = this._groupNodesBySlot(currentNodes); | ||
| const storedPerSlot = this._groupNodesBySlot(this._storedNodes); | ||
| const movedNodes = []; | ||
| if (currentNodes.length) { | ||
| addedNodes = currentNodes.filter((node) => !this._storedNodes.includes(node)); | ||
| } | ||
| if (this._storedNodes.length) { | ||
| this._storedNodes.forEach((node, index) => { | ||
| const idx = currentNodes.indexOf(node); | ||
| if (idx === -1) { | ||
| removedNodes.push(node); | ||
| } else if (idx !== index) { | ||
| currentPerSlot.forEach((nodes, slot) => { | ||
| const stored = storedPerSlot.get(slot) || []; | ||
| // Skip slots whose membership changed: nodes entered or left the slot. | ||
| if (new Set(stored).difference(new Set(nodes)).size > 0) { | ||
| return; | ||
| } | ||
| stored.forEach((node, storedIndex) => { | ||
| if (nodes.indexOf(node) !== storedIndex) { | ||
| movedNodes.push(node); | ||
| } | ||
| }); | ||
| } | ||
| }); | ||
| return movedNodes; | ||
| } | ||
| /** @private */ | ||
| _processNodes() { | ||
| const currentNodes = this._collectNodes(); | ||
| const addedNodes = currentNodes.filter((node) => !this._storedNodes.includes(node)); | ||
| const removedNodes = this._storedNodes.filter((node) => !currentNodes.includes(node)); | ||
| const movedNodes = this._collectMovedNodes(currentNodes); | ||
| // By default, callback is not invoked if there is no child nodes in the slot. | ||
@@ -108,0 +148,0 @@ // Use `forceInitial` flag if needed to also invoke it for the initial state. |
+28
-30
@@ -42,36 +42,34 @@ /** | ||
| * This is useful e.g. to hide native `<input type="number">` controls. | ||
| * | ||
| * @polymerMixin | ||
| */ | ||
| export const SlotStylesMixin = dedupeMixin( | ||
| (superclass) => | ||
| class SlotStylesMixinClass extends superclass { | ||
| /** | ||
| * List of styles to insert into root. | ||
| * @protected | ||
| */ | ||
| get slotStyles() { | ||
| return []; | ||
| } | ||
| const SlotStylesMixinImplementation = (superclass) => | ||
| class SlotStylesMixinClass extends superclass { | ||
| /** | ||
| * List of styles to insert into root. | ||
| * @protected | ||
| */ | ||
| get slotStyles() { | ||
| return []; | ||
| } | ||
| /** @protected */ | ||
| connectedCallback() { | ||
| super.connectedCallback(); | ||
| /** @protected */ | ||
| connectedCallback() { | ||
| super.connectedCallback(); | ||
| this.__applySlotStyles(); | ||
| } | ||
| this.__applySlotStyles(); | ||
| } | ||
| /** @private */ | ||
| __applySlotStyles() { | ||
| const root = this.getRootNode(); | ||
| const rootStyles = getRootStyles(root); | ||
| /** @private */ | ||
| __applySlotStyles() { | ||
| const root = this.getRootNode(); | ||
| const rootStyles = getRootStyles(root); | ||
| this.slotStyles.forEach((styles) => { | ||
| if (!rootStyles.has(styles)) { | ||
| insertStyles(styles, root); | ||
| rootStyles.add(styles); | ||
| } | ||
| }); | ||
| } | ||
| }, | ||
| ); | ||
| this.slotStyles.forEach((styles) => { | ||
| if (!rootStyles.has(styles)) { | ||
| insertStyles(styles, root); | ||
| rootStyles.add(styles); | ||
| } | ||
| }); | ||
| } | ||
| }; | ||
| export const SlotStylesMixin = dedupeMixin(SlotStylesMixinImplementation); |
@@ -25,5 +25,3 @@ /** | ||
| inherits: true, | ||
| // Use this initial value so the color stays visible when the property | ||
| // is set to an invalid value to make debugging a bit easier. | ||
| initialValue: 'light-dark(black, white)', | ||
| initialValue: 'transparent', | ||
| }); | ||
@@ -90,2 +88,3 @@ }); | ||
| --_vaadin-icon-chevron-down: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m6 9 6 6 6-6"/></svg>'); | ||
| --_vaadin-icon-chevron-right: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m9 18 6-6-6-6"/></svg>'); | ||
| --_vaadin-icon-clock: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 6v6l4 2"/><circle cx="12" cy="12" r="10"/></svg>'); | ||
@@ -92,0 +91,0 @@ --_vaadin-icon-cross: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M6 18 18 6M6 6l12 12" /></svg>'); |
@@ -26,2 +26,4 @@ /** | ||
| export class TooltipController extends SlotController { | ||
| constructor(host: HTMLElement); | ||
| /** | ||
@@ -47,8 +49,2 @@ * An HTML element for linking with the tooltip overlay | ||
| /** | ||
| * When true, the tooltip is opened programmatically. | ||
| * Only works if `manual` is set to `true`. | ||
| */ | ||
| opened: boolean; | ||
| /** | ||
| * Position of the tooltip with respect to its target. | ||
@@ -80,7 +76,2 @@ */ | ||
| /** | ||
| * Toggle opened state on the slotted tooltip. | ||
| */ | ||
| setOpened(opened: boolean): void; | ||
| /** | ||
| * Set default position for the slotted tooltip. | ||
@@ -102,2 +93,16 @@ * This can be overridden by setting the position | ||
| setTarget(target: HTMLElement): void; | ||
| /** | ||
| * Schedule opening the slotted tooltip. Respects the tooltip's | ||
| * configured `hoverDelay` / `focusDelay` and the shared warm-up state. | ||
| * No-op when no tooltip is slotted. | ||
| */ | ||
| open(options?: { hover?: boolean; focus?: boolean; immediate?: boolean }): void; | ||
| /** | ||
| * Schedule closing the slotted tooltip. Respects the tooltip's | ||
| * configured `hideDelay` unless `immediate` is true. | ||
| * No-op when no tooltip is slotted. | ||
| */ | ||
| close(immediate?: boolean): void; | ||
| } |
@@ -42,6 +42,2 @@ /** | ||
| if (this.opened !== undefined) { | ||
| tooltipNode.opened = this.opened; | ||
| } | ||
| if (this.position !== undefined) { | ||
@@ -118,15 +114,2 @@ tooltipNode._position = this.position; | ||
| /** | ||
| * Toggle opened state on the slotted tooltip. | ||
| * @param {boolean} opened | ||
| */ | ||
| setOpened(opened) { | ||
| this.opened = opened; | ||
| const tooltipNode = this.node; | ||
| if (tooltipNode) { | ||
| tooltipNode.opened = opened; | ||
| } | ||
| } | ||
| /** | ||
| * Set default position for the slotted tooltip. | ||
@@ -173,2 +156,30 @@ * This can be overridden by setting the position | ||
| /** | ||
| * Schedule opening the slotted tooltip. Respects the tooltip's | ||
| * configured `hoverDelay` / `focusDelay` and the shared warm-up state. | ||
| * No-op when no tooltip is slotted. | ||
| * | ||
| * @param {{ hover?: boolean, focus?: boolean, immediate?: boolean }} [options] | ||
| */ | ||
| open(options) { | ||
| const tooltipNode = this.node; | ||
| if (tooltipNode?.isConnected) { | ||
| tooltipNode._stateController.open(options); | ||
| } | ||
| } | ||
| /** | ||
| * Schedule closing the slotted tooltip. Respects the tooltip's | ||
| * configured `hideDelay` unless `immediate` is true. | ||
| * No-op when no tooltip is slotted. | ||
| * | ||
| * @param {boolean} [immediate] | ||
| */ | ||
| close(immediate) { | ||
| const tooltipNode = this.node; | ||
| if (tooltipNode) { | ||
| tooltipNode._stateController.close(immediate); | ||
| } | ||
| } | ||
| /** @private */ | ||
@@ -175,0 +186,0 @@ __onContentChange(event) { |
@@ -518,7 +518,15 @@ /** | ||
| __getFocusedElement(visibleElements = this.__getVisibleElements()) { | ||
| return visibleElements.find( | ||
| (element) => | ||
| element.contains(this.elementsContainer.getRootNode().activeElement) || | ||
| element.contains(this.scrollTarget.getRootNode().activeElement), | ||
| ); | ||
| // `document.activeElement` retargets to the outermost shadow host when | ||
| // focus lives in a nested shadow tree. Descend through nested shadow | ||
| // roots' `activeElement`s to reach the real focused node, then walk up | ||
| // the flattened tree (via `assignedSlot`/`parentNode`/`host`) until a | ||
| // visible row is reached. | ||
| let node = document.activeElement; | ||
| while (node?.shadowRoot?.activeElement) { | ||
| node = node.shadowRoot.activeElement; | ||
| } | ||
| while (node && !visibleElements.includes(node)) { | ||
| node = node.assignedSlot || node.parentNode || node.host; | ||
| } | ||
| return node; | ||
| } | ||
@@ -525,0 +533,0 @@ |
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
252420
1.67%7152
1.35%13
-7.14%