@patternfly/pfe-core 2.0.0 → 2.1.0

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.
@@ -43,6 +43,13 @@ export declare class RovingTabindexController implements ReactiveController {
43
43
  /**
44
44
  * from array of HTML items, and sets active items
45
45
  */
46
- initItems(items: HTMLElement[]): void;
46
+ initItems(items: HTMLElement[], itemsContainer?: HTMLElement): void;
47
+ /**
48
+ * adds event listners to items container
49
+ */
47
50
  hostConnected(): void;
51
+ /**
52
+ * removes event listners from items container
53
+ */
54
+ hostDisconnected(): void;
48
55
  }
@@ -1,4 +1,4 @@
1
- var _RovingTabindexController_instances, _RovingTabindexController_activeItem, _RovingTabindexController_items, _RovingTabindexController_focusableItems_get, _RovingTabindexController_activeIndex_get, _RovingTabindexController_itemIndex_get, _RovingTabindexController_onKeydown;
1
+ var _RovingTabindexController_instances, _RovingTabindexController_activeItem, _RovingTabindexController_itemsContainer, _RovingTabindexController_items, _RovingTabindexController_focusableItems_get, _RovingTabindexController_activeIndex_get, _RovingTabindexController_itemIndex_get, _RovingTabindexController_onKeydown;
2
2
  import { __classPrivateFieldGet, __classPrivateFieldSet } from "tslib";
3
3
  const isFocusableElement = (el) => !!el &&
4
4
  !el.hasAttribute('disabled') &&
@@ -47,8 +47,75 @@ export class RovingTabindexController {
47
47
  _RovingTabindexController_instances.add(this);
48
48
  /** active focusable element */
49
49
  _RovingTabindexController_activeItem.set(this, void 0);
50
+ /** closest ancestor containing items */
51
+ _RovingTabindexController_itemsContainer.set(this, void 0);
50
52
  /** array of all focusable elements */
51
53
  _RovingTabindexController_items.set(this, []);
54
+ /**
55
+ * handles keyboard navigation
56
+ */
57
+ _RovingTabindexController_onKeydown.set(this, (event) => {
58
+ if (event.ctrlKey || event.altKey || event.metaKey || __classPrivateFieldGet(this, _RovingTabindexController_instances, "a", _RovingTabindexController_focusableItems_get).length < 1) {
59
+ return;
60
+ }
61
+ const item = this.activeItem;
62
+ let shouldPreventDefault = false;
63
+ const horizontalOnly = !item ? false
64
+ : item.tagName === 'SELECT' ||
65
+ item.getAttribute('role') === 'spinbutton';
66
+ switch (event.key) {
67
+ case 'ArrowLeft':
68
+ this.focusOnItem(this.prevItem);
69
+ shouldPreventDefault = true;
70
+ break;
71
+ case 'ArrowRight':
72
+ this.focusOnItem(this.nextItem);
73
+ shouldPreventDefault = true;
74
+ break;
75
+ case 'ArrowUp':
76
+ if (horizontalOnly) {
77
+ return;
78
+ }
79
+ this.focusOnItem(this.prevItem);
80
+ shouldPreventDefault = true;
81
+ break;
82
+ case 'ArrowDown':
83
+ if (horizontalOnly) {
84
+ return;
85
+ }
86
+ this.focusOnItem(this.nextItem);
87
+ shouldPreventDefault = true;
88
+ break;
89
+ case 'Home':
90
+ this.focusOnItem(this.firstItem);
91
+ shouldPreventDefault = true;
92
+ break;
93
+ case 'PageUp':
94
+ if (horizontalOnly) {
95
+ return;
96
+ }
97
+ this.focusOnItem(this.firstItem);
98
+ shouldPreventDefault = true;
99
+ break;
100
+ case 'End':
101
+ this.focusOnItem(this.lastItem);
102
+ shouldPreventDefault = true;
103
+ break;
104
+ case 'PageDown':
105
+ if (horizontalOnly) {
106
+ return;
107
+ }
108
+ this.focusOnItem(this.lastItem);
109
+ shouldPreventDefault = true;
110
+ break;
111
+ default:
112
+ break;
113
+ }
114
+ if (shouldPreventDefault) {
115
+ event.stopPropagation();
116
+ event.preventDefault();
117
+ }
118
+ });
52
119
  this.host.addController(this);
53
120
  }
54
121
  /**
@@ -81,7 +148,7 @@ export class RovingTabindexController {
81
148
  /**
82
149
  * from array of HTML items, and sets active items
83
150
  */
