kempo-ui 0.0.80 → 0.0.82
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.
- package/.github/skills/new-component/SKILL.md +80 -2
- package/dist/components/FilterItem.js +8 -0
- package/dist/components/FilterList.js +5 -0
- package/docs/components/filter-list.html +135 -0
- package/docs/icons/search.svg +1 -1
- package/docs/index.html +6 -0
- package/docs/nav-1.inc.html +69 -0
- package/docs/nav.inc.html +69 -0
- package/docs/nav.inc.js +52 -2
- package/docs/src/components/FilterItem.js +8 -0
- package/docs/src/components/FilterList.js +5 -0
- package/icons/search.svg +1 -0
- package/package.json +1 -1
- package/src/components/FilterItem.js +25 -0
- package/src/components/FilterList.js +33 -0
- package/tests/components/FilterItem.browser-test.js +104 -0
- package/tests/components/FilterList.browser-test.js +162 -0
|
@@ -13,12 +13,13 @@ Use this skill any time you are asked to create a new component or add a new cus
|
|
|
13
13
|
|
|
14
14
|
## Overview
|
|
15
15
|
|
|
16
|
-
Creating a component involves
|
|
16
|
+
Creating a component involves five steps:
|
|
17
17
|
|
|
18
18
|
1. **Choose the base component** — pick the right rendering strategy
|
|
19
19
|
2. **Write the source file** in `src/components/`
|
|
20
20
|
3. **Register the custom element** at the bottom of the source file
|
|
21
21
|
4. **Add documentation** in `docs/components/`
|
|
22
|
+
5. **Write and run unit tests** in `tests/components/`
|
|
22
23
|
|
|
23
24
|
---
|
|
24
25
|
|
|
@@ -151,7 +152,7 @@ customElements.define('k-my-component', MyComponent);
|
|
|
151
152
|
|
|
152
153
|
---
|
|
153
154
|
|
|
154
|
-
## Step 3: Register in the Nav (if it should appear in docs)
|
|
155
|
+
## Step 3: Register in the Nav and Index (if it should appear in docs)
|
|
155
156
|
|
|
156
157
|
Add a link to the component in `docs/nav.inc.html` inside the `<menu>` under the appropriate section, in alphabetical order:
|
|
157
158
|
|
|
@@ -159,6 +160,17 @@ Add a link to the component in `docs/nav.inc.html` inside the `<menu>` under the
|
|
|
159
160
|
<a href="./components/my-component.html">My Component</a>
|
|
160
161
|
```
|
|
161
162
|
|
|
163
|
+
Also add a card in `docs/index.html` inside the `<div class="row -mx">` under the `<h2>Components</h2>` section, in alphabetical order:
|
|
164
|
+
|
|
165
|
+
```html
|
|
166
|
+
<div class="span-12 t-span-6 d-span-4 px">
|
|
167
|
+
<a href="./components/my-component.html" class="card mb no-link d-b">
|
|
168
|
+
<h3 class="tc-primary">My Component</h3>
|
|
169
|
+
<p class="tc-muted">One-sentence description of what the component does.</p>
|
|
170
|
+
</a>
|
|
171
|
+
</div>
|
|
172
|
+
```
|
|
173
|
+
|
|
162
174
|
---
|
|
163
175
|
|
|
164
176
|
## Step 4: Add Documentation
|
|
@@ -182,6 +194,72 @@ Key requirements:
|
|
|
182
194
|
|
|
183
195
|
---
|
|
184
196
|
|
|
197
|
+
## Step 5: Write and Run Unit Tests
|
|
198
|
+
|
|
199
|
+
Create `tests/components/MyComponent.browser-test.js`. Use an existing test file (e.g. `tests/components/Toggle.browser-test.js`) as a reference.
|
|
200
|
+
|
|
201
|
+
Key conventions:
|
|
202
|
+
- Import the component class at the top.
|
|
203
|
+
- Define an async `createMyComponent()` helper that builds the DOM, appends it to `document.body`, awaits `el.updateComplete`, and returns `{ container, el }`.
|
|
204
|
+
- Define a `cleanup(container)` helper that removes the container from the DOM.
|
|
205
|
+
- Export a default plain object where each key is a test description and each value is an `async ({pass, fail}) => {}` function.
|
|
206
|
+
- Always call `cleanup(container)` before every `pass()` or `fail()` call.
|
|
207
|
+
- Use multi-line comments to group related tests (e.g. `/* Element Creation */`, `/* Properties */`).
|
|
208
|
+
|
|
209
|
+
Tests to include at minimum:
|
|
210
|
+
- Element is created and is an instance of the component class
|
|
211
|
+
- Element has a shadow root
|
|
212
|
+
- Default property values are correct
|
|
213
|
+
- Attribute reflection works (if applicable)
|
|
214
|
+
- Public methods behave correctly
|
|
215
|
+
- Events are dispatched correctly (if applicable)
|
|
216
|
+
|
|
217
|
+
Example skeleton:
|
|
218
|
+
|
|
219
|
+
```javascript
|
|
220
|
+
import MyComponent from '../../src/components/MyComponent.js';
|
|
221
|
+
|
|
222
|
+
const createMyComponent = async () => {
|
|
223
|
+
const container = document.createElement('div');
|
|
224
|
+
container.innerHTML = `<k-my-component></k-my-component>`;
|
|
225
|
+
document.body.appendChild(container);
|
|
226
|
+
const el = container.querySelector('k-my-component');
|
|
227
|
+
await el.updateComplete;
|
|
228
|
+
return { container, el };
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
const cleanup = (container) => {
|
|
232
|
+
if(container && container.parentNode){
|
|
233
|
+
container.parentNode.removeChild(container);
|
|
234
|
+
}
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
export default {
|
|
238
|
+
/*
|
|
239
|
+
Element Creation
|
|
240
|
+
*/
|
|
241
|
+
'should create my-component element': async ({pass, fail}) => {
|
|
242
|
+
const { container, el } = await createMyComponent();
|
|
243
|
+
if(!(el instanceof MyComponent)){
|
|
244
|
+
cleanup(container);
|
|
245
|
+
return fail('Element should be instance of MyComponent');
|
|
246
|
+
}
|
|
247
|
+
cleanup(container);
|
|
248
|
+
pass('MyComponent element created correctly');
|
|
249
|
+
},
|
|
250
|
+
};
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
After writing the tests, run them to confirm they all pass:
|
|
254
|
+
|
|
255
|
+
```
|
|
256
|
+
npm run test -- MyComponent
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
The partial string `MyComponent` will match any test file whose path contains that string. Fix any failures before considering the component complete.
|
|
260
|
+
|
|
261
|
+
---
|
|
262
|
+
|
|
185
263
|
## Component Architecture and Communication
|
|
186
264
|
|
|
187
265
|
- Use **methods** to trigger actions. Events notify that something already happened; they should not be used to trigger logic.
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import ShadowComponent from"./ShadowComponent.js";import{html,css}from"../lit-all.min.js";export default class FilterItem extends ShadowComponent{render(){return html`<slot></slot>`}static styles=css`
|
|
2
|
+
:host {
|
|
3
|
+
display: contents;
|
|
4
|
+
}
|
|
5
|
+
:host([hidden]) {
|
|
6
|
+
display: none !important;
|
|
7
|
+
}
|
|
8
|
+
`}customElements.define("k-filter-item",FilterItem);
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import ShadowComponent from"./ShadowComponent.js";import{html,css}from"../lit-all.min.js";export default class FilterList extends ShadowComponent{render(){return html`<slot></slot>`}filter(t){const e=t.toLowerCase().split(/\s+/).filter(t=>t.length>0);this.querySelectorAll("k-filter-item").forEach(t=>{const s=(t.getAttribute("filter-keywords")||"").toLowerCase();t.hidden=e.length>0&&!e.every(t=>s.includes(t))})}static styles=css`
|
|
2
|
+
:host {
|
|
3
|
+
display: block;
|
|
4
|
+
}
|
|
5
|
+
`}customElements.define("k-filter-list",FilterList);
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>Filter List - Components - Kempo Docs - A Web Components Solution</title>
|
|
7
|
+
<link rel="icon" type="image/png" sizes="48x48" href="../media/icon48.png">
|
|
8
|
+
<link rel="manifest" href="../manifest.json" />
|
|
9
|
+
<link rel="stylesheet" href="../kempo-vars.css" />
|
|
10
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/kempo-css@1.3.11/dist/kempo.min.css" />
|
|
11
|
+
<link rel="stylesheet" href="../kempo-hljs.css" />
|
|
12
|
+
<link rel="stylesheet" href="../styles.css" />
|
|
13
|
+
<script>window.litDisableBundleWarning = true;</script>
|
|
14
|
+
</head>
|
|
15
|
+
<body>
|
|
16
|
+
<k-import src="../nav-1.inc.html"></k-import>
|
|
17
|
+
<main>
|
|
18
|
+
<h1 class="ta-center">Filter List</h1>
|
|
19
|
+
|
|
20
|
+
<k-accordion persistent-id="toc" class="b r mb">
|
|
21
|
+
<k-accordion-header for-panel="toc-panel">Table of Contents</k-accordion-header>
|
|
22
|
+
<k-accordion-panel name="toc-panel">
|
|
23
|
+
<div class="m">
|
|
24
|
+
<h6>Examples</h6>
|
|
25
|
+
<a href="#basicUsage">Basic Usage</a><br />
|
|
26
|
+
<a href="#tagButtons">Tag Buttons</a><br />
|
|
27
|
+
|
|
28
|
+
<h6 class="mt"><a href="#jsRef" class="no-link">JavaScript Reference</a></h6>
|
|
29
|
+
<a href="#constructor">Constructor</a><br />
|
|
30
|
+
<a href="#requirements">Requirements</a><br />
|
|
31
|
+
<a href="#filterItem">FilterItem Attributes</a><br />
|
|
32
|
+
<a href="#methods">Methods</a><br />
|
|
33
|
+
</div>
|
|
34
|
+
</k-accordion-panel>
|
|
35
|
+
</k-accordion>
|
|
36
|
+
|
|
37
|
+
<h3 id="basicUsage"><a href="#basicUsage" class="no-link">Basic Usage</a></h3>
|
|
38
|
+
<p>Wrap items in <code>k-filter-item</code> elements with a <code>filter-keywords</code> attribute inside a <code>k-filter-list</code>. Connect an input to <code>filter()</code> using the <a href="../utils/debounce.html">debounce</a> utility to avoid filtering on every keystroke.</p>
|
|
39
|
+
<div class="row -mx">
|
|
40
|
+
<div class="col m-span-12 px">
|
|
41
|
+
<k-card label="HTML">
|
|
42
|
+
<pre><code class="hljs xml"><span class="hljs-tag"><<span class="hljs-name">input</span> <span class="hljs-attr">id</span>=<span class="hljs-string">filterInput</span> <span class="hljs-attr">type</span>=<span class="hljs-string">search</span> <span class="hljs-attr">placeholder</span>=<span class="hljs-string">'Filter...'</span> /></span><br><span class="hljs-tag"><<span class="hljs-name">k-filter-list</span> <span class="hljs-attr">id</span>=<span class="hljs-string">myFilterList</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">k-filter-item</span> <span class="hljs-attr">class</span>=<span class="hljs-string">d-b</span> <span class="hljs-attr">filter-keywords</span>=<span class="hljs-string">'apple fruit red'</span>></span><span class="hljs-tag"><<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">#</span>></span>Apple<span class="hljs-tag"></<span class="hljs-name">a</span>></span><span class="hljs-tag"></<span class="hljs-name">k-filter-item</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">k-filter-item</span> <span class="hljs-attr">class</span>=<span class="hljs-string">d-b</span> <span class="hljs-attr">filter-keywords</span>=<span class="hljs-string">'banana fruit yellow'</span>></span><span class="hljs-tag"><<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">#</span>></span>Banana<span class="hljs-tag"></<span class="hljs-name">a</span>></span><span class="hljs-tag"></<span class="hljs-name">k-filter-item</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">k-filter-item</span> <span class="hljs-attr">class</span>=<span class="hljs-string">d-b</span> <span class="hljs-attr">filter-keywords</span>=<span class="hljs-string">'cherry fruit red'</span>></span><span class="hljs-tag"><<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">#</span>></span>Cherry<span class="hljs-tag"></<span class="hljs-name">a</span>></span><span class="hljs-tag"></<span class="hljs-name">k-filter-item</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">k-filter-item</span> <span class="hljs-attr">class</span>=<span class="hljs-string">d-b</span> <span class="hljs-attr">filter-keywords</span>=<span class="hljs-string">'grape fruit purple'</span>></span><span class="hljs-tag"><<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">#</span>></span>Grape<span class="hljs-tag"></<span class="hljs-name">a</span>></span><span class="hljs-tag"></<span class="hljs-name">k-filter-item</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">k-filter-item</span> <span class="hljs-attr">class</span>=<span class="hljs-string">d-b</span> <span class="hljs-attr">filter-keywords</span>=<span class="hljs-string">'lemon citrus yellow fruit'</span>></span><span class="hljs-tag"><<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">#</span>></span>Lemon<span class="hljs-tag"></<span class="hljs-name">a</span>></span><span class="hljs-tag"></<span class="hljs-name">k-filter-item</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">k-filter-item</span> <span class="hljs-attr">class</span>=<span class="hljs-string">d-b</span> <span class="hljs-attr">filter-keywords</span>=<span class="hljs-string">'orange citrus fruit'</span>></span><span class="hljs-tag"><<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">#</span>></span>Orange<span class="hljs-tag"></<span class="hljs-name">a</span>></span><span class="hljs-tag"></<span class="hljs-name">k-filter-item</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">k-filter-item</span> <span class="hljs-attr">class</span>=<span class="hljs-string">d-b</span> <span class="hljs-attr">filter-keywords</span>=<span class="hljs-string">'strawberry fruit red'</span>></span><span class="hljs-tag"><<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">#</span>></span>Strawberry<span class="hljs-tag"></<span class="hljs-name">a</span>></span><span class="hljs-tag"></<span class="hljs-name">k-filter-item</span>></span><br><span class="hljs-tag"></<span class="hljs-name">k-filter-list</span>></span><br><span class="hljs-tag"><<span class="hljs-name">script</span> <span class="hljs-attr">type</span>=<span class="hljs-string">module</span>></span><span class="language-javascript"><br> <span class="hljs-keyword">import</span> debounce <span class="hljs-keyword">from</span> <span class="hljs-string">'../src/utils/debounce.js'</span>;<br> <span class="hljs-keyword">const</span> list = <span class="hljs-variable language_">document</span>.<span class="hljs-title function_">getElementById</span>(<span class="hljs-string">'myFilterList'</span>);<br> <span class="hljs-variable language_">document</span>.<span class="hljs-title function_">getElementById</span>(<span class="hljs-string">'filterInput'</span>).<span class="hljs-title function_">addEventListener</span>(<span class="hljs-string">'input'</span>, <span class="hljs-title function_">debounce</span>(<span class="hljs-function"><span class="hljs-params">e</span> =></span> list.<span class="hljs-title function_">filter</span>(e.<span class="hljs-property">target</span>.<span class="hljs-property">value</span>)));<br></span><span class="hljs-tag"></<span class="hljs-name">script</span>></span></code></pre>
|
|
43
|
+
</k-card>
|
|
44
|
+
</div>
|
|
45
|
+
<div class="col m-span-12 px">
|
|
46
|
+
<k-card label="Output">
|
|
47
|
+
<input id="filterInput" type="search" placeholder="Filter..." class="mb" />
|
|
48
|
+
<k-filter-list id="myFilterList">
|
|
49
|
+
<k-filter-item class="d-b" filter-keywords="apple fruit red"><a href="#">Apple</a></k-filter-item>
|
|
50
|
+
<k-filter-item class="d-b" filter-keywords="banana fruit yellow"><a href="#">Banana</a></k-filter-item>
|
|
51
|
+
<k-filter-item class="d-b" filter-keywords="cherry fruit red"><a href="#">Cherry</a></k-filter-item>
|
|
52
|
+
<k-filter-item class="d-b" filter-keywords="grape fruit purple"><a href="#">Grape</a></k-filter-item>
|
|
53
|
+
<k-filter-item class="d-b" filter-keywords="lemon citrus yellow fruit"><a href="#">Lemon</a></k-filter-item>
|
|
54
|
+
<k-filter-item class="d-b" filter-keywords="orange citrus fruit"><a href="#">Orange</a></k-filter-item>
|
|
55
|
+
<k-filter-item class="d-b" filter-keywords="strawberry fruit red"><a href="#">Strawberry</a></k-filter-item>
|
|
56
|
+
</k-filter-list>
|
|
57
|
+
<script type="module">
|
|
58
|
+
import debounce from '../src/utils/debounce.js';
|
|
59
|
+
const list = document.getElementById('myFilterList');
|
|
60
|
+
document.getElementById('filterInput').addEventListener('input', debounce(e => list.filter(e.target.value)));
|
|
61
|
+
</script>
|
|
62
|
+
</k-card>
|
|
63
|
+
</div>
|
|
64
|
+
</div>
|
|
65
|
+
|
|
66
|
+
<h3 id="tagButtons"><a href="#tagButtons" class="no-link">Tag Buttons</a></h3>
|
|
67
|
+
<p>Use category buttons to filter the list. Each button stores its filter term in <code>data-filter</code>; clicking it activates that button and calls <code>filter()</code>. An empty string clears the filter and shows all items.</p>
|
|
68
|
+
<div class="row -mx">
|
|
69
|
+
<div class="col m-span-12 px">
|
|
70
|
+
<k-card label="HTML">
|
|
71
|
+
<pre><code class="hljs xml"><span class="hljs-tag"><<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">'d-f gap mb'</span> <span class="hljs-attr">id</span>=<span class="hljs-string">tagButtons</span>></span><span class="hljs-tag"><<span class="hljs-name">button</span> <span class="hljs-attr">class</span>=<span class="hljs-string">'btn primary'</span> <span class="hljs-attr">data-filter</span>=<span class="hljs-string">''</span>></span>All<span class="hljs-tag"></<span class="hljs-name">button</span>></span><span class="hljs-tag"><<span class="hljs-name">button</span> <span class="hljs-attr">class</span>=<span class="hljs-string">btn</span> <span class="hljs-attr">data-filter</span>=<span class="hljs-string">red</span>></span>Red<span class="hljs-tag"></<span class="hljs-name">button</span>></span><span class="hljs-tag"><<span class="hljs-name">button</span> <span class="hljs-attr">class</span>=<span class="hljs-string">btn</span> <span class="hljs-attr">data-filter</span>=<span class="hljs-string">yellow</span>></span>Yellow<span class="hljs-tag"></<span class="hljs-name">button</span>></span><span class="hljs-tag"><<span class="hljs-name">button</span> <span class="hljs-attr">class</span>=<span class="hljs-string">btn</span> <span class="hljs-attr">data-filter</span>=<span class="hljs-string">citrus</span>></span>Citrus<span class="hljs-tag"></<span class="hljs-name">button</span>></span><span class="hljs-tag"></<span class="hljs-name">div</span>></span><br><span class="hljs-tag"><<span class="hljs-name">k-filter-list</span> <span class="hljs-attr">id</span>=<span class="hljs-string">tagFilterList</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">k-filter-item</span> <span class="hljs-attr">class</span>=<span class="hljs-string">d-b</span> <span class="hljs-attr">filter-keywords</span>=<span class="hljs-string">'apple fruit red'</span>></span><span class="hljs-tag"><<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">#</span>></span>Apple<span class="hljs-tag"></<span class="hljs-name">a</span>></span><span class="hljs-tag"></<span class="hljs-name">k-filter-item</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">k-filter-item</span> <span class="hljs-attr">class</span>=<span class="hljs-string">d-b</span> <span class="hljs-attr">filter-keywords</span>=<span class="hljs-string">'banana fruit yellow'</span>></span><span class="hljs-tag"><<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">#</span>></span>Banana<span class="hljs-tag"></<span class="hljs-name">a</span>></span><span class="hljs-tag"></<span class="hljs-name">k-filter-item</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">k-filter-item</span> <span class="hljs-attr">class</span>=<span class="hljs-string">d-b</span> <span class="hljs-attr">filter-keywords</span>=<span class="hljs-string">'cherry fruit red'</span>></span><span class="hljs-tag"><<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">#</span>></span>Cherry<span class="hljs-tag"></<span class="hljs-name">a</span>></span><span class="hljs-tag"></<span class="hljs-name">k-filter-item</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">k-filter-item</span> <span class="hljs-attr">class</span>=<span class="hljs-string">d-b</span> <span class="hljs-attr">filter-keywords</span>=<span class="hljs-string">'grape fruit purple'</span>></span><span class="hljs-tag"><<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">#</span>></span>Grape<span class="hljs-tag"></<span class="hljs-name">a</span>></span><span class="hljs-tag"></<span class="hljs-name">k-filter-item</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">k-filter-item</span> <span class="hljs-attr">class</span>=<span class="hljs-string">d-b</span> <span class="hljs-attr">filter-keywords</span>=<span class="hljs-string">'lemon citrus yellow fruit'</span>></span><span class="hljs-tag"><<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">#</span>></span>Lemon<span class="hljs-tag"></<span class="hljs-name">a</span>></span><span class="hljs-tag"></<span class="hljs-name">k-filter-item</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">k-filter-item</span> <span class="hljs-attr">class</span>=<span class="hljs-string">d-b</span> <span class="hljs-attr">filter-keywords</span>=<span class="hljs-string">'orange citrus fruit'</span>></span><span class="hljs-tag"><<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">#</span>></span>Orange<span class="hljs-tag"></<span class="hljs-name">a</span>></span><span class="hljs-tag"></<span class="hljs-name">k-filter-item</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">k-filter-item</span> <span class="hljs-attr">class</span>=<span class="hljs-string">d-b</span> <span class="hljs-attr">filter-keywords</span>=<span class="hljs-string">'strawberry fruit red'</span>></span><span class="hljs-tag"><<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">#</span>></span>Strawberry<span class="hljs-tag"></<span class="hljs-name">a</span>></span><span class="hljs-tag"></<span class="hljs-name">k-filter-item</span>></span><br><span class="hljs-tag"></<span class="hljs-name">k-filter-list</span>></span><br><span class="hljs-tag"><<span class="hljs-name">script</span> <span class="hljs-attr">type</span>=<span class="hljs-string">module</span>></span><span class="language-javascript"><br> <span class="hljs-keyword">const</span> list = <span class="hljs-variable language_">document</span>.<span class="hljs-title function_">getElementById</span>(<span class="hljs-string">'tagFilterList'</span>);<br> <span class="hljs-variable language_">document</span>.<span class="hljs-title function_">querySelectorAll</span>(<span class="hljs-string">'#tagButtons [data-filter]'</span>).<span class="hljs-title function_">forEach</span>(<span class="hljs-function"><span class="hljs-params">btn</span> =></span> {<br> btn.<span class="hljs-title function_">addEventListener</span>(<span class="hljs-string">'click'</span>, <span class="hljs-function">() =></span> {<br> <span class="hljs-variable language_">document</span>.<span class="hljs-title function_">querySelectorAll</span>(<span class="hljs-string">'#tagButtons [data-filter]'</span>).<span class="hljs-title function_">forEach</span>(<span class="hljs-function"><span class="hljs-params">b</span> =></span> b.<span class="hljs-property">classList</span>.<span class="hljs-title function_">remove</span>(<span class="hljs-string">'primary'</span>));<br> btn.<span class="hljs-property">classList</span>.<span class="hljs-title function_">add</span>(<span class="hljs-string">'primary'</span>);<br> list.<span class="hljs-title function_">filter</span>(btn.<span class="hljs-property">dataset</span>.<span class="hljs-property">filter</span>);<br> });<br> });<br></span><span class="hljs-tag"></<span class="hljs-name">script</span>></span></code></pre>
|
|
72
|
+
</k-card>
|
|
73
|
+
</div>
|
|
74
|
+
<div class="col m-span-12 px">
|
|
75
|
+
<k-card label="Output">
|
|
76
|
+
<div class="d-f gap mb" id="tagButtons">
|
|
77
|
+
<button class="btn primary" data-filter="">All</button>
|
|
78
|
+
<button class="btn" data-filter="red">Red</button>
|
|
79
|
+
<button class="btn" data-filter="yellow">Yellow</button>
|
|
80
|
+
<button class="btn" data-filter="citrus">Citrus</button>
|
|
81
|
+
</div>
|
|
82
|
+
<k-filter-list id="tagFilterList">
|
|
83
|
+
<k-filter-item class="d-b" filter-keywords="apple fruit red"><a href="#">Apple</a></k-filter-item>
|
|
84
|
+
<k-filter-item class="d-b" filter-keywords="banana fruit yellow"><a href="#">Banana</a></k-filter-item>
|
|
85
|
+
<k-filter-item class="d-b" filter-keywords="cherry fruit red"><a href="#">Cherry</a></k-filter-item>
|
|
86
|
+
<k-filter-item class="d-b" filter-keywords="grape fruit purple"><a href="#">Grape</a></k-filter-item>
|
|
87
|
+
<k-filter-item class="d-b" filter-keywords="lemon citrus yellow fruit"><a href="#">Lemon</a></k-filter-item>
|
|
88
|
+
<k-filter-item class="d-b" filter-keywords="orange citrus fruit"><a href="#">Orange</a></k-filter-item>
|
|
89
|
+
<k-filter-item class="d-b" filter-keywords="strawberry fruit red"><a href="#">Strawberry</a></k-filter-item>
|
|
90
|
+
</k-filter-list>
|
|
91
|
+
<script type="module">
|
|
92
|
+
const list = document.getElementById('tagFilterList');
|
|
93
|
+
document.querySelectorAll('#tagButtons [data-filter]').forEach(btn => {
|
|
94
|
+
btn.addEventListener('click', () => {
|
|
95
|
+
document.querySelectorAll('#tagButtons [data-filter]').forEach(b => b.classList.remove('primary'));
|
|
96
|
+
btn.classList.add('primary');
|
|
97
|
+
list.filter(btn.dataset.filter);
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
</script>
|
|
101
|
+
</k-card>
|
|
102
|
+
</div>
|
|
103
|
+
</div>
|
|
104
|
+
|
|
105
|
+
<h2 id="jsRef"><a href="#jsRef" class="no-link">JavaScript Reference</a></h2>
|
|
106
|
+
|
|
107
|
+
<h3 id="constructor"><a href="#constructor" class="no-link">Constructor</a></h3>
|
|
108
|
+
<h6>Extends <a href="./shadow-component.html">ShadowComponent</a></h6>
|
|
109
|
+
<h5><code>new FilterList()</code></h5>
|
|
110
|
+
<h5><code>new FilterItem()</code></h5>
|
|
111
|
+
|
|
112
|
+
<h3 id="requirements"><a href="#requirements" class="no-link">Requirements</a></h3>
|
|
113
|
+
<ul>
|
|
114
|
+
<li><a href="./shadow-component.html">ShadowComponent</a></li>
|
|
115
|
+
</ul>
|
|
116
|
+
|
|
117
|
+
<h3 id="filterItem"><a href="#filterItem" class="no-link">FilterItem Attributes</a></h3>
|
|
118
|
+
<h5><code>filter-keywords<i>: string</i></code></h5>
|
|
119
|
+
<p>A string of keywords used to match against the search term. The <code>filter()</code> method checks if this value includes the lowercased search term as a substring.</p>
|
|
120
|
+
|
|
121
|
+
<h3 id="methods"><a href="#methods" class="no-link">Methods</a></h3>
|
|
122
|
+
<h5><code>filter(term<i>: string</i>)<i>: void</i></code></h5>
|
|
123
|
+
<p>Shows or hides each child <code>k-filter-item</code> based on whether its <code>filter-keywords</code> attribute includes <code>term</code> (case-insensitive). Passing an empty string reveals all items.</p>
|
|
124
|
+
|
|
125
|
+
</main>
|
|
126
|
+
<div style="height:33vh"></div>
|
|
127
|
+
|
|
128
|
+
<script type="module" src="../src/components/Import.js"></script>
|
|
129
|
+
<script type="module" src="../src/components/FilterList.js"></script>
|
|
130
|
+
<script type="module" src="../src/components/FilterItem.js"></script>
|
|
131
|
+
<script type="module" src="../src/components/Accordion.js"></script>
|
|
132
|
+
<script type="module" src="../src/components/Card.js"></script>
|
|
133
|
+
|
|
134
|
+
</body>
|
|
135
|
+
</html>
|
package/docs/icons/search.svg
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path fill="currentColor" d="M784-120 532-372q-30 24-69 38t-83 14q-109 0-184.5-75.5T120-580q0-109 75.5-184.5T380-840q109 0 184.5 75.5T640-580q0 44-14 83t-38 69l252 252-56 56ZM380-400q75 0 127.5-52.5T560-580q0-75-52.5-127.5T380-760q-75 0-127.5 52.5T200-580q0 75 52.5 127.5T380-400Z"/></svg>
|
package/docs/index.html
CHANGED
|
@@ -83,6 +83,12 @@
|
|
|
83
83
|
<p class="tc-muted">Action menus with keyboard navigation and customizable positioning.</p>
|
|
84
84
|
</a>
|
|
85
85
|
</div>
|
|
86
|
+
<div class="span-12 t-span-6 d-span-4 px">
|
|
87
|
+
<a href="./components/filter-list.html" class="card mb no-link d-b">
|
|
88
|
+
<h3 class="tc-primary">Filter List</h3>
|
|
89
|
+
<p class="tc-muted">Show or hide items based on a search term matching their filter keywords.</p>
|
|
90
|
+
</a>
|
|
91
|
+
</div>
|
|
86
92
|
<div class="span-12 t-span-6 d-span-4 px">
|
|
87
93
|
<a href="./components/focus-capture.html" class="card mb no-link d-b">
|
|
88
94
|
<h3 class="tc-primary">FocusCapture</h3>
|
package/docs/nav-1.inc.html
CHANGED
|
@@ -15,6 +15,67 @@
|
|
|
15
15
|
<img src="../media/icon32.png" alt="Kempo UI Icon" class="pr" />
|
|
16
16
|
Kempo UI
|
|
17
17
|
</a>
|
|
18
|
+
<div style="position:relative;margin:0 .25rem;align-self:center;" theme="light">
|
|
19
|
+
<input id="navSearchInput" type="search" placeholder="Search docs..." autocomplete="off" style="width:11rem;" />
|
|
20
|
+
<div id="navSearchDropdown" hidden theme="light" style="position:absolute;top:calc(100% + 4px);left:0;width:min(480px,90vw);max-height:70vh;background:var(--c_bg);border:1px solid var(--c_border);border-radius:var(--radius);overflow:auto;z-index:9999;box-shadow:0 4px 12px rgba(0,0,0,.3);">
|
|
21
|
+
<k-filter-list id="navSearchList" style="display:block;padding:.25rem 0;">
|
|
22
|
+
<k-filter-item filter-keywords="accordion components"><a href="../components/accordion.html">Accordion<br><small>Component</small></a></k-filter-item>
|
|
23
|
+
<k-filter-item filter-keywords="card components"><a href="../components/card.html">Card<br><small>Component</small></a></k-filter-item>
|
|
24
|
+
<k-filter-item filter-keywords="color picker colorpicker components"><a href="../components/color-picker.html">Color Picker<br><small>Component</small></a></k-filter-item>
|
|
25
|
+
<k-filter-item filter-keywords="content slider components"><a href="../components/content-slider.html">Content Slider<br><small>Component</small></a></k-filter-item>
|
|
26
|
+
<k-filter-item filter-keywords="dialog modal popup components"><a href="../components/dialog.html">Dialog<br><small>Component</small></a></k-filter-item>
|
|
27
|
+
<k-filter-item filter-keywords="dropdown select components"><a href="../components/dropdown.html">Dropdown<br><small>Component</small></a></k-filter-item>
|
|
28
|
+
<k-filter-item filter-keywords="filter list filterlist components"><a href="../components/filter-list.html">Filter List<br><small>Component</small></a></k-filter-item>
|
|
29
|
+
<k-filter-item filter-keywords="focus capture focuscapture components"><a href="../components/focus-capture.html">Focus Capture<br><small>Component</small></a></k-filter-item>
|
|
30
|
+
<k-filter-item filter-keywords="html editor components"><a href="../components/html-editor.html">HTML Editor<br><small>Component</small></a></k-filter-item>
|
|
31
|
+
<k-filter-item filter-keywords="icon components"><a href="../components/icon.html">Icon<br><small>Component</small></a></k-filter-item>
|
|
32
|
+
<k-filter-item filter-keywords="import components"><a href="../components/import.html">Import<br><small>Component</small></a></k-filter-item>
|
|
33
|
+
<k-filter-item filter-keywords="photo viewer photoviewer components"><a href="../components/photo-viewer.html">Photo Viewer<br><small>Component</small></a></k-filter-item>
|
|
34
|
+
<k-filter-item filter-keywords="resize components"><a href="../components/resize.html">Resize<br><small>Component</small></a></k-filter-item>
|
|
35
|
+
<k-filter-item filter-keywords="show more showmore components"><a href="../components/show-more.html">Show More<br><small>Component</small></a></k-filter-item>
|
|
36
|
+
<k-filter-item filter-keywords="side menu sidemenu components"><a href="../components/side-menu.html">Side Menu<br><small>Component</small></a></k-filter-item>
|
|
37
|
+
<k-filter-item filter-keywords="side panel sidepanel components"><a href="../components/side-panel.html">Side Panel<br><small>Component</small></a></k-filter-item>
|
|
38
|
+
<k-filter-item filter-keywords="sortable drag drop sort components"><a href="../components/sortable.html">Sortable<br><small>Component</small></a></k-filter-item>
|
|
39
|
+
<k-filter-item filter-keywords="spinner loading components"><a href="../components/spinner.html">Spinner<br><small>Component</small></a></k-filter-item>
|
|
40
|
+
<k-filter-item filter-keywords="split pane components"><a href="../components/split.html">Split<br><small>Component</small></a></k-filter-item>
|
|
41
|
+
<k-filter-item filter-keywords="table data grid components"><a href="../components/table.html">Table<br><small>Component</small></a></k-filter-item>
|
|
42
|
+
<k-filter-item filter-keywords="table controls advanced example"><a href="../components/tableControls.html">Table — Controls<br><small>Advanced Example</small></a></k-filter-item>
|
|
43
|
+
<k-filter-item filter-keywords="table custom fields advanced example"><a href="../components/tableCustomFields.html">Table — Custom Fields<br><small>Advanced Example</small></a></k-filter-item>
|
|
44
|
+
<k-filter-item filter-keywords="table fetch records advanced example"><a href="../components/tableFetchRecords.html">Table — Fetch Records<br><small>Advanced Example</small></a></k-filter-item>
|
|
45
|
+
<k-filter-item filter-keywords="table field sort hide advanced example"><a href="../components/tableFieldSortHide.html">Table — Field Sort & Hide<br><small>Advanced Example</small></a></k-filter-item>
|
|
46
|
+
<k-filter-item filter-keywords="table pagination advanced example"><a href="../components/tablePagination.html">Table — Pagination<br><small>Advanced Example</small></a></k-filter-item>
|
|
47
|
+
<k-filter-item filter-keywords="table record editing advanced example"><a href="../components/tableRecordEditing.html">Table — Record Editing<br><small>Advanced Example</small></a></k-filter-item>
|
|
48
|
+
<k-filter-item filter-keywords="table record filtering advanced example"><a href="../components/tableRecordFiltering.html">Table — Record Filtering<br><small>Advanced Example</small></a></k-filter-item>
|
|
49
|
+
<k-filter-item filter-keywords="table record hiding advanced example"><a href="../components/tableRecordHiding.html">Table — Record Hiding<br><small>Advanced Example</small></a></k-filter-item>
|
|
50
|
+
<k-filter-item filter-keywords="table record searching advanced example"><a href="../components/tableRecordSearching.html">Table — Record Searching<br><small>Advanced Example</small></a></k-filter-item>
|
|
51
|
+
<k-filter-item filter-keywords="table record selection advanced example"><a href="../components/tableRecordSelection.html">Table — Record Selection<br><small>Advanced Example</small></a></k-filter-item>
|
|
52
|
+
<k-filter-item filter-keywords="table row controls advanced example"><a href="../components/tableRowControls.html">Table — Row Controls<br><small>Advanced Example</small></a></k-filter-item>
|
|
53
|
+
<k-filter-item filter-keywords="table server sync advanced example"><a href="../components/tableServerSync.html">Table — Server Sync<br><small>Advanced Example</small></a></k-filter-item>
|
|
54
|
+
<k-filter-item filter-keywords="table sorting advanced example"><a href="../components/tableSorting.html">Table — Sorting<br><small>Advanced Example</small></a></k-filter-item>
|
|
55
|
+
<k-filter-item filter-keywords="tabs tab panel components"><a href="../components/tabs.html">Tabs<br><small>Component</small></a></k-filter-item>
|
|
56
|
+
<k-filter-item filter-keywords="tags tag input components"><a href="../components/tags.html">Tags<br><small>Component</small></a></k-filter-item>
|
|
57
|
+
<k-filter-item filter-keywords="toast notification alert components"><a href="../components/toast.html">Toast<br><small>Component</small></a></k-filter-item>
|
|
58
|
+
<k-filter-item filter-keywords="theme switcher dark light components"><a href="../components/theme-switcher.html">Theme Switcher<br><small>Component</small></a></k-filter-item>
|
|
59
|
+
<k-filter-item filter-keywords="timestamp date time components"><a href="../components/timestamp.html">Timestamp<br><small>Component</small></a></k-filter-item>
|
|
60
|
+
<k-filter-item filter-keywords="toggle switch checkbox components"><a href="../components/toggle.html">Toggle<br><small>Component</small></a></k-filter-item>
|
|
61
|
+
<k-filter-item filter-keywords="tree treeview components"><a href="../components/tree.html">Tree<br><small>Component</small></a></k-filter-item>
|
|
62
|
+
<k-filter-item filter-keywords="shadow component base"><a href="../components/shadow-component.html">Shadow Component<br><small>Base Component</small></a></k-filter-item>
|
|
63
|
+
<k-filter-item filter-keywords="light component base"><a href="../components/light-component.html">Light Component<br><small>Base Component</small></a></k-filter-item>
|
|
64
|
+
<k-filter-item filter-keywords="hybrid component base"><a href="../components/hybrid-component.html">Hybrid Component<br><small>Base Component</small></a></k-filter-item>
|
|
65
|
+
<k-filter-item filter-keywords="cookie utils utility"><a href="../utils/cookie.html">cookie<br><small>Utility</small></a></k-filter-item>
|
|
66
|
+
<k-filter-item filter-keywords="context utils utility"><a href="../utils/context.html">context<br><small>Utility</small></a></k-filter-item>
|
|
67
|
+
<k-filter-item filter-keywords="debounce utils utility"><a href="../utils/debounce.html">debounce<br><small>Utility</small></a></k-filter-item>
|
|
68
|
+
<k-filter-item filter-keywords="drag utils utility"><a href="../utils/drag.html">drag<br><small>Utility</small></a></k-filter-item>
|
|
69
|
+
<k-filter-item filter-keywords="formattimestamp timestamp format date time utils utility"><a href="../utils/formatTimestamp.html">formatTimestamp<br><small>Utility</small></a></k-filter-item>
|
|
70
|
+
<k-filter-item filter-keywords="object utils utility"><a href="../utils/object.html">object<br><small>Utility</small></a></k-filter-item>
|
|
71
|
+
<k-filter-item filter-keywords="propconverters prop converters utils utility"><a href="../utils/propConverters.html">propConverters<br><small>Utility</small></a></k-filter-item>
|
|
72
|
+
<k-filter-item filter-keywords="string utils utility"><a href="../utils/string.html">string<br><small>Utility</small></a></k-filter-item>
|
|
73
|
+
<k-filter-item filter-keywords="theme utils utility"><a href="../utils/theme.html">theme<br><small>Utility</small></a></k-filter-item>
|
|
74
|
+
<k-filter-item filter-keywords="type utils utility"><a href="../utils/type.html">type<br><small>Utility</small></a></k-filter-item>
|
|
75
|
+
<k-filter-item filter-keywords="wait async utils utility"><a href="../utils/wait.html">wait<br><small>Utility</small></a></k-filter-item>
|
|
76
|
+
</k-filter-list>
|
|
77
|
+
</div>
|
|
78
|
+
</div>
|
|
18
79
|
<div class="flex"></div>
|
|
19
80
|
<a href="https://github.com/dustinpoissant/kempo-ui?tab=License-1-ov-file#creative-commons-attribution-noncommercial-sharealike-20"><k-icon name="license"></k-icont></a>
|
|
20
81
|
<a href="https://github.com/dustinpoissant/kempo-ui"><k-icon name="github-mark"></k-icont></a>
|
|
@@ -37,6 +98,7 @@
|
|
|
37
98
|
<a href="../components/content-slider.html">Content Slider</a>
|
|
38
99
|
<a href="../components/dialog.html">Dialog</a>
|
|
39
100
|
<a href="../components/dropdown.html">Dropdown</a>
|
|
101
|
+
<a href="../components/filter-list.html">Filter List</a>
|
|
40
102
|
<a href="../components/focus-capture.html">FocusCapture</a>
|
|
41
103
|
<a href="../components/html-editor.html">HTML Editor</a>
|
|
42
104
|
<a href="../components/icon.html">Icon</a>
|
|
@@ -82,6 +144,13 @@
|
|
|
82
144
|
</div>
|
|
83
145
|
</menu>
|
|
84
146
|
</k-side-menu>
|
|
147
|
+
<style>
|
|
148
|
+
#navSearchList a{display:block;padding:.25rem .5rem;border-radius:var(--radius);text-decoration:none;color:var(--tc);}
|
|
149
|
+
#navSearchList a:hover{background:rgba(128,128,128,.15);}
|
|
150
|
+
#navSearchList a small{display:block;color:var(--tc_muted);}
|
|
151
|
+
</style>
|
|
152
|
+
<script src="../src/components/FilterList.js" type="module"></script>
|
|
153
|
+
<script src="../src/components/FilterItem.js" type="module"></script>
|
|
85
154
|
<script src="../src/components/SideMenu.js" type="module"></script>
|
|
86
155
|
<script src="../src/components/Icon.js" type="module"></script>
|
|
87
156
|
<script src="../src/components/ThemeSwitcher.js" type="module"></script>
|
package/docs/nav.inc.html
CHANGED
|
@@ -15,6 +15,67 @@
|
|
|
15
15
|
<img src="./media/icon32.png" alt="Kempo UI Icon" class="pr" />
|
|
16
16
|
Kempo UI
|
|
17
17
|
</a>
|
|
18
|
+
<div style="position:relative;margin:0 .25rem;align-self:center;" theme="light">
|
|
19
|
+
<input id="navSearchInput" type="search" placeholder="Search docs..." autocomplete="off" style="width:11rem;" />
|
|
20
|
+
<div id="navSearchDropdown" hidden theme="light" style="position:absolute;top:calc(100% + 4px);left:0;width:min(480px,90vw);max-height:70vh;background:var(--c_bg);border:1px solid var(--c_border);border-radius:var(--radius);overflow:auto;z-index:9999;box-shadow:0 4px 12px rgba(0,0,0,.3);">
|
|
21
|
+
<k-filter-list id="navSearchList" style="display:block;padding:.25rem 0;">
|
|
22
|
+
<k-filter-item filter-keywords="accordion components"><a href="./components/accordion.html">Accordion<br><small>Component</small></a></k-filter-item>
|
|
23
|
+
<k-filter-item filter-keywords="card components"><a href="./components/card.html">Card<br><small>Component</small></a></k-filter-item>
|
|
24
|
+
<k-filter-item filter-keywords="color picker colorpicker components"><a href="./components/color-picker.html">Color Picker<br><small>Component</small></a></k-filter-item>
|
|
25
|
+
<k-filter-item filter-keywords="content slider components"><a href="./components/content-slider.html">Content Slider<br><small>Component</small></a></k-filter-item>
|
|
26
|
+
<k-filter-item filter-keywords="dialog modal popup components"><a href="./components/dialog.html">Dialog<br><small>Component</small></a></k-filter-item>
|
|
27
|
+
<k-filter-item filter-keywords="dropdown select components"><a href="./components/dropdown.html">Dropdown<br><small>Component</small></a></k-filter-item>
|
|
28
|
+
<k-filter-item filter-keywords="filter list filterlist components"><a href="./components/filter-list.html">Filter List<br><small>Component</small></a></k-filter-item>
|
|
29
|
+
<k-filter-item filter-keywords="focus capture focuscapture components"><a href="./components/focus-capture.html">Focus Capture<br><small>Component</small></a></k-filter-item>
|
|
30
|
+
<k-filter-item filter-keywords="html editor components"><a href="./components/html-editor.html">HTML Editor<br><small>Component</small></a></k-filter-item>
|
|
31
|
+
<k-filter-item filter-keywords="icon components"><a href="./components/icon.html">Icon<br><small>Component</small></a></k-filter-item>
|
|
32
|
+
<k-filter-item filter-keywords="import components"><a href="./components/import.html">Import<br><small>Component</small></a></k-filter-item>
|
|
33
|
+
<k-filter-item filter-keywords="photo viewer photoviewer components"><a href="./components/photo-viewer.html">Photo Viewer<br><small>Component</small></a></k-filter-item>
|
|
34
|
+
<k-filter-item filter-keywords="resize components"><a href="./components/resize.html">Resize<br><small>Component</small></a></k-filter-item>
|
|
35
|
+
<k-filter-item filter-keywords="show more showmore components"><a href="./components/show-more.html">Show More<br><small>Component</small></a></k-filter-item>
|
|
36
|
+
<k-filter-item filter-keywords="side menu sidemenu components"><a href="./components/side-menu.html">Side Menu<br><small>Component</small></a></k-filter-item>
|
|
37
|
+
<k-filter-item filter-keywords="side panel sidepanel components"><a href="./components/side-panel.html">Side Panel<br><small>Component</small></a></k-filter-item>
|
|
38
|
+
<k-filter-item filter-keywords="sortable drag drop sort components"><a href="./components/sortable.html">Sortable<br><small>Component</small></a></k-filter-item>
|
|
39
|
+
<k-filter-item filter-keywords="spinner loading components"><a href="./components/spinner.html">Spinner<br><small>Component</small></a></k-filter-item>
|
|
40
|
+
<k-filter-item filter-keywords="split pane components"><a href="./components/split.html">Split<br><small>Component</small></a></k-filter-item>
|
|
41
|
+
<k-filter-item filter-keywords="table data grid components"><a href="./components/table.html">Table<br><small>Component</small></a></k-filter-item>
|
|
42
|
+
<k-filter-item filter-keywords="table controls advanced example"><a href="./components/tableControls.html">Table — Controls<br><small>Advanced Example</small></a></k-filter-item>
|
|
43
|
+
<k-filter-item filter-keywords="table custom fields advanced example"><a href="./components/tableCustomFields.html">Table — Custom Fields<br><small>Advanced Example</small></a></k-filter-item>
|
|
44
|
+
<k-filter-item filter-keywords="table fetch records advanced example"><a href="./components/tableFetchRecords.html">Table — Fetch Records<br><small>Advanced Example</small></a></k-filter-item>
|
|
45
|
+
<k-filter-item filter-keywords="table field sort hide advanced example"><a href="./components/tableFieldSortHide.html">Table — Field Sort & Hide<br><small>Advanced Example</small></a></k-filter-item>
|
|
46
|
+
<k-filter-item filter-keywords="table pagination advanced example"><a href="./components/tablePagination.html">Table — Pagination<br><small>Advanced Example</small></a></k-filter-item>
|
|
47
|
+
<k-filter-item filter-keywords="table record editing advanced example"><a href="./components/tableRecordEditing.html">Table — Record Editing<br><small>Advanced Example</small></a></k-filter-item>
|
|
48
|
+
<k-filter-item filter-keywords="table record filtering advanced example"><a href="./components/tableRecordFiltering.html">Table — Record Filtering<br><small>Advanced Example</small></a></k-filter-item>
|
|
49
|
+
<k-filter-item filter-keywords="table record hiding advanced example"><a href="./components/tableRecordHiding.html">Table — Record Hiding<br><small>Advanced Example</small></a></k-filter-item>
|
|
50
|
+
<k-filter-item filter-keywords="table record searching advanced example"><a href="./components/tableRecordSearching.html">Table — Record Searching<br><small>Advanced Example</small></a></k-filter-item>
|
|
51
|
+
<k-filter-item filter-keywords="table record selection advanced example"><a href="./components/tableRecordSelection.html">Table — Record Selection<br><small>Advanced Example</small></a></k-filter-item>
|
|
52
|
+
<k-filter-item filter-keywords="table row controls advanced example"><a href="./components/tableRowControls.html">Table — Row Controls<br><small>Advanced Example</small></a></k-filter-item>
|
|
53
|
+
<k-filter-item filter-keywords="table server sync advanced example"><a href="./components/tableServerSync.html">Table — Server Sync<br><small>Advanced Example</small></a></k-filter-item>
|
|
54
|
+
<k-filter-item filter-keywords="table sorting advanced example"><a href="./components/tableSorting.html">Table — Sorting<br><small>Advanced Example</small></a></k-filter-item>
|
|
55
|
+
<k-filter-item filter-keywords="tabs tab panel components"><a href="./components/tabs.html">Tabs<br><small>Component</small></a></k-filter-item>
|
|
56
|
+
<k-filter-item filter-keywords="tags tag input components"><a href="./components/tags.html">Tags<br><small>Component</small></a></k-filter-item>
|
|
57
|
+
<k-filter-item filter-keywords="toast notification alert components"><a href="./components/toast.html">Toast<br><small>Component</small></a></k-filter-item>
|
|
58
|
+
<k-filter-item filter-keywords="theme switcher dark light components"><a href="./components/theme-switcher.html">Theme Switcher<br><small>Component</small></a></k-filter-item>
|
|
59
|
+
<k-filter-item filter-keywords="timestamp date time components"><a href="./components/timestamp.html">Timestamp<br><small>Component</small></a></k-filter-item>
|
|
60
|
+
<k-filter-item filter-keywords="toggle switch checkbox components"><a href="./components/toggle.html">Toggle<br><small>Component</small></a></k-filter-item>
|
|
61
|
+
<k-filter-item filter-keywords="tree treeview components"><a href="./components/tree.html">Tree<br><small>Component</small></a></k-filter-item>
|
|
62
|
+
<k-filter-item filter-keywords="shadow component base"><a href="./components/shadow-component.html">Shadow Component<br><small>Base Component</small></a></k-filter-item>
|
|
63
|
+
<k-filter-item filter-keywords="light component base"><a href="./components/light-component.html">Light Component<br><small>Base Component</small></a></k-filter-item>
|
|
64
|
+
<k-filter-item filter-keywords="hybrid component base"><a href="./components/hybrid-component.html">Hybrid Component<br><small>Base Component</small></a></k-filter-item>
|
|
65
|
+
<k-filter-item filter-keywords="cookie utils utility"><a href="./utils/cookie.html">cookie<br><small>Utility</small></a></k-filter-item>
|
|
66
|
+
<k-filter-item filter-keywords="context utils utility"><a href="./utils/context.html">context<br><small>Utility</small></a></k-filter-item>
|
|
67
|
+
<k-filter-item filter-keywords="debounce utils utility"><a href="./utils/debounce.html">debounce<br><small>Utility</small></a></k-filter-item>
|
|
68
|
+
<k-filter-item filter-keywords="drag utils utility"><a href="./utils/drag.html">drag<br><small>Utility</small></a></k-filter-item>
|
|
69
|
+
<k-filter-item filter-keywords="formattimestamp timestamp format date time utils utility"><a href="./utils/formatTimestamp.html">formatTimestamp<br><small>Utility</small></a></k-filter-item>
|
|
70
|
+
<k-filter-item filter-keywords="object utils utility"><a href="./utils/object.html">object<br><small>Utility</small></a></k-filter-item>
|
|
71
|
+
<k-filter-item filter-keywords="propconverters prop converters utils utility"><a href="./utils/propConverters.html">propConverters<br><small>Utility</small></a></k-filter-item>
|
|
72
|
+
<k-filter-item filter-keywords="string utils utility"><a href="./utils/string.html">string<br><small>Utility</small></a></k-filter-item>
|
|
73
|
+
<k-filter-item filter-keywords="theme utils utility"><a href="./utils/theme.html">theme<br><small>Utility</small></a></k-filter-item>
|
|
74
|
+
<k-filter-item filter-keywords="type utils utility"><a href="./utils/type.html">type<br><small>Utility</small></a></k-filter-item>
|
|
75
|
+
<k-filter-item filter-keywords="wait async utils utility"><a href="./utils/wait.html">wait<br><small>Utility</small></a></k-filter-item>
|
|
76
|
+
</k-filter-list>
|
|
77
|
+
</div>
|
|
78
|
+
</div>
|
|
18
79
|
<div class="flex"></div>
|
|
19
80
|
<a href="https://github.com/dustinpoissant/kempo-ui?tab=License-1-ov-file#creative-commons-attribution-noncommercial-sharealike-20" target="_blank"><k-icon name="license"></k-icont></a>
|
|
20
81
|
<a href="https://github.com/dustinpoissant/kempo-ui" target="_blank"><k-icon name="github-mark"></k-icont></a>
|
|
@@ -37,6 +98,7 @@
|
|
|
37
98
|
<a href="./components/color-picker.html">ColorPicker</a>
|
|
38
99
|
<a href="./components/content-slider.html">Content Slider</a>
|
|
39
100
|
<a href="./components/dialog.html">Dialog</a>
|
|
101
|
+
<a href="./components/filter-list.html">Filter List</a>
|
|
40
102
|
<a href="./components/focus-capture.html">FocusCapture</a>
|
|
41
103
|
<a href="./components/html-editor.html">HTML Editor</a>
|
|
42
104
|
<a href="./components/icon.html">Icon</a>
|
|
@@ -81,6 +143,13 @@
|
|
|
81
143
|
</div>
|
|
82
144
|
</menu>
|
|
83
145
|
</k-side-menu>
|
|
146
|
+
<style>
|
|
147
|
+
#navSearchList a{display:block;padding:.25rem .5rem;border-radius:var(--radius);text-decoration:none;color:var(--tc);}
|
|
148
|
+
#navSearchList a:hover{background:rgba(128,128,128,.15);}
|
|
149
|
+
#navSearchList a small{display:block;color:var(--tc_muted);}
|
|
150
|
+
</style>
|
|
151
|
+
<script src="./src/components/FilterList.js" type="module"></script>
|
|
152
|
+
<script src="./src/components/FilterItem.js" type="module"></script>
|
|
84
153
|
<script src="./src/components/SideMenu.js" type="module"></script>
|
|
85
154
|
<script src="./src/components/Icon.js" type="module"></script>
|
|
86
155
|
<script src="./src/components/ThemeSwitcher.js" type="module"></script>
|
package/docs/nav.inc.js
CHANGED
|
@@ -1,16 +1,66 @@
|
|
|
1
|
+
import debounce from './src/utils/debounce.js';
|
|
2
|
+
|
|
1
3
|
document.getElementById('toggleNavSideMenu').addEventListener('click', async () => {
|
|
2
4
|
await window.customElements.whenDefined('k-side-menu');
|
|
3
5
|
document.getElementById('navSideMenu').toggle();
|
|
4
6
|
});
|
|
7
|
+
|
|
5
8
|
document.addEventListener('click', function(e) {
|
|
6
|
-
if
|
|
9
|
+
if(e.target.matches('a[href^="#"]')) {
|
|
7
10
|
e.preventDefault();
|
|
8
11
|
const targetId = e.target.getAttribute('href').replace('#', '');
|
|
9
12
|
const target = document.getElementById(targetId);
|
|
10
|
-
if
|
|
13
|
+
if(target) {
|
|
11
14
|
target.scrollIntoView({ behavior: 'smooth' });
|
|
12
15
|
const url = window.location.pathname + window.location.search + '#' + targetId;
|
|
13
16
|
history.replaceState(null, '', url);
|
|
14
17
|
}
|
|
15
18
|
}
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
/*
|
|
22
|
+
Nav Search
|
|
23
|
+
*/
|
|
24
|
+
const searchInput = document.getElementById('navSearchInput');
|
|
25
|
+
const searchDropdown = document.getElementById('navSearchDropdown');
|
|
26
|
+
let navSearchList = null;
|
|
27
|
+
|
|
28
|
+
const openSearch = async () => {
|
|
29
|
+
await customElements.whenDefined('k-filter-list');
|
|
30
|
+
if(!navSearchList) navSearchList = document.getElementById('navSearchList');
|
|
31
|
+
navSearchList.filter(searchInput.value);
|
|
32
|
+
searchDropdown.hidden = false;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const closeSearch = () => {
|
|
36
|
+
searchDropdown.hidden = true;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
searchInput.addEventListener('focus', openSearch);
|
|
40
|
+
|
|
41
|
+
searchInput.addEventListener('input', debounce(e => {
|
|
42
|
+
navSearchList?.filter(e.target.value);
|
|
43
|
+
}, 150));
|
|
44
|
+
|
|
45
|
+
searchInput.addEventListener('keydown', e => {
|
|
46
|
+
if(e.key === 'Escape') {
|
|
47
|
+
searchInput.value = '';
|
|
48
|
+
searchInput.blur();
|
|
49
|
+
closeSearch();
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
searchInput.addEventListener('blur', () => {
|
|
54
|
+
setTimeout(closeSearch, 150);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
document.addEventListener('keydown', e => {
|
|
58
|
+
const active = document.activeElement;
|
|
59
|
+
const tag = active?.tagName;
|
|
60
|
+
if(tag === 'INPUT' || tag === 'TEXTAREA' || active?.isContentEditable) return;
|
|
61
|
+
if(e.metaKey || e.ctrlKey || e.altKey) return;
|
|
62
|
+
if(e.key.length === 1) {
|
|
63
|
+
searchInput.focus();
|
|
64
|
+
// Don't preventDefault — let the keystroke land in the input naturally
|
|
65
|
+
}
|
|
16
66
|
});
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import ShadowComponent from"./ShadowComponent.js";import{html,css}from"../lit-all.min.js";export default class FilterItem extends ShadowComponent{render(){return html`<slot></slot>`}static styles=css`
|
|
2
|
+
:host {
|
|
3
|
+
display: contents;
|
|
4
|
+
}
|
|
5
|
+
:host([hidden]) {
|
|
6
|
+
display: none !important;
|
|
7
|
+
}
|
|
8
|
+
`}customElements.define("k-filter-item",FilterItem);
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import ShadowComponent from"./ShadowComponent.js";import{html,css}from"../lit-all.min.js";export default class FilterList extends ShadowComponent{render(){return html`<slot></slot>`}filter(t){const e=t.toLowerCase().split(/\s+/).filter(t=>t.length>0);this.querySelectorAll("k-filter-item").forEach(t=>{const s=(t.getAttribute("filter-keywords")||"").toLowerCase();t.hidden=e.length>0&&!e.every(t=>s.includes(t))})}static styles=css`
|
|
2
|
+
:host {
|
|
3
|
+
display: block;
|
|
4
|
+
}
|
|
5
|
+
`}customElements.define("k-filter-list",FilterList);
|
package/icons/search.svg
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path fill="currentColor" d="M784-120 532-372q-30 24-69 38t-83 14q-109 0-184.5-75.5T120-580q0-109 75.5-184.5T380-840q109 0 184.5 75.5T640-580q0 44-14 83t-38 69l252 252-56 56ZM380-400q75 0 127.5-52.5T560-580q0-75-52.5-127.5T380-760q-75 0-127.5 52.5T200-580q0 75 52.5 127.5T380-400Z"/></svg>
|
package/package.json
CHANGED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import ShadowComponent from './ShadowComponent.js';
|
|
2
|
+
import { html, css } from '../lit-all.min.js';
|
|
3
|
+
|
|
4
|
+
export default class FilterItem extends ShadowComponent {
|
|
5
|
+
/*
|
|
6
|
+
Rendering
|
|
7
|
+
*/
|
|
8
|
+
render() {
|
|
9
|
+
return html`<slot></slot>`;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/*
|
|
13
|
+
Styles
|
|
14
|
+
*/
|
|
15
|
+
static styles = css`
|
|
16
|
+
:host {
|
|
17
|
+
display: contents;
|
|
18
|
+
}
|
|
19
|
+
:host([hidden]) {
|
|
20
|
+
display: none !important;
|
|
21
|
+
}
|
|
22
|
+
`;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
customElements.define('k-filter-item', FilterItem);
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import ShadowComponent from './ShadowComponent.js';
|
|
2
|
+
import { html, css } from '../lit-all.min.js';
|
|
3
|
+
|
|
4
|
+
export default class FilterList extends ShadowComponent {
|
|
5
|
+
/*
|
|
6
|
+
Rendering
|
|
7
|
+
*/
|
|
8
|
+
render() {
|
|
9
|
+
return html`<slot></slot>`;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/*
|
|
13
|
+
Public Methods
|
|
14
|
+
*/
|
|
15
|
+
filter(term) {
|
|
16
|
+
const words = term.toLowerCase().split(/\s+/).filter(w => w.length > 0);
|
|
17
|
+
this.querySelectorAll('k-filter-item').forEach(item => {
|
|
18
|
+
const keywords = (item.getAttribute('filter-keywords') || '').toLowerCase();
|
|
19
|
+
item.hidden = words.length > 0 && !words.every(w => keywords.includes(w));
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/*
|
|
24
|
+
Styles
|
|
25
|
+
*/
|
|
26
|
+
static styles = css`
|
|
27
|
+
:host {
|
|
28
|
+
display: block;
|
|
29
|
+
}
|
|
30
|
+
`;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
customElements.define('k-filter-list', FilterList);
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import FilterItem from '../../src/components/FilterItem.js';
|
|
2
|
+
|
|
3
|
+
const createFilterItem = async (content = 'item') => {
|
|
4
|
+
const container = document.createElement('div');
|
|
5
|
+
container.innerHTML = `<k-filter-item>${content}</k-filter-item>`;
|
|
6
|
+
document.body.appendChild(container);
|
|
7
|
+
const el = container.querySelector('k-filter-item');
|
|
8
|
+
await el.updateComplete;
|
|
9
|
+
return { container, el };
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
const cleanup = (container) => {
|
|
13
|
+
if(container && container.parentNode){
|
|
14
|
+
container.parentNode.removeChild(container);
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export default {
|
|
19
|
+
/*
|
|
20
|
+
Element Creation
|
|
21
|
+
*/
|
|
22
|
+
'should create filter-item element': async ({pass, fail}) => {
|
|
23
|
+
const { container, el } = await createFilterItem();
|
|
24
|
+
if(!el){
|
|
25
|
+
cleanup(container);
|
|
26
|
+
return fail('k-filter-item element should be created');
|
|
27
|
+
}
|
|
28
|
+
if(!(el instanceof FilterItem)){
|
|
29
|
+
cleanup(container);
|
|
30
|
+
return fail('Element should be instance of FilterItem');
|
|
31
|
+
}
|
|
32
|
+
cleanup(container);
|
|
33
|
+
pass('FilterItem element created correctly');
|
|
34
|
+
},
|
|
35
|
+
|
|
36
|
+
'should have shadow root': async ({pass, fail}) => {
|
|
37
|
+
const { container, el } = await createFilterItem();
|
|
38
|
+
if(!el.shadowRoot){
|
|
39
|
+
cleanup(container);
|
|
40
|
+
return fail('FilterItem should have shadow root');
|
|
41
|
+
}
|
|
42
|
+
cleanup(container);
|
|
43
|
+
pass('FilterItem has shadow root');
|
|
44
|
+
},
|
|
45
|
+
|
|
46
|
+
/*
|
|
47
|
+
Visibility
|
|
48
|
+
*/
|
|
49
|
+
'should be visible by default': async ({pass, fail}) => {
|
|
50
|
+
const { container, el } = await createFilterItem();
|
|
51
|
+
if(el.hidden){
|
|
52
|
+
cleanup(container);
|
|
53
|
+
return fail('FilterItem should be visible by default');
|
|
54
|
+
}
|
|
55
|
+
cleanup(container);
|
|
56
|
+
pass('FilterItem is visible by default');
|
|
57
|
+
},
|
|
58
|
+
|
|
59
|
+
'setting hidden should hide the element': async ({pass, fail}) => {
|
|
60
|
+
const { container, el } = await createFilterItem();
|
|
61
|
+
el.hidden = true;
|
|
62
|
+
await el.updateComplete;
|
|
63
|
+
const style = getComputedStyle(el);
|
|
64
|
+
if(style.display !== 'none'){
|
|
65
|
+
cleanup(container);
|
|
66
|
+
return fail(`Expected display:none when hidden, got ${style.display}`);
|
|
67
|
+
}
|
|
68
|
+
cleanup(container);
|
|
69
|
+
pass('FilterItem is hidden when hidden attribute is set');
|
|
70
|
+
},
|
|
71
|
+
|
|
72
|
+
'removing hidden should show the element': async ({pass, fail}) => {
|
|
73
|
+
const { container, el } = await createFilterItem();
|
|
74
|
+
el.hidden = true;
|
|
75
|
+
await el.updateComplete;
|
|
76
|
+
el.hidden = false;
|
|
77
|
+
await el.updateComplete;
|
|
78
|
+
const style = getComputedStyle(el);
|
|
79
|
+
if(style.display === 'none'){
|
|
80
|
+
cleanup(container);
|
|
81
|
+
return fail('FilterItem should be visible after removing hidden');
|
|
82
|
+
}
|
|
83
|
+
cleanup(container);
|
|
84
|
+
pass('FilterItem is visible after removing hidden');
|
|
85
|
+
},
|
|
86
|
+
|
|
87
|
+
/*
|
|
88
|
+
filter-keywords attribute
|
|
89
|
+
*/
|
|
90
|
+
'should accept filter-keywords attribute': async ({pass, fail}) => {
|
|
91
|
+
const container = document.createElement('div');
|
|
92
|
+
container.innerHTML = `<k-filter-item filter-keywords="foo bar">content</k-filter-item>`;
|
|
93
|
+
document.body.appendChild(container);
|
|
94
|
+
const el = container.querySelector('k-filter-item');
|
|
95
|
+
await el.updateComplete;
|
|
96
|
+
const kw = el.getAttribute('filter-keywords');
|
|
97
|
+
if(kw !== 'foo bar'){
|
|
98
|
+
cleanup(container);
|
|
99
|
+
return fail(`Expected filter-keywords "foo bar", got "${kw}"`);
|
|
100
|
+
}
|
|
101
|
+
cleanup(container);
|
|
102
|
+
pass('filter-keywords attribute set correctly');
|
|
103
|
+
},
|
|
104
|
+
};
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import FilterList from '../../src/components/FilterList.js';
|
|
2
|
+
import '../../src/components/FilterItem.js';
|
|
3
|
+
|
|
4
|
+
const createFilterList = async (items = []) => {
|
|
5
|
+
const container = document.createElement('div');
|
|
6
|
+
container.innerHTML = `<k-filter-list>${items.map(kw => `<k-filter-item filter-keywords="${kw}">${kw}</k-filter-item>`).join('')}</k-filter-list>`;
|
|
7
|
+
document.body.appendChild(container);
|
|
8
|
+
const el = container.querySelector('k-filter-list');
|
|
9
|
+
await el.updateComplete;
|
|
10
|
+
return { container, el };
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
const cleanup = (container) => {
|
|
14
|
+
if(container && container.parentNode){
|
|
15
|
+
container.parentNode.removeChild(container);
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export default {
|
|
20
|
+
/*
|
|
21
|
+
Element Creation
|
|
22
|
+
*/
|
|
23
|
+
'should create filter-list element': async ({pass, fail}) => {
|
|
24
|
+
const { container, el } = await createFilterList();
|
|
25
|
+
if(!el){
|
|
26
|
+
cleanup(container);
|
|
27
|
+
return fail('k-filter-list element should be created');
|
|
28
|
+
}
|
|
29
|
+
if(!(el instanceof FilterList)){
|
|
30
|
+
cleanup(container);
|
|
31
|
+
return fail('Element should be instance of FilterList');
|
|
32
|
+
}
|
|
33
|
+
cleanup(container);
|
|
34
|
+
pass('FilterList element created correctly');
|
|
35
|
+
},
|
|
36
|
+
|
|
37
|
+
'should have shadow root': async ({pass, fail}) => {
|
|
38
|
+
const { container, el } = await createFilterList();
|
|
39
|
+
if(!el.shadowRoot){
|
|
40
|
+
cleanup(container);
|
|
41
|
+
return fail('FilterList should have shadow root');
|
|
42
|
+
}
|
|
43
|
+
cleanup(container);
|
|
44
|
+
pass('FilterList has shadow root');
|
|
45
|
+
},
|
|
46
|
+
|
|
47
|
+
/*
|
|
48
|
+
filter() — empty term
|
|
49
|
+
*/
|
|
50
|
+
'filter("") should show all items': async ({pass, fail}) => {
|
|
51
|
+
const { container, el } = await createFilterList(['apple', 'banana', 'cherry']);
|
|
52
|
+
el.filter('');
|
|
53
|
+
const items = [...el.querySelectorAll('k-filter-item')];
|
|
54
|
+
const hidden = items.filter(i => i.hidden);
|
|
55
|
+
if(hidden.length !== 0){
|
|
56
|
+
cleanup(container);
|
|
57
|
+
return fail(`Expected 0 hidden items, got ${hidden.length}`);
|
|
58
|
+
}
|
|
59
|
+
cleanup(container);
|
|
60
|
+
pass('All items visible when term is empty');
|
|
61
|
+
},
|
|
62
|
+
|
|
63
|
+
/*
|
|
64
|
+
filter() — single word
|
|
65
|
+
*/
|
|
66
|
+
'filter("apple") should hide non-matching items': async ({pass, fail}) => {
|
|
67
|
+
const { container, el } = await createFilterList(['apple', 'banana', 'cherry']);
|
|
68
|
+
el.filter('apple');
|
|
69
|
+
const banana = el.querySelector('[filter-keywords="banana"]');
|
|
70
|
+
const cherry = el.querySelector('[filter-keywords="cherry"]');
|
|
71
|
+
if(!banana.hidden || !cherry.hidden){
|
|
72
|
+
cleanup(container);
|
|
73
|
+
return fail('Non-matching items should be hidden');
|
|
74
|
+
}
|
|
75
|
+
cleanup(container);
|
|
76
|
+
pass('Non-matching items are hidden');
|
|
77
|
+
},
|
|
78
|
+
|
|
79
|
+
'filter("apple") should show matching item': async ({pass, fail}) => {
|
|
80
|
+
const { container, el } = await createFilterList(['apple', 'banana', 'cherry']);
|
|
81
|
+
el.filter('apple');
|
|
82
|
+
const apple = el.querySelector('[filter-keywords="apple"]');
|
|
83
|
+
if(apple.hidden){
|
|
84
|
+
cleanup(container);
|
|
85
|
+
return fail('Matching item should not be hidden');
|
|
86
|
+
}
|
|
87
|
+
cleanup(container);
|
|
88
|
+
pass('Matching item is visible');
|
|
89
|
+
},
|
|
90
|
+
|
|
91
|
+
/*
|
|
92
|
+
filter() — multi-word (AND logic)
|
|
93
|
+
*/
|
|
94
|
+
'filter("apple pie") should require all words to match': async ({pass, fail}) => {
|
|
95
|
+
const { container, el } = await createFilterList(['apple pie', 'apple', 'pie']);
|
|
96
|
+
el.filter('apple pie');
|
|
97
|
+
const applePie = el.querySelector('[filter-keywords="apple pie"]');
|
|
98
|
+
const appleOnly = el.querySelector('[filter-keywords="apple"]');
|
|
99
|
+
const pieOnly = el.querySelector('[filter-keywords="pie"]');
|
|
100
|
+
if(applePie.hidden){
|
|
101
|
+
cleanup(container);
|
|
102
|
+
return fail('Item matching all words should be visible');
|
|
103
|
+
}
|
|
104
|
+
if(!appleOnly.hidden || !pieOnly.hidden){
|
|
105
|
+
cleanup(container);
|
|
106
|
+
return fail('Items matching only one word should be hidden');
|
|
107
|
+
}
|
|
108
|
+
cleanup(container);
|
|
109
|
+
pass('Multi-word filter requires all words to match');
|
|
110
|
+
},
|
|
111
|
+
|
|
112
|
+
/*
|
|
113
|
+
filter() — case insensitivity
|
|
114
|
+
*/
|
|
115
|
+
'filter() should be case-insensitive': async ({pass, fail}) => {
|
|
116
|
+
const { container, el } = await createFilterList(['Apple', 'banana']);
|
|
117
|
+
el.filter('APPLE');
|
|
118
|
+
const apple = el.querySelector('[filter-keywords="Apple"]');
|
|
119
|
+
if(apple.hidden){
|
|
120
|
+
cleanup(container);
|
|
121
|
+
return fail('Matching should be case-insensitive');
|
|
122
|
+
}
|
|
123
|
+
cleanup(container);
|
|
124
|
+
pass('filter() is case-insensitive');
|
|
125
|
+
},
|
|
126
|
+
|
|
127
|
+
/*
|
|
128
|
+
filter() — no keywords attribute
|
|
129
|
+
*/
|
|
130
|
+
'items with no filter-keywords should be hidden when term is provided': async ({pass, fail}) => {
|
|
131
|
+
const container = document.createElement('div');
|
|
132
|
+
container.innerHTML = `<k-filter-list><k-filter-item>no keywords</k-filter-item></k-filter-list>`;
|
|
133
|
+
document.body.appendChild(container);
|
|
134
|
+
const el = container.querySelector('k-filter-list');
|
|
135
|
+
await el.updateComplete;
|
|
136
|
+
el.filter('something');
|
|
137
|
+
const item = el.querySelector('k-filter-item');
|
|
138
|
+
if(!item.hidden){
|
|
139
|
+
cleanup(container);
|
|
140
|
+
return fail('Item with no keywords should be hidden when term is given');
|
|
141
|
+
}
|
|
142
|
+
cleanup(container);
|
|
143
|
+
pass('Item with no keywords is hidden when term is provided');
|
|
144
|
+
},
|
|
145
|
+
|
|
146
|
+
/*
|
|
147
|
+
filter() — restore all after filtering
|
|
148
|
+
*/
|
|
149
|
+
'filter("") after filtering should restore all items': async ({pass, fail}) => {
|
|
150
|
+
const { container, el } = await createFilterList(['apple', 'banana']);
|
|
151
|
+
el.filter('apple');
|
|
152
|
+
el.filter('');
|
|
153
|
+
const items = [...el.querySelectorAll('k-filter-item')];
|
|
154
|
+
const hidden = items.filter(i => i.hidden);
|
|
155
|
+
if(hidden.length !== 0){
|
|
156
|
+
cleanup(container);
|
|
157
|
+
return fail(`Expected all items visible after clearing filter, got ${hidden.length} hidden`);
|
|
158
|
+
}
|
|
159
|
+
cleanup(container);
|
|
160
|
+
pass('All items restored after clearing filter');
|
|
161
|
+
},
|
|
162
|
+
};
|