Skip to content

Commit ac0c376

Browse files
authored
feat(core): adding isEmpty, hasSlotted w/ default slot (#2604)
* feat(core): adding isEmpty, hasSlotted w/ default slot * chore: adding changeset
1 parent 3d7ce5a commit ac0c376

File tree

2 files changed

+42
-23
lines changed

2 files changed

+42
-23
lines changed

.changeset/cold-cars-relate.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"@patternfly/pfe-core": minor
3+
---
4+
5+
`SlotController`: Add `isEmpty` method to check if a slot is empty. If no slot name is provided it will check the default slot. (#2603)
6+
`SlotController`: `hasSlotted` method now returns default slot if no slot name is provided. (#2603)

core/pfe-core/controllers/slot-controller.ts

Lines changed: 36 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -41,15 +41,17 @@ function isObjectConfigSpread(config: ([SlotsConfig] | (string | null)[])): conf
4141
* for the default slot, look for direct children not assigned to a slot
4242
*/
4343
const isSlot =
44-
<T extends Element = Element>(n: string | typeof SlotController.anonymous) =>
44+
<T extends Element = Element>(n: string | typeof SlotController.default) =>
4545
(child: Element): child is T =>
46-
n === SlotController.anonymous ? !child.hasAttribute('slot')
46+
n === SlotController.default ? !child.hasAttribute('slot')
4747
: child.getAttribute('slot') === n;
4848

4949
export class SlotController implements ReactiveController {
50-
public static anonymous = Symbol('anonymous slot');
50+
public static default = Symbol('default slot');
51+
/** @deprecated use `default` */
52+
public static anonymous = this.default;
5153

52-
#nodes = new Map<string | typeof SlotController.anonymous, Slot>();
54+
#nodes = new Map<string | typeof SlotController.default, Slot>();
5355

5456
#logger: Logger;
5557

@@ -105,22 +107,6 @@ export class SlotController implements ReactiveController {
105107
this.#mo.disconnect();
106108
}
107109

108-
/**
109-
* Returns a boolean statement of whether or not any of those slots exists in the light DOM.
110-
*
111-
* @param {String|Array} name The slot name.
112-
* @example this.hasSlotted("header");
113-
*/
114-
hasSlotted(...names: string[]): boolean {
115-
if (!names.length) {
116-
this.#logger.warn(`Please provide at least one slot name for which to search.`);
117-
return false;
118-
} else {
119-
return names.some(x =>
120-
this.#nodes.get(x)?.hasContent ?? false);
121-
}
122-
}
123-
124110
/**
125111
* Given a slot name or slot names, returns elements assigned to the requested slots as an array.
126112
* 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 {
142128
*/
143129
getSlotted<T extends Element = Element>(...slotNames: string[]): T[] {
144130
if (!slotNames.length) {
145-
return (this.#nodes.get(SlotController.anonymous)?.elements ?? []) as T[];
131+
return (this.#nodes.get(SlotController.default)?.elements ?? []) as T[];
146132
} else {
147133
return slotNames.flatMap(slotName =>
148134
this.#nodes.get(slotName)?.elements ?? []) as T[];
149135
}
150136
}
151137

138+
/**
139+
* Returns a boolean statement of whether or not any of those slots exists in the light DOM.
140+
*
141+
* @param names The slot names to check.
142+
* @example this.hasSlotted('header');
143+
*/
144+
hasSlotted(...names: (string | null | undefined)[]): boolean {
145+
const { anonymous } = SlotController;
146+
const slotNames = Array.from(names, x => x == null ? anonymous : x);
147+
if (!slotNames.length) {
148+
slotNames.push(anonymous);
149+
}
150+
return slotNames.some(x => this.#nodes.get(x)?.hasContent ?? false);
151+
}
152+
153+
/**
154+
* Whether or not all the requested slots are empty.
155+
*
156+
* @param slots The slot name. If no value is provided, it returns the default slot.
157+
* @example this.isEmpty('header', 'footer');
158+
* @example this.isEmpty();
159+
* @returns {Boolean}
160+
*/
161+
isEmpty(...names: (string | null | undefined)[]): boolean {
162+
return !this.hasSlotted(...names);
163+
}
164+
152165
#onSlotChange = (event: Event & { target: HTMLSlotElement }) => {
153166
const slotName = event.target.name;
154167
this.#initSlot(slotName);
@@ -168,13 +181,13 @@ export class SlotController implements ReactiveController {
168181
this.host.requestUpdate();
169182
};
170183

171-
#getChildrenForSlot<T extends Element = Element>(name: string | typeof SlotController.anonymous): T[] {
184+
#getChildrenForSlot<T extends Element = Element>(name: string | typeof SlotController.default): T[] {
172185
const children = Array.from(this.host.children) as T[];
173186
return children.filter(isSlot(name));
174187
}
175188

176189
#initSlot = (slotName: string | null) => {
177-
const name = slotName || SlotController.anonymous;
190+
const name = slotName || SlotController.default;
178191
const elements = this.#nodes.get(name)?.slot?.assignedElements?.() ?? this.#getChildrenForSlot(name);
179192
const selector = slotName ? `slot[name="${slotName}"]` : 'slot:not([name])';
180193
const slot = this.host.shadowRoot?.querySelector?.<HTMLSlotElement>(selector) ?? null;

0 commit comments

Comments
 (0)