84
- initItems(items) {
151
+ initItems(items, itemsContainer = this.host) {
85
152
  __classPrivateFieldSet(this, _RovingTabindexController_items, items ?? [], "f");
86
153
  const focusableItems = __classPrivateFieldGet(this, _RovingTabindexController_instances, "a", _RovingTabindexController_focusableItems_get);
87
154
  const [focusableItem] = focusableItems;
@@ -89,78 +156,33 @@ export class RovingTabindexController {
89
156
  for (const item of focusableItems) {
90
157
  item.tabIndex = __classPrivateFieldGet(this, _RovingTabindexController_activeItem, "f") === item ? 0 : -1;
91
158
  }
159
+ /**
160
+ * removes listener on previous contained and applies it to new container
161
+ */
162
+ if (!__classPrivateFieldGet(this, _RovingTabindexController_itemsContainer, "f") || itemsContainer !== __classPrivateFieldGet(this, _RovingTabindexController_itemsContainer, "f")) {
163
+ __classPrivateFieldGet(this, _RovingTabindexController_itemsContainer, "f")?.removeEventListener('keydown', __classPrivateFieldGet(this, _RovingTabindexController_onKeydown, "f"));
164
+ __classPrivateFieldSet(this, _RovingTabindexController_itemsContainer, itemsContainer, "f");
165
+ this.hostConnected();
166
+ }
92
167
  }
168
+ /**
169
+ * adds event listners to items container
170
+ */
93
171
  hostConnected() {
94
- this.host.addEventListener('keydown', __classPrivateFieldGet(this, _RovingTabindexController_instances, "m", _RovingTabindexController_onKeydown).bind(this));
172
+ __classPrivateFieldGet(this, _RovingTabindexController_itemsContainer, "f")?.addEventListener('keydown', __classPrivateFieldGet(this, _RovingTabindexController_onKeydown, "f"));
173
+ }
174
+ /**
175
+ * removes event listners from items container
176
+ */
177
+ hostDisconnected() {
178
+ __classPrivateFieldGet(this, _RovingTabindexController_itemsContainer, "f")?.removeEventListener('keydown', __classPrivateFieldGet(this, _RovingTabindexController_onKeydown, "f"));
95
179
  }
96
180
  }
97
- _RovingTabindexController_activeItem = new WeakMap(), _RovingTabindexController_items = new WeakMap(), _RovingTabindexController_instances = new WeakSet(), _RovingTabindexController_focusableItems_get = function _RovingTabindexController_focusableItems_get() {
181
+ _RovingTabindexController_activeItem = new WeakMap(), _RovingTabindexController_itemsContainer = new WeakMap(), _RovingTabindexController_items = new WeakMap(), _RovingTabindexController_onKeydown = new WeakMap(), _RovingTabindexController_instances = new WeakSet(), _RovingTabindexController_focusableItems_get = function _RovingTabindexController_focusableItems_get() {
98
182
  return __classPrivateFieldGet(this, _RovingTabindexController_items, "f").filter(isFocusableElement);
99
183
  }, _RovingTabindexController_activeIndex_get = function _RovingTabindexController_activeIndex_get() {
100
184
  return !!__classPrivateFieldGet(this, _RovingTabindexController_instances, "a", _RovingTabindexController_focusableItems_get) && !!this.activeItem ? __classPrivateFieldGet(this, _RovingTabindexController_instances, "a", _RovingTabindexController_focusableItems_get).indexOf(this.activeItem) : -1;
101
185
  }, _RovingTabindexController_itemIndex_get = function _RovingTabindexController_itemIndex_get() {
102
186
  return this.activeItem ? __classPrivateFieldGet(this, _RovingTabindexController_items, "f").indexOf(this.activeItem) : -1;
103
- }, _RovingTabindexController_onKeydown = function _RovingTabindexController_onKeydown(event) {
104
- if (event.ctrlKey || event.altKey || event.metaKey || __classPrivateFieldGet(this, _RovingTabindexController_instances, "a", _RovingTabindexController_focusableItems_get).length < 1) {
105
- return;
106
- }
107
- const item = this.activeItem;
108
- let shouldPreventDefault = false;
109
- const horizontalOnly = !item ? false
110
- : item.tagName === 'SELECT' ||
111
- item.getAttribute('aria-expanded') === 'true' ||
112
- item.getAttribute('role') === 'spinbutton';
113
- switch (event.key) {
114
- case 'ArrowLeft':
115
- this.focusOnItem(this.prevItem);
116
- shouldPreventDefault = true;
117
- break;
118
- case 'ArrowRight':
119
- this.focusOnItem(this.nextItem);
120
- shouldPreventDefault = true;
121
- break;
122
- case 'ArrowUp':
123
- if (horizontalOnly) {
124
- return;
125
- }
126
- this.focusOnItem(this.prevItem);
127
- shouldPreventDefault = true;
128
- break;
129
- case 'ArrowDown':
130
- if (horizontalOnly) {
131
- return;
132
- }
133
- this.focusOnItem(this.nextItem);
134
- shouldPreventDefault = true;
135
- break;
136
- case 'Home':
137
- this.focusOnItem(this.firstItem);
138
- shouldPreventDefault = true;
139
- break;
140
- case 'PageUp':
141
- if (horizontalOnly) {
142
- return;
143
- }
144
- this.focusOnItem(this.firstItem);
145
- shouldPreventDefault = true;
146
- break;
147
- case 'End':
148
- this.focusOnItem(this.lastItem);
149
- shouldPreventDefault = true;
150
- break;
151
- case 'PageDown':
152
- if (horizontalOnly) {
153
- return;
154
- }
155
- this.focusOnItem(this.lastItem);
156
- shouldPreventDefault = true;
157
- break;
158
- default:
159
- break;
160
- }
161
- if (shouldPreventDefault) {
162
- event.stopPropagation();
163
- event.preventDefault();
164
- }
165
187
  };
166
188
  //# sourceMappingURL=roving-tabindex-controller.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"roving-tabindex-controller.js","sourceRoot":"","sources":["roving-tabindex-controller.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ReactiveController, ReactiveControllerHost } from 'lit';\n\nconst isFocusableElement = (el: Element): el is HTMLElement =>\n !!el &&\n !el.hasAttribute('disabled') &&\n !el.ariaHidden &&\n !el.hasAttribute('hidden');\n\n/**\n * Implements roving tabindex, as described in WAI-ARIA practices, [Managing Focus Within\n * Components Using a Roving\n * tabindex](https://www.w3.org/WAI/ARIA/apg/practices/keyboard-interface/#kbd_roving_tabindex)\n */\nexport class RovingTabindexController implements ReactiveController {\n /** active focusable element */\n #activeItem?: HTMLElement;\n\n /** array of all focusable elements */\n #items: HTMLElement[] = [];\n\n /**\n * finds focusable items from a group of items\n */\n get #focusableItems(): HTMLElement[] {\n return this.#items.filter(isFocusableElement);\n }\n\n /**\n * index of active item in array of focusable items\n */\n get #activeIndex(): number {\n return !!this.#focusableItems && !!this.activeItem ? this.#focusableItems.indexOf(this.activeItem) : -1;\n }\n\n /**\n * index of active item in array of items\n */\n get #itemIndex(): number {\n return this.activeItem ? this.#items.indexOf(this.activeItem) : -1;\n }\n\n /**\n * active item of array of items\n */\n get activeItem(): HTMLElement | undefined {\n return this.#activeItem;\n }\n\n /**\n * first item in array of focusable items\n */\n get firstItem(): HTMLElement | undefined {\n return this.#focusableItems[0];\n }\n\n /**\n * last item in array of focusable items\n */\n get lastItem(): HTMLElement | undefined {\n return this.#focusableItems.at(-1);\n }\n\n /**\n * next item after active item in array of focusable items\n */\n get nextItem(): HTMLElement | undefined {\n return (\n this.#activeIndex < this.#focusableItems.length - 1 ? this.#focusableItems[this.#activeIndex + 1]\n : this.firstItem\n );\n }\n\n /**\n * previous item after active item in array of focusable items\n */\n get prevItem(): HTMLElement | undefined {\n return (\n this.#activeIndex > 0 ? this.#focusableItems[this.#activeIndex - 1]\n : this.lastItem\n );\n }\n\n constructor(public host: ReactiveControllerHost & HTMLElement) {\n this.host.addController(this);\n }\n\n /**\n * handles keyboard navigation\n */\n #onKeydown(event: KeyboardEvent):void {\n if (event.ctrlKey || event.altKey || event.metaKey || this.#focusableItems.length < 1) {\n return;\n }\n\n const item = this.activeItem;\n let shouldPreventDefault = false;\n const horizontalOnly =\n !item ? false\n : item.tagName === 'SELECT' ||\n item.getAttribute('aria-expanded') === 'true' ||\n item.getAttribute('role') === 'spinbutton';\n\n switch (event.key) {\n case 'ArrowLeft':\n this.focusOnItem(this.prevItem);\n shouldPreventDefault = true;\n break;\n case 'ArrowRight':\n this.focusOnItem(this.nextItem);\n shouldPreventDefault = true;\n break;\n case 'ArrowUp':\n if (horizontalOnly) {\n return;\n }\n this.focusOnItem(this.prevItem);\n shouldPreventDefault = true;\n break;\n case 'ArrowDown':\n if (horizontalOnly) {\n return;\n }\n this.focusOnItem(this.nextItem);\n shouldPreventDefault = true;\n break;\n case 'Home':\n this.focusOnItem(this.firstItem);\n shouldPreventDefault = true;\n break;\n case 'PageUp':\n if (horizontalOnly) {\n return;\n }\n this.focusOnItem(this.firstItem);\n shouldPreventDefault = true;\n break;\n case 'End':\n this.focusOnItem(this.lastItem);\n shouldPreventDefault = true;\n break;\n case 'PageDown':\n if (horizontalOnly) {\n return;\n }\n this.focusOnItem(this.lastItem);\n shouldPreventDefault = true;\n break;\n default:\n break;\n }\n\n if (shouldPreventDefault) {\n event.stopPropagation();\n event.preventDefault();\n }\n }\n\n /**\n * sets tabindex of item based on whether or not it is active\n */\n updateActiveItem(item?: HTMLElement):void {\n if (item) {\n if (!!this.#activeItem && item !== this.#activeItem) {\n this.#activeItem.tabIndex = -1;\n }\n item.tabIndex = 0;\n this.#activeItem = item;\n }\n }\n\n /**\n * focuses on an item and sets it as active\n */\n focusOnItem(item?: HTMLElement):void {\n this.updateActiveItem(item || this.firstItem);\n this.#activeItem?.focus();\n }\n\n /**\n * Focuses next focusable item\n */\n updateItems(items: HTMLElement[]) {\n const sequence = [...items.slice(this.#itemIndex), ...items.slice(0, this.#itemIndex)];\n const first = sequence.find(item => this.#focusableItems.includes(item));\n this.focusOnItem(first || this.firstItem);\n }\n\n /**\n * from array of HTML items, and sets active items\n */\n initItems(items: HTMLElement[]) {\n this.#items = items ?? [];\n const focusableItems = this.#focusableItems;\n const [focusableItem] = focusableItems;\n this.#activeItem = focusableItem;\n for (const item of focusableItems) {\n item.tabIndex = this.#activeItem === item ? 0 : -1;\n }\n }\n\n hostConnected() {\n this.host.addEventListener('keydown', this.#onKeydown.bind(this));\n }\n}\n"]}
1
+ {"version":3,"file":"roving-tabindex-controller.js","sourceRoot":"","sources":["roving-tabindex-controller.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ReactiveController, ReactiveControllerHost } from 'lit';\n\nconst isFocusableElement = (el: Element): el is HTMLElement =>\n !!el &&\n !el.hasAttribute('disabled') &&\n !el.ariaHidden &&\n !el.hasAttribute('hidden');\n\n/**\n * Implements roving tabindex, as described in WAI-ARIA practices, [Managing Focus Within\n * Components Using a Roving\n * tabindex](https://www.w3.org/WAI/ARIA/apg/practices/keyboard-interface/#kbd_roving_tabindex)\n */\nexport class RovingTabindexController implements ReactiveController {\n /** active focusable element */\n #activeItem?: HTMLElement;\n\n /** closest ancestor containing items */\n #itemsContainer?: HTMLElement;\n\n /** array of all focusable elements */\n #items: HTMLElement[] = [];\n\n /**\n * finds focusable items from a group of items\n */\n get #focusableItems(): HTMLElement[] {\n return this.#items.filter(isFocusableElement);\n }\n\n /**\n * index of active item in array of focusable items\n */\n get #activeIndex(): number {\n return !!this.#focusableItems && !!this.activeItem ? this.#focusableItems.indexOf(this.activeItem) : -1;\n }\n\n /**\n * index of active item in array of items\n */\n get #itemIndex(): number {\n return this.activeItem ? this.#items.indexOf(this.activeItem) : -1;\n }\n\n /**\n * active item of array of items\n */\n get activeItem(): HTMLElement | undefined {\n return this.#activeItem;\n }\n\n /**\n * first item in array of focusable items\n */\n get firstItem(): HTMLElement | undefined {\n return this.#focusableItems[0];\n }\n\n /**\n * last item in array of focusable items\n */\n get lastItem(): HTMLElement | undefined {\n return this.#focusableItems.at(-1);\n }\n\n /**\n * next item after active item in array of focusable items\n */\n get nextItem(): HTMLElement | undefined {\n return (\n this.#activeIndex < this.#focusableItems.length - 1 ? this.#focusableItems[this.#activeIndex + 1]\n : this.firstItem\n );\n }\n\n /**\n * previous item after active item in array of focusable items\n */\n get prevItem(): HTMLElement | undefined {\n return (\n this.#activeIndex > 0 ? this.#focusableItems[this.#activeIndex - 1]\n : this.lastItem\n );\n }\n\n constructor(public host: ReactiveControllerHost & HTMLElement) {\n this.host.addController(this);\n }\n\n /**\n * handles keyboard navigation\n */\n #onKeydown = (event: KeyboardEvent) => {\n if (event.ctrlKey || event.altKey || event.metaKey || this.#focusableItems.length < 1) {\n return;\n }\n\n const item = this.activeItem;\n let shouldPreventDefault = false;\n const horizontalOnly =\n !item ? false\n : item.tagName === 'SELECT' ||\n item.getAttribute('role') === 'spinbutton';\n\n\n switch (event.key) {\n case 'ArrowLeft':\n this.focusOnItem(this.prevItem);\n shouldPreventDefault = true;\n break;\n case 'ArrowRight':\n this.focusOnItem(this.nextItem);\n shouldPreventDefault = true;\n break;\n case 'ArrowUp':\n if (horizontalOnly) {\n return;\n }\n this.focusOnItem(this.prevItem);\n shouldPreventDefault = true;\n break;\n case 'ArrowDown':\n if (horizontalOnly) {\n return;\n }\n this.focusOnItem(this.nextItem);\n shouldPreventDefault = true;\n break;\n case 'Home':\n this.focusOnItem(this.firstItem);\n shouldPreventDefault = true;\n break;\n case 'PageUp':\n if (horizontalOnly) {\n return;\n }\n this.focusOnItem(this.firstItem);\n shouldPreventDefault = true;\n break;\n case 'End':\n this.focusOnItem(this.lastItem);\n shouldPreventDefault = true;\n break;\n case 'PageDown':\n if (horizontalOnly) {\n return;\n }\n this.focusOnItem(this.lastItem);\n shouldPreventDefault = true;\n break;\n default:\n break;\n }\n\n if (shouldPreventDefault) {\n event.stopPropagation();\n event.preventDefault();\n }\n };\n\n /**\n * sets tabindex of item based on whether or not it is active\n */\n updateActiveItem(item?: HTMLElement):void {\n if (item) {\n if (!!this.#activeItem && item !== this.#activeItem) {\n this.#activeItem.tabIndex = -1;\n }\n item.tabIndex = 0;\n this.#activeItem = item;\n }\n }\n\n /**\n * focuses on an item and sets it as active\n */\n focusOnItem(item?: HTMLElement):void {\n this.updateActiveItem(item || this.firstItem);\n this.#activeItem?.focus();\n }\n\n /**\n * Focuses next focusable item\n */\n updateItems(items: HTMLElement[]) {\n const sequence = [...items.slice(this.#itemIndex), ...items.slice(0, this.#itemIndex)];\n const first = sequence.find(item => this.#focusableItems.includes(item));\n this.focusOnItem(first || this.firstItem);\n }\n\n /**\n * from array of HTML items, and sets active items\n */\n initItems(items: HTMLElement[], itemsContainer: HTMLElement = this.host) {\n this.#items = items ?? [];\n const focusableItems = this.#focusableItems;\n const [focusableItem] = focusableItems;\n this.#activeItem = focusableItem;\n for (const item of focusableItems) {\n item.tabIndex = this.#activeItem === item ? 0 : -1;\n }\n /**\n * removes listener on previous contained and applies it to new container\n */\n if (!this.#itemsContainer || itemsContainer !== this.#itemsContainer) {\n this.#itemsContainer?.removeEventListener('keydown', this.#onKeydown);\n this.#itemsContainer = itemsContainer;\n this.hostConnected();\n }\n }\n\n /**\n * adds event listners to items container\n */\n hostConnected() {\n this.#itemsContainer?.addEventListener('keydown', this.#onKeydown);\n }\n\n /**\n * removes event listners from items container\n */\n hostDisconnected() {\n this.#itemsContainer?.removeEventListener('keydown', this.#onKeydown);\n }\n}\n"]}
@@ -4163,6 +4163,13 @@
4163
4163
  "type": {
4164
4164
  "text": "HTMLElement[]"
4165
4165
  }
4166
+ },
4167
+ {
4168
+ "name": "itemsContainer",
4169
+ "optional": true,
4170
+ "type": {
4171
+ "text": "HTMLElement"
4172
+ }
4166
4173
  }
4167
4174
  ],
