@johnhenry/cyclable 0.0.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.
- package/LICENSE +21 -0
- package/README.md +173 -0
- package/package.json +34 -0
- package/src/class-cycler.button.component/global.mjs +4 -0
- package/src/class-cycler.button.component/index.mjs +46 -0
- package/src/class-cycler.button.component/readme.md +34 -0
- package/src/class-cycler.component/global.mjs +2 -0
- package/src/class-cycler.component/index.mjs +40 -0
- package/src/class-cycler.component/readme.md +32 -0
- package/src/localstorage-class-cycler/index.mjs +14 -0
- package/src/localstorage-class-cycler/readme.md +18 -0
- package/src/localstorage-cycler/class.mjs +11 -0
- package/src/localstorage-cycler/index.mjs +31 -0
- package/src/localstorage-cycler/readme.md +54 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 John Henry
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
# cyclable
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@johnhenry/cyclable)
|
|
4
|
+
[](https://github.com/johnhenry/cyclable/actions/workflows/ci.yml)
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
|
|
7
|
+
Full documentation: [opensource.johnhenry.me/cyclable](https://opensource.johnhenry.me/cyclable/)
|
|
8
|
+
|
|
9
|
+
> **Provenance:** originally four individually-versioned modules under
|
|
10
|
+
> [`johnhenry/lib`](https://github.com/johnhenry/lib)'s `js/` directory
|
|
11
|
+
> (`js/localstorage-cycler/0.0.0/`, `js/localstorage-class-cycler/0.0.0/`,
|
|
12
|
+
> `js/class-cycler.component/0.0.0/`, `js/class-cycler.button.component/0.0.0/`),
|
|
13
|
+
> then briefly part of [`@johnhenry/domkit`](https://github.com/johnhenry/domkit)'s
|
|
14
|
+
> ~33-module toolkit, now extracted into this standalone package because
|
|
15
|
+
> the four form a real, coherent family — one engine plus wrapper variants
|
|
16
|
+
> — rather than an unrelated grab-bag of widgets. `domkit` no longer
|
|
17
|
+
> carries this functionality. See `## Family` below and `CHANGELOG.md` for
|
|
18
|
+
> the full history.
|
|
19
|
+
|
|
20
|
+
A localStorage-backed value cycler: **one engine, three consumption
|
|
21
|
+
shapes.** `localStorageCycler` cycles a `localStorage`-backed value
|
|
22
|
+
through a fixed list of strings and calls an optional change handler.
|
|
23
|
+
`classStorageCycler` wraps it to apply the cycled value as a CSS class on
|
|
24
|
+
a DOM element. `<class-cycler>` and
|
|
25
|
+
`<button is="class-cycler-button">` are two ready-made custom elements
|
|
26
|
+
built on that wrapper — a global-function variant and a self-contained
|
|
27
|
+
button variant, respectively. All four modules are independent files
|
|
28
|
+
under `src/`, importable individually with no shared barrel and no build
|
|
29
|
+
step.
|
|
30
|
+
|
|
31
|
+
## Install
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
npm install @johnhenry/cyclable
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## `localstorage-cycler` — the engine
|
|
38
|
+
|
|
39
|
+
Cycle a `localStorage` value through a given list of strings.
|
|
40
|
+
|
|
41
|
+
```javascript
|
|
42
|
+
import localStorageCycler from "@johnhenry/cyclable/localstorage-cycler/index.mjs";
|
|
43
|
+
const updateLocalStorage = localStorageCycler("my-key", "a", "b", "c");
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
The call to `localStorageCycler` checks for the existence of the key
|
|
47
|
+
(`"my-key"`) in `localStorage`, and sets it to the first value (`"a"`) if
|
|
48
|
+
not already set.
|
|
49
|
+
|
|
50
|
+
When called, the returned `updateLocalStorage` function cycles the value
|
|
51
|
+
associated with the key in `localStorage` through the given values (`"a"`,
|
|
52
|
+
`"b"`, and `"c"`).
|
|
53
|
+
|
|
54
|
+
`updateLocalStorage` returns an object with the following keys:
|
|
55
|
+
|
|
56
|
+
- `key` — the associated local storage key
|
|
57
|
+
- `value` — the current value of the local storage item
|
|
58
|
+
- `index` — the current index of the local storage item
|
|
59
|
+
- `result` — the result of a handler, if passed (see below)
|
|
60
|
+
|
|
61
|
+
### Change handler
|
|
62
|
+
|
|
63
|
+
To react to the change, pass an optional change handler as the second
|
|
64
|
+
parameter to `localStorageCycler`.
|
|
65
|
+
|
|
66
|
+
```javascript
|
|
67
|
+
const onChange = ({ value, key, index, events }) =>
|
|
68
|
+
console.log({ value, key, index, events });
|
|
69
|
+
const updateLocalStorage = localStorageCycler(
|
|
70
|
+
"my-key",
|
|
71
|
+
onChange,
|
|
72
|
+
"a",
|
|
73
|
+
"b",
|
|
74
|
+
"c"
|
|
75
|
+
);
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
The handler takes four parameters:
|
|
79
|
+
|
|
80
|
+
- the same `key`, `value`, and `index` parameters returned from calling
|
|
81
|
+
`updateLocalStorage`
|
|
82
|
+
- an `events` parameter — an array of everything passed into the
|
|
83
|
+
`updateLocalStorage` function, or an `init` `CustomEvent` if fired from
|
|
84
|
+
the initial call to `localStorageCycler`
|
|
85
|
+
|
|
86
|
+
## `localstorage-class-cycler` — apply the cycled value as a class
|
|
87
|
+
|
|
88
|
+
Cycle a `localStorage` value through a given list of strings, and render
|
|
89
|
+
the cycled value as a class on a given element.
|
|
90
|
+
|
|
91
|
+
```javascript
|
|
92
|
+
import classStorageCycler from "@johnhenry/cyclable/localstorage-class-cycler/index.mjs";
|
|
93
|
+
const updateBodyClass = classStorageCycler(
|
|
94
|
+
document.body,
|
|
95
|
+
"my-key",
|
|
96
|
+
"a",
|
|
97
|
+
"b",
|
|
98
|
+
"c"
|
|
99
|
+
);
|
|
100
|
+
updateBodyClass();
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## `class-cycler.component` — global-function custom element
|
|
104
|
+
|
|
105
|
+
Exposes a `localstorage-class-cycler` instance as a named global
|
|
106
|
+
function, so it can be called from anywhere (e.g. a plain `<button
|
|
107
|
+
onclick="...">`).
|
|
108
|
+
|
|
109
|
+
| Attribute | Description |
|
|
110
|
+
|---|---|
|
|
111
|
+
| `global` | Name to assign the cycler function to on `globalThis` (required — removing this attribute, or disconnecting the element, unassigns it) |
|
|
112
|
+
| `selector` | Selector for the element whose classes get cycled. Defaults to `body` |
|
|
113
|
+
| `storage-key` | localStorage key the current class value persists under |
|
|
114
|
+
| `classes` | Comma-delimited list of classes to cycle through |
|
|
115
|
+
|
|
116
|
+
```html
|
|
117
|
+
<script
|
|
118
|
+
type="module"
|
|
119
|
+
src="https://esm.sh/@johnhenry/cyclable/class-cycler.component/global.mjs"
|
|
120
|
+
></script>
|
|
121
|
+
<class-cycler
|
|
122
|
+
global="cycleTheme"
|
|
123
|
+
selector="body"
|
|
124
|
+
storage-key="theme"
|
|
125
|
+
classes="light,dark,system"
|
|
126
|
+
></class-cycler>
|
|
127
|
+
<button onclick="cycleTheme()">Toggle theme</button>
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## `class-cycler.button.component` — self-contained button custom element
|
|
131
|
+
|
|
132
|
+
A `<button is="class-cycler-button">` that cycles the classes of one or
|
|
133
|
+
more target elements every time it's clicked.
|
|
134
|
+
|
|
135
|
+
| Attribute | Description |
|
|
136
|
+
|---|---|
|
|
137
|
+
| `select` | Selector for a single target element. Defaults to `html` if neither `select` nor `select-all` is set |
|
|
138
|
+
| `select-all` | Selector for multiple target elements (all matches get cycled together) |
|
|
139
|
+
| `storage-key` | localStorage key the current class value persists under |
|
|
140
|
+
| `classes` | Comma-delimited list of classes to cycle through |
|
|
141
|
+
|
|
142
|
+
```html
|
|
143
|
+
<script
|
|
144
|
+
type="module"
|
|
145
|
+
src="https://esm.sh/@johnhenry/cyclable/class-cycler.button.component/global.mjs"
|
|
146
|
+
></script>
|
|
147
|
+
<button
|
|
148
|
+
is="class-cycler-button"
|
|
149
|
+
select="body"
|
|
150
|
+
storage-key="theme"
|
|
151
|
+
classes="light,dark,system"
|
|
152
|
+
>
|
|
153
|
+
Toggle theme
|
|
154
|
+
</button>
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## Family
|
|
158
|
+
|
|
159
|
+
- [`@johnhenry/domkit`](https://github.com/johnhenry/domkit) — the
|
|
160
|
+
~33-module DOM/HTML-component toolkit this package was extracted from.
|
|
161
|
+
domkit is a real npm dependency of neither direction of this
|
|
162
|
+
relationship — it does not depend on cyclable, and cyclable does not
|
|
163
|
+
depend on domkit; the four modules here simply no longer live in
|
|
164
|
+
domkit's `src/`.
|
|
165
|
+
- [`@johnhenry/domable`](https://github.com/johnhenry/domable) — the DOM
|
|
166
|
+
⇄ text ⇄ React conversion primitives domkit itself builds on. Unrelated
|
|
167
|
+
to this package's functionality; listed here only because it's the
|
|
168
|
+
other sibling extraction in this same lineage
|
|
169
|
+
(`johnhenry/lib` → `johnhenry/domkit` → standalone package).
|
|
170
|
+
|
|
171
|
+
## License
|
|
172
|
+
|
|
173
|
+
MIT
|
package/package.json
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@johnhenry/cyclable",
|
|
3
|
+
"version": "0.0.0",
|
|
4
|
+
"description": "A localStorage-backed value cycler: one engine, a class-cycler wrapper, and two ready-made custom elements for cycling CSS classes on click.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"exports": {
|
|
7
|
+
"./*": "./src/*"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"src/"
|
|
11
|
+
],
|
|
12
|
+
"scripts": {
|
|
13
|
+
"test": "node scripts/check-syntax.mjs"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"html",
|
|
17
|
+
"dom",
|
|
18
|
+
"web-components",
|
|
19
|
+
"custom-elements",
|
|
20
|
+
"localstorage",
|
|
21
|
+
"cycler",
|
|
22
|
+
"theme-toggle"
|
|
23
|
+
],
|
|
24
|
+
"author": "John Henry",
|
|
25
|
+
"license": "MIT",
|
|
26
|
+
"repository": {
|
|
27
|
+
"type": "git",
|
|
28
|
+
"url": "git+https://github.com/johnhenry/cyclable.git"
|
|
29
|
+
},
|
|
30
|
+
"homepage": "https://opensource.johnhenry.me/cyclable/",
|
|
31
|
+
"engines": {
|
|
32
|
+
"node": ">=26.0.0"
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import classCycler from "../localstorage-class-cycler/index.mjs";
|
|
2
|
+
|
|
3
|
+
const DEFAULT_SELECTOR = "html";
|
|
4
|
+
export default class extends globalThis.HTMLButtonElement {
|
|
5
|
+
updateBound = null;
|
|
6
|
+
constructor() {
|
|
7
|
+
super();
|
|
8
|
+
}
|
|
9
|
+
connectedCallback() {
|
|
10
|
+
this.init();
|
|
11
|
+
this.updateBound = this.update.bind(this);
|
|
12
|
+
this.addEventListener("click", this.updateBound);
|
|
13
|
+
}
|
|
14
|
+
disconnectedCallback() {
|
|
15
|
+
this.removeEventListener("click", this.updateBound);
|
|
16
|
+
}
|
|
17
|
+
update() {
|
|
18
|
+
this.classCycler?.();
|
|
19
|
+
}
|
|
20
|
+
init() {
|
|
21
|
+
const selectors = [];
|
|
22
|
+
if (this.getAttribute("select")) {
|
|
23
|
+
selectors.push(document.querySelector(this.getAttribute("select")));
|
|
24
|
+
} else if (this.getAttribute("select-all")) {
|
|
25
|
+
selectors.push(
|
|
26
|
+
...document.querySelectorAll(this.getAttribute("select-all"))
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
if (
|
|
30
|
+
!selectors.length &&
|
|
31
|
+
this.getAttribute("select") !== "" &&
|
|
32
|
+
this.getAttribute("select-all") !== ""
|
|
33
|
+
) {
|
|
34
|
+
selectors.push(document.querySelector(DEFAULT_SELECTOR));
|
|
35
|
+
}
|
|
36
|
+
const storageKey = this.getAttribute("storage-key");
|
|
37
|
+
const classes = (this.getAttribute("classes") || "").split(",");
|
|
38
|
+
this.classCycler = classCycler(selectors, storageKey, ...classes);
|
|
39
|
+
}
|
|
40
|
+
static get observedAttributes() {
|
|
41
|
+
return ["select", "storage-key", "select-all", "classes"];
|
|
42
|
+
}
|
|
43
|
+
attributeChangedCallback() {
|
|
44
|
+
this.init();
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Class Cycler Button
|
|
2
|
+
|
|
3
|
+
A `<button is="class-cycler-button">` that cycles the classes of one or
|
|
4
|
+
more target elements every time it's clicked, via
|
|
5
|
+
[localstorage-class-cycler](../localstorage-class-cycler/readme.md).
|
|
6
|
+
Self-contained button variant — see
|
|
7
|
+
[class-cycler.component](../class-cycler.component/readme.md)
|
|
8
|
+
for a global-function variant callable from anywhere.
|
|
9
|
+
|
|
10
|
+
## Attributes
|
|
11
|
+
|
|
12
|
+
| Attribute | Description |
|
|
13
|
+
|---|---|
|
|
14
|
+
| `select` | Selector for a single target element. Defaults to `html` if neither `select` nor `select-all` is set |
|
|
15
|
+
| `select-all` | Selector for multiple target elements (all matches get cycled together) |
|
|
16
|
+
| `storage-key` | localStorage key the current class value persists under |
|
|
17
|
+
| `classes` | Comma-delimited list of classes to cycle through |
|
|
18
|
+
|
|
19
|
+
## Usage
|
|
20
|
+
|
|
21
|
+
```html
|
|
22
|
+
<script
|
|
23
|
+
type="module"
|
|
24
|
+
src="https://esm.sh/@johnhenry/domkit/class-cycler.button.component/global.mjs"
|
|
25
|
+
></script>
|
|
26
|
+
<button
|
|
27
|
+
is="class-cycler-button"
|
|
28
|
+
select="body"
|
|
29
|
+
storage-key="theme"
|
|
30
|
+
classes="light,dark,system"
|
|
31
|
+
>
|
|
32
|
+
Toggle theme
|
|
33
|
+
</button>
|
|
34
|
+
```
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import classCycler from "../localstorage-class-cycler/index.mjs";
|
|
2
|
+
const DEFAULT_SELECTOR = "body";
|
|
3
|
+
export default class extends globalThis.HTMLElement {
|
|
4
|
+
constructor() {
|
|
5
|
+
super();
|
|
6
|
+
}
|
|
7
|
+
connectedCallback() {
|
|
8
|
+
this.reset();
|
|
9
|
+
}
|
|
10
|
+
reset() {
|
|
11
|
+
const global = this.getAttribute("global");
|
|
12
|
+
if (global) {
|
|
13
|
+
const selector = this.getAttribute("selector") || DEFAULT_SELECTOR;
|
|
14
|
+
const storageKey = this.getAttribute("storage-key");
|
|
15
|
+
const classes = (this.getAttribute("classes") || "").split(",");
|
|
16
|
+
globalThis[global] = classCycler(
|
|
17
|
+
document.querySelector(selector),
|
|
18
|
+
storageKey,
|
|
19
|
+
...classes
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
disconnectedCallback() {
|
|
24
|
+
delete globalThis[this.getAttribute("global")];
|
|
25
|
+
}
|
|
26
|
+
static get observedAttributes() {
|
|
27
|
+
return ["global", "selector", "storage-key", "classes"];
|
|
28
|
+
}
|
|
29
|
+
attributeChangedCallback(name, old, current) {
|
|
30
|
+
switch (name) {
|
|
31
|
+
case "global":
|
|
32
|
+
if (old && !current) {
|
|
33
|
+
delete globalThis[old];
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
break;
|
|
37
|
+
}
|
|
38
|
+
this.reset();
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Class Cycler
|
|
2
|
+
|
|
3
|
+
Exposes a [localstorage-class-cycler](../localstorage-class-cycler/readme.md)
|
|
4
|
+
instance as a named global function, so it can be called from anywhere
|
|
5
|
+
(e.g. a plain `<button onclick="...">`). Container/global variant — see
|
|
6
|
+
[class-cycler.button.component](../class-cycler.button.component/readme.md)
|
|
7
|
+
for a self-contained `<button>` element that cycles its own classes.
|
|
8
|
+
|
|
9
|
+
## Attributes
|
|
10
|
+
|
|
11
|
+
| Attribute | Description |
|
|
12
|
+
|---|---|
|
|
13
|
+
| `global` | Name to assign the cycler function to on `globalThis` (required — removing this attribute, or disconnecting the element, unassigns it) |
|
|
14
|
+
| `selector` | Selector for the element whose classes get cycled. Defaults to `body` |
|
|
15
|
+
| `storage-key` | localStorage key the current class value persists under |
|
|
16
|
+
| `classes` | Comma-delimited list of classes to cycle through |
|
|
17
|
+
|
|
18
|
+
## Usage
|
|
19
|
+
|
|
20
|
+
```html
|
|
21
|
+
<script
|
|
22
|
+
type="module"
|
|
23
|
+
src="https://esm.sh/@johnhenry/domkit/class-cycler.component/global.mjs"
|
|
24
|
+
></script>
|
|
25
|
+
<class-cycler
|
|
26
|
+
global="cycleTheme"
|
|
27
|
+
selector="body"
|
|
28
|
+
storage-key="theme"
|
|
29
|
+
classes="light,dark,system"
|
|
30
|
+
></class-cycler>
|
|
31
|
+
<button onclick="cycleTheme()">Toggle theme</button>
|
|
32
|
+
```
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import localStorageCycler from "../localstorage-cycler/index.mjs";
|
|
2
|
+
export default (targets, key, ...classes) => {
|
|
3
|
+
const values = classes;
|
|
4
|
+
const emit = ({ value }) => {
|
|
5
|
+
const target_list = Array.isArray(targets) ? targets : [targets];
|
|
6
|
+
for (const target of target_list) {
|
|
7
|
+
target.classList.remove(...values.filter((s) => s));
|
|
8
|
+
if (value) {
|
|
9
|
+
target.classList.add(value);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
return localStorageCycler(key, emit, ...values);
|
|
14
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# LocalStorage Cycler
|
|
2
|
+
|
|
3
|
+
Cycle local storage values through a given list of strings.
|
|
4
|
+
Renders cycled value as class on given element.
|
|
5
|
+
|
|
6
|
+
## Usage
|
|
7
|
+
|
|
8
|
+
```javascript
|
|
9
|
+
import classStorageCycler from "..";
|
|
10
|
+
const updateBodyClass = classStorageCycler(
|
|
11
|
+
document.body,
|
|
12
|
+
"my-key",
|
|
13
|
+
"a",
|
|
14
|
+
"b",
|
|
15
|
+
"c"
|
|
16
|
+
);
|
|
17
|
+
updateBodyClass();
|
|
18
|
+
```
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import localStorageCycler from "./index.mjs";
|
|
2
|
+
export default (target, key, ...classes) => {
|
|
3
|
+
const values = classes;
|
|
4
|
+
const emit = ({ value }) => {
|
|
5
|
+
target.classList.remove(...values.filter((s) => s));
|
|
6
|
+
if (value) {
|
|
7
|
+
target.classList.add(value);
|
|
8
|
+
}
|
|
9
|
+
};
|
|
10
|
+
return localStorageCycler(key, emit, ...values);
|
|
11
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
const createNext =
|
|
2
|
+
(key, handler, ...values) =>
|
|
3
|
+
(...events) => {
|
|
4
|
+
const stored = globalThis.localStorage.getItem(key);
|
|
5
|
+
const index = values.indexOf(stored) + 1;
|
|
6
|
+
const value = values[index] ?? values[0];
|
|
7
|
+
globalThis.localStorage.setItem(key, value);
|
|
8
|
+
return {
|
|
9
|
+
value,
|
|
10
|
+
key,
|
|
11
|
+
index,
|
|
12
|
+
result: handler({ value, key, index, events }),
|
|
13
|
+
};
|
|
14
|
+
};
|
|
15
|
+
export default (key, ...values) => {
|
|
16
|
+
if (!key) {
|
|
17
|
+
throw new Error("key is required");
|
|
18
|
+
}
|
|
19
|
+
const handler = typeof values[0] === "function" ? values.shift() : () => {};
|
|
20
|
+
const stored = globalThis.localStorage.getItem(key) ?? values[0];
|
|
21
|
+
const index = values.indexOf(stored);
|
|
22
|
+
const value = values[index] ?? values[0];
|
|
23
|
+
globalThis.localStorage.setItem(key, value);
|
|
24
|
+
handler({
|
|
25
|
+
value,
|
|
26
|
+
key,
|
|
27
|
+
index,
|
|
28
|
+
events: [new CustomEvent("init", { detail: { value, key, index } })],
|
|
29
|
+
});
|
|
30
|
+
return createNext(key, handler, ...values);
|
|
31
|
+
};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# LocalStorage Cycler
|
|
2
|
+
|
|
3
|
+
Cycle local storage values through a given list of strings
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
```javascript
|
|
8
|
+
import localStorageCycler from "?";
|
|
9
|
+
const updateLocalStorage = localStorageCycler("my-key", "a", "b", "c");
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
The call to "localStorageCycler"
|
|
13
|
+
checks for the existence
|
|
14
|
+
of the key ("my-key") in localStorage.
|
|
15
|
+
and sets it to the first key ("a") if not already set.
|
|
16
|
+
|
|
17
|
+
When called, the "updateLocalStorage" function
|
|
18
|
+
cycles the value associated with the key
|
|
19
|
+
in localStorage through the given values ("a", "b", and "c").
|
|
20
|
+
|
|
21
|
+
The "updateLocalStorage" returns an object with the following keys:
|
|
22
|
+
|
|
23
|
+
- key - the associated local storage key
|
|
24
|
+
- value - the current value of the local storage item
|
|
25
|
+
- index - the current index of the local storage item
|
|
26
|
+
- result - the reuslt of an handler, if passed (see below)
|
|
27
|
+
|
|
28
|
+
## Change Handler
|
|
29
|
+
|
|
30
|
+
To react to the change,
|
|
31
|
+
pass a optional change handler
|
|
32
|
+
as the second parameter to "localStorageCycler".
|
|
33
|
+
|
|
34
|
+
```javascript
|
|
35
|
+
const onChange = ({ value, key, index, events }) =>
|
|
36
|
+
console.log({ value, key, index, events });
|
|
37
|
+
const updateLocalStorage = localStorageCycler(
|
|
38
|
+
"my-key",
|
|
39
|
+
onChange,
|
|
40
|
+
"a",
|
|
41
|
+
"b",
|
|
42
|
+
"c"
|
|
43
|
+
);
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
The handler takes four parametes:
|
|
47
|
+
|
|
48
|
+
- the same, "key", "value", and "index" parameters
|
|
49
|
+
returned from calling "updateLocalStorage"
|
|
50
|
+
|
|
51
|
+
- an "events" parameter -- an array of everything
|
|
52
|
+
passed into the "updateLocalStorage" function OR
|
|
53
|
+
an "init" CustomEvent if fired from the initial
|
|
54
|
+
call to localStorageCycler.
|