From ac0c3760b40fcf9099611dc96c8b7627edf5f98a Mon Sep 17 00:00:00 2001 From: Brian Ferry Date: Mon, 4 Dec 2023 12:46:43 -0500 Subject: [PATCH] feat(core): adding isEmpty, hasSlotted w/ default slot (#2604) * feat(core): adding isEmpty, hasSlotted w/ default slot * chore: adding changeset --- .changeset/cold-cars-relate.md | 6 ++ core/pfe-core/controllers/slot-controller.ts | 59 ++++++++++++-------- 2 files changed, 42 insertions(+), 23 deletions(-) create mode 100644 .changeset/cold-cars-relate.md diff --git a/.changeset/cold-cars-relate.md b/.changeset/cold-cars-relate.md new file mode 100644 index 0000000000..21b638d22c --- /dev/null +++ b/.changeset/cold-cars-relate.md @@ -0,0 +1,6 @@ +--- +"@patternfly/pfe-core": minor +--- + +`SlotController`: Add `isEmpty` method to check if a slot is empty. If no slot name is provided it will check the default slot. (#2603) +`SlotController`: `hasSlotted` method now returns default slot if no slot name is provided. (#2603) diff --git a/core/pfe-core/controllers/slot-controller.ts b/core/pfe-core/controllers/slot-controller.ts index d5ef657784..2902adf2c3 100644 --- a/core/pfe-core/controllers/slot-controller.ts +++ b/core/pfe-core/controllers/slot-controller.ts @@ -41,15 +41,17 @@ function isObjectConfigSpread(config: ([SlotsConfig] | (string | null)[])): conf * for the default slot, look for direct children not assigned to a slot */ const isSlot = - (n: string | typeof SlotController.anonymous) => + (n: string | typeof SlotController.default) => (child: Element): child is T => - n === SlotController.anonymous ? !child.hasAttribute('slot') + n === SlotController.default ? !child.hasAttribute('slot') : child.getAttribute('slot') === n; export class SlotController implements ReactiveController { - public static anonymous = Symbol('anonymous slot'); + public static default = Symbol('default slot'); + /** @deprecated use `default` */ + public static anonymous = this.default; - #nodes = new Map(); + #nodes = new Map(); #logger: Logger; @@ -105,22 +107,6 @@ export class SlotController implements ReactiveController { this.#mo.disconnect(); } - /** - * Returns a boolean statement of whether or not any of those slots exists in the light DOM. - * - * @param {String|Array} name The slot name. - * @example this.hasSlotted("header"); - */ - hasSlotted(...names: string[]): boolean { - if (!names.length) { - this.#logger.warn(`Please provide at least one slot name for which to search.`); - return false; - } else { - return names.some(x => - this.#nodes.get(x)?.hasContent ?? false); - } - } - /** * Given a slot name or slot names, returns elements assigned to the requested slots as an array. * If no value is provided, it returns all children not assigned to a slot (without a slot attribute). @@ -142,13 +128,40 @@ export class SlotController implements ReactiveController { */ getSlotted(...slotNames: string[]): T[] { if (!slotNames.length) { - return (this.#nodes.get(SlotController.anonymous)?.elements ?? []) as T[]; + return (this.#nodes.get(SlotController.default)?.elements ?? []) as T[]; } else { return slotNames.flatMap(slotName => this.#nodes.get(slotName)?.elements ?? []) as T[]; } } + /** + * Returns a boolean statement of whether or not any of those slots exists in the light DOM. + * + * @param names The slot names to check. + * @example this.hasSlotted('header'); + */ + hasSlotted(...names: (string | null | undefined)[]): boolean { + const { anonymous } = SlotController; + const slotNames = Array.from(names, x => x == null ? anonymous : x); + if (!slotNames.length) { + slotNames.push(anonymous); + } + return slotNames.some(x => this.#nodes.get(x)?.hasContent ?? false); + } + + /** + * Whether or not all the requested slots are empty. + * + * @param slots The slot name. If no value is provided, it returns the default slot. + * @example this.isEmpty('header', 'footer'); + * @example this.isEmpty(); + * @returns {Boolean} + */ + isEmpty(...names: (string | null | undefined)[]): boolean { + return !this.hasSlotted(...names); + } + #onSlotChange = (event: Event & { target: HTMLSlotElement }) => { const slotName = event.target.name; this.#initSlot(slotName); @@ -168,13 +181,13 @@ export class SlotController implements ReactiveController { this.host.requestUpdate(); }; - #getChildrenForSlot(name: string | typeof SlotController.anonymous): T[] { + #getChildrenForSlot(name: string | typeof SlotController.default): T[] { const children = Array.from(this.host.children) as T[]; return children.filter(isSlot(name)); } #initSlot = (slotName: string | null) => { - const name = slotName || SlotController.anonymous; + const name = slotName || SlotController.default; const elements = this.#nodes.get(name)?.slot?.assignedElements?.() ?? this.#getChildrenForSlot(name); const selector = slotName ? `slot[name="${slotName}"]` : 'slot:not([name])'; const slot = this.host.shadowRoot?.querySelector?.(selector) ?? null;