4168
4175
  "description": "from array of HTML items, and sets active items"
@@ -4174,7 +4181,18 @@
4174
4181
  "type": {
4175
4182
  "text": "void"
4176
4183
  }
4177
- }
4184
+ },
4185
+ "description": "adds event listners to items container"
4186
+ },
4187
+ {
4188
+ "kind": "method",
4189
+ "name": "hostDisconnected",
4190
+ "return": {
4191
+ "type": {
4192
+ "text": "void"
4193
+ }
4194
+ },
4195
+ "description": "removes event listners from items container"
4178
4196
  }
4179
4197
  ]
4180
4198
  }
@@ -4260,13 +4278,23 @@
4260
4278
  "parameters": [
4261
4279
  {
4262
4280
  "name": "items"
4281
+ },
4282
+ {
4283
+ "name": "itemsContainer",
4284
+ "default": "this.host"
4263
4285
  }
4264
4286
  ],
4265
4287
  "description": "from array of HTML items, and sets active items"
4266
4288
  },
4267
4289
  {
4268
4290
  "kind": "method",
4269
- "name": "hostConnected"
4291
+ "name": "hostConnected",
4292
+ "description": "adds event listners to items container"
4293
+ },
4294
+ {
4295
+ "kind": "method",
4296
+ "name": "hostDisconnected",
4297
+ "description": "removes event listners from items container"
4270
4298
  },
4271
4299
  {
4272
4300
  "kind": "field",
@@ -4305,6 +4333,15 @@
4305
4333
  },
4306
4334
  "description": "active focusable element"
4307
4335
  },
4336
+ {
4337
+ "kind": "field",
4338
+ "name": "#itemsContainer",
4339
+ "privacy": "private",
4340
+ "type": {
4341
+ "text": "HTMLElement | undefined"
4342
+ },
4343
+ "description": "closest ancestor containing items"
4344
+ },
4308
4345
  {
4309
4346
  "kind": "field",
4310
4347
  "name": "#items",
@@ -4383,21 +4420,9 @@
4383
4420
  "description": "previous item after active item in array of focusable items"
4384
4421
  },
4385
4422
  {
4386
- "kind": "method",
4423
+ "kind": "field",
4387
4424
  "name": "#onKeydown",
4388
- "return": {
4389
- "type": {
4390
- "text": "void"
4391
- }
4392
- },
4393
- "parameters": [
4394
- {
4395
- "name": "event",
4396
- "type": {
4397
- "text": "KeyboardEvent"
4398
- }
4399
- }
4400
- ],
4425
+ "privacy": "private",
4401
4426
  "description": "handles keyboard navigation"
4402
4427
  },
4403
4428
  {
@@ -4460,13 +4485,26 @@
4460
4485
  "type": {
4461
4486
  "text": "HTMLElement[]"
4462
4487
  }
4488
+ },
4489
+ {
4490
+ "name": "itemsContainer",
4491
+ "default": "this.host",
4492
+ "type": {
4493
+ "text": "HTMLElement"
4494
+ }
4463
4495
  }
4464
4496
  ],
4465
4497
  "description": "from array of HTML items, and sets active items"
4466
4498
  },
4467
4499
  {
4468
4500
  "kind": "method",
4469
- "name": "hostConnected"
4501
+ "name": "hostConnected",
4502
+ "description": "adds event listners to items container"
4503
+ },
4504
+ {
4505
+ "kind": "method",
4506
+ "name": "hostDisconnected",
4507
+ "description": "removes event listners from items container"
4470
4508
  },
4471
4509
  {
4472
4510
  "kind": "field",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@patternfly/pfe-core",
3
- "version": "2.0.0",
3
+ "version": "2.1.0",
4
4
  "license": "MIT",
5
5
  "description": "PatternFly Elements Core Library",
6
6
  "customElements": "custom-elements.json",