@c2n/spinner 0.0.6
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 +25 -0
- package/dist/spinner.js +84 -0
- package/package.json +61 -0
- package/types/src/spinner.d.ts +45 -0
- package/types/src/spinner.d.ts.map +1 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Nguyen Thai Vinh
|
|
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,25 @@
|
|
|
1
|
+
# @c2n/spinner
|
|
2
|
+
|
|
3
|
+
Spinner built with Lit: a circular progress ring, indeterminate by default or showing a value, with optional text.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npm install @c2n/spinner
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
```html
|
|
10
|
+
<script type="module">
|
|
11
|
+
import '@c2n/spinner'
|
|
12
|
+
</script>
|
|
13
|
+
|
|
14
|
+
<c2-spinner></c2-spinner>
|
|
15
|
+
<c2-spinner>Loading…</c2-spinner>
|
|
16
|
+
<c2-spinner value="65"></c2-spinner>
|
|
17
|
+
<c2-spinner value="3" max="4">3 of 4 steps</c2-spinner>
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
- **Indeterminate**: the arc grows, shrinks and spins; `--c2-spinner--animation-duration` sets one cycle.
|
|
21
|
+
- **Determinate**: `value` (out of `max`, default 100) fills the ring clockwise from the top and animates between values; clear it to spin again.
|
|
22
|
+
- **Text**: slotted text sits beside the ring (`--c2-spinner--flex-direction: column` for below) and is the accessible name; without text, `label` (default "Loading") names it. The element is `role="progressbar"` with `aria-valuenow` when determinate.
|
|
23
|
+
- **Reduced motion**: a slow steady rotation of a fixed arc replaces the pulsing.
|
|
24
|
+
|
|
25
|
+
Theme it with `--c2-spinner--size`, `--c2-spinner--stroke-width` (in the ring's 48-unit box, so it scales with the size), `--c2-spinner--stroke-linecap`, `--c2-spinner--color`, `--c2-spinner__track--color` (`transparent` for an arc alone) and the `--c2-spinner__label--*` text variables. The full list is in `custom-elements.json` and on the docs site.
|
package/dist/spinner.js
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { LitElement, html, nothing, unsafeCSS } from "lit";
|
|
2
|
+
import { customElement, property, state } from "lit/decorators.js";
|
|
3
|
+
import { classMap } from "lit/directives/class-map.js";
|
|
4
|
+
import { ifDefined } from "lit/directives/if-defined.js";
|
|
5
|
+
import { styleMap } from "lit/directives/style-map.js";
|
|
6
|
+
//#region src/spinner.scss?inline
|
|
7
|
+
var spinner_default = "/* ex : var((width: 24px), width, c2-checkbox) returns var(--c2-checkbox-width, 24px) */\n:host {\n display: inline-flex;\n vertical-align: middle;\n}\n\n:host([hidden]) {\n display: none;\n}\n\n.c2-spinner {\n display: inline-flex;\n align-items: center;\n flex-direction: var(--c2-spinner--flex-direction,row);\n gap: var(--c2-spinner--gap,8px);\n}\n\n.c2-spinner-ring {\n flex: none;\n width: var(--c2-spinner--size,24px);\n height: var(--c2-spinner--size,24px);\n transform: rotate(-90deg);\n animation: c2-spinner-rotate var(--c2-spinner--animation-duration,1.4s) linear infinite;\n}\n\n.c2-spinner-track,\n.c2-spinner-arc {\n fill: none;\n stroke-width: var(--c2-spinner--stroke-width,4px);\n}\n\n.c2-spinner-track {\n stroke: var(--c2-spinner__track--color,#e4e4e7);\n}\n\n.c2-spinner-arc {\n stroke: var(--c2-spinner--color,#0265dc);\n stroke-linecap: var(--c2-spinner--stroke-linecap,round);\n stroke-dasharray: 1 200;\n stroke-dashoffset: 0;\n animation: c2-spinner-dash calc(var(--c2-spinner--animation-duration,1.4s) * 1.1) ease-in-out infinite;\n}\n\n.is-determinate .c2-spinner-ring {\n animation: none;\n}\n\n.is-determinate .c2-spinner-arc {\n animation: none;\n transition: stroke-dashoffset calc(var(--c2-spinner--animation-duration,1.4s) / 2) cubic-bezier(0.2, 0, 0, 1);\n}\n\n.c2-spinner-label {\n color: var(--c2-spinner__label--color,#71717a);\n font-size: var(--c2-spinner__label--font-size,14px);\n font-weight: var(--c2-spinner__label--font-weight);\n line-height: 1.4;\n}\n.c2-spinner-label[hidden] {\n display: none;\n}\n\n@keyframes c2-spinner-rotate {\n from {\n transform: rotate(-90deg);\n }\n to {\n transform: rotate(270deg);\n }\n}\n@keyframes c2-spinner-dash {\n 0% {\n stroke-dasharray: 1 200;\n stroke-dashoffset: 0;\n }\n 50% {\n stroke-dasharray: 90 200;\n stroke-dashoffset: -30;\n }\n 100% {\n stroke-dasharray: 90 200;\n stroke-dashoffset: -124;\n }\n}\n@media (prefers-reduced-motion: reduce) {\n .c2-spinner-ring {\n animation-duration: 3s;\n }\n .c2-spinner-arc {\n animation: none;\n stroke-dasharray: 60 200;\n }\n .is-determinate .c2-spinner-arc {\n transition: none;\n }\n}";
|
|
8
|
+
//#endregion
|
|
9
|
+
//#region \0@oxc-project+runtime@0.148.0/helpers/esm/decorate.js
|
|
10
|
+
function __decorate(decorators, target, key, desc) {
|
|
11
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
12
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
13
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
14
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
15
|
+
}
|
|
16
|
+
//#endregion
|
|
17
|
+
//#region src/spinner.ts
|
|
18
|
+
/** Radius of the ring in the 48-unit view box; the circumference drives the dash lengths. */
|
|
19
|
+
var RADIUS = 20;
|
|
20
|
+
var CIRCUMFERENCE = 2 * Math.PI * RADIUS;
|
|
21
|
+
var Spinner = class Spinner extends LitElement {
|
|
22
|
+
constructor(..._args) {
|
|
23
|
+
super(..._args);
|
|
24
|
+
this.value = void 0;
|
|
25
|
+
this.max = 100;
|
|
26
|
+
this.label = "";
|
|
27
|
+
this.hasText = false;
|
|
28
|
+
}
|
|
29
|
+
static {
|
|
30
|
+
this.styles = unsafeCSS(spinner_default);
|
|
31
|
+
}
|
|
32
|
+
/** Completed fraction, `0` to `1`; `undefined` while indeterminate. */
|
|
33
|
+
get fraction() {
|
|
34
|
+
if (this.value === void 0 || Number.isNaN(this.value)) return void 0;
|
|
35
|
+
const max = this.max > 0 ? this.max : 100;
|
|
36
|
+
return Math.min(1, Math.max(0, this.value / max));
|
|
37
|
+
}
|
|
38
|
+
firstUpdated() {
|
|
39
|
+
const slot = this.renderRoot.querySelector("slot");
|
|
40
|
+
if (slot) this.updateText(slot);
|
|
41
|
+
}
|
|
42
|
+
handleSlotChange(event) {
|
|
43
|
+
this.updateText(event.target);
|
|
44
|
+
}
|
|
45
|
+
updateText(slot) {
|
|
46
|
+
this.hasText = slot.assignedNodes({ flatten: true }).some((node) => node.nodeType === Node.ELEMENT_NODE || (node.textContent ?? "").trim() !== "");
|
|
47
|
+
}
|
|
48
|
+
render() {
|
|
49
|
+
const fraction = this.fraction;
|
|
50
|
+
const determinate = fraction !== void 0;
|
|
51
|
+
const arcStyle = determinate ? styleMap({
|
|
52
|
+
strokeDasharray: `${CIRCUMFERENCE}`,
|
|
53
|
+
strokeDashoffset: `${CIRCUMFERENCE * (1 - fraction)}`
|
|
54
|
+
}) : nothing;
|
|
55
|
+
return html`
|
|
56
|
+
<div
|
|
57
|
+
class=${classMap({
|
|
58
|
+
"c2-spinner": true,
|
|
59
|
+
"is-determinate": determinate,
|
|
60
|
+
"has-text": this.hasText
|
|
61
|
+
})}
|
|
62
|
+
role="progressbar"
|
|
63
|
+
aria-label=${this.hasText ? nothing : ifDefined(this.label || "Loading")}
|
|
64
|
+
aria-labelledby=${this.hasText ? "label" : nothing}
|
|
65
|
+
aria-valuemin=${determinate ? "0" : nothing}
|
|
66
|
+
aria-valuemax=${determinate ? String(this.max) : nothing}
|
|
67
|
+
aria-valuenow=${determinate ? String(this.value) : nothing}
|
|
68
|
+
>
|
|
69
|
+
<svg class="c2-spinner-ring" part="ring" viewBox="0 0 48 48" aria-hidden="true" focusable="false">
|
|
70
|
+
<circle class="c2-spinner-track" part="track" cx="24" cy="24" r=${RADIUS}></circle>
|
|
71
|
+
<circle class="c2-spinner-arc" part="arc" cx="24" cy="24" r=${RADIUS} style=${arcStyle}></circle>
|
|
72
|
+
</svg>
|
|
73
|
+
<span id="label" class="c2-spinner-label" part="label" ?hidden=${!this.hasText}><slot @slotchange=${this.handleSlotChange}></slot></span>
|
|
74
|
+
</div>
|
|
75
|
+
`;
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
__decorate([property({ type: Number })], Spinner.prototype, "value", void 0);
|
|
79
|
+
__decorate([property({ type: Number })], Spinner.prototype, "max", void 0);
|
|
80
|
+
__decorate([property()], Spinner.prototype, "label", void 0);
|
|
81
|
+
__decorate([state()], Spinner.prototype, "hasText", void 0);
|
|
82
|
+
Spinner = __decorate([customElement("c2-spinner")], Spinner);
|
|
83
|
+
//#endregion
|
|
84
|
+
export { Spinner };
|
package/package.json
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@c2n/spinner",
|
|
3
|
+
"version": "0.0.6",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "dist/spinner.js",
|
|
6
|
+
"exports": {
|
|
7
|
+
".": {
|
|
8
|
+
"default": "./dist/spinner.js",
|
|
9
|
+
"types": "./types/src/spinner.d.ts"
|
|
10
|
+
},
|
|
11
|
+
"./custom-elements.json": "./custom-elements.json"
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"dist",
|
|
15
|
+
"types"
|
|
16
|
+
],
|
|
17
|
+
"keywords": [
|
|
18
|
+
"spinner",
|
|
19
|
+
"web component",
|
|
20
|
+
"lit"
|
|
21
|
+
],
|
|
22
|
+
"license": "MIT",
|
|
23
|
+
"author": "code2nguyen@gmail.com",
|
|
24
|
+
"publishConfig": {
|
|
25
|
+
"registry": "https://registry.npmjs.org",
|
|
26
|
+
"access": "public"
|
|
27
|
+
},
|
|
28
|
+
"repository": {
|
|
29
|
+
"type": "git",
|
|
30
|
+
"url": "https://github.com/code2nguyen/web-components.git"
|
|
31
|
+
},
|
|
32
|
+
"scripts": {
|
|
33
|
+
"dev": "vite",
|
|
34
|
+
"build": "wireit",
|
|
35
|
+
"build:only": "vite build",
|
|
36
|
+
"type-check": "wireit"
|
|
37
|
+
},
|
|
38
|
+
"wireit": {
|
|
39
|
+
"type-check": {
|
|
40
|
+
"dependencies": [
|
|
41
|
+
"../../core:build"
|
|
42
|
+
],
|
|
43
|
+
"command": "tsc -p tsconfig.lib.json --composite false"
|
|
44
|
+
},
|
|
45
|
+
"build": {
|
|
46
|
+
"dependencies": [
|
|
47
|
+
"type-check"
|
|
48
|
+
],
|
|
49
|
+
"command": "vite build"
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
"dependencies": {
|
|
53
|
+
"@c2n/core": "0.0.6",
|
|
54
|
+
"lit": "3.3.3"
|
|
55
|
+
},
|
|
56
|
+
"devDependencies": {
|
|
57
|
+
"@c2n/config": "*"
|
|
58
|
+
},
|
|
59
|
+
"customElements": "custom-elements.json",
|
|
60
|
+
"gitHead": "2921054bc75299da66dad11c1e374246ff88a51e"
|
|
61
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { LitElement } from 'lit';
|
|
2
|
+
/**
|
|
3
|
+
* Circular progress indicator. Without a `value` it spins indeterminately; with one it draws the completed arc of a
|
|
4
|
+
* ring and animates between values. Text in the default slot sits beside it (or below, via a variable) and names the
|
|
5
|
+
* indicator for assistive technology, as does the `label` attribute. Everything is one SVG ring, so size, thickness,
|
|
6
|
+
* colours and speed are variables.
|
|
7
|
+
*
|
|
8
|
+
* @tag c2-spinner
|
|
9
|
+
*
|
|
10
|
+
* @slot - Optional text shown next to the ring (e.g. "Loading…"). Also used as the accessible name.
|
|
11
|
+
*
|
|
12
|
+
* @cssproperty {pixel} [--c2-spinner--size=24px] - Diameter of the ring.
|
|
13
|
+
* @cssproperty {pixel} [--c2-spinner--stroke-width=4px] - Thickness of the ring, in the ring's 48-unit box; it scales with the size.
|
|
14
|
+
* @cssproperty {stroke-linecap} [--c2-spinner--stroke-linecap=round] - `butt` for square arc ends.
|
|
15
|
+
* @cssproperty {color} [--c2-spinner--color=#0265dc] - Colour of the moving arc.
|
|
16
|
+
* @cssproperty {color} [--c2-spinner__track--color=#e4e4e7] - Colour of the full ring behind the arc; `transparent` hides it.
|
|
17
|
+
* @cssproperty {time} [--c2-spinner--animation-duration=1.4s] - One indeterminate cycle; also the determinate transition length divided by two.
|
|
18
|
+
* @cssproperty {pixel} [--c2-spinner--gap=8px] - Space between the ring and the text.
|
|
19
|
+
* @cssproperty {flex-direction} [--c2-spinner--flex-direction=row] - `column` puts the text under the ring.
|
|
20
|
+
* @cssproperty {color} [--c2-spinner__label--color=#71717a]
|
|
21
|
+
* @cssproperty {font-size} [--c2-spinner__label--font-size=14px]
|
|
22
|
+
* @cssproperty {font-weight} --c2-spinner__label--font-weight
|
|
23
|
+
*/
|
|
24
|
+
export declare class Spinner extends LitElement {
|
|
25
|
+
static styles: import("lit").CSSResult;
|
|
26
|
+
/** Progress between `0` and `max`. Leave unset for an indeterminate spinner. */
|
|
27
|
+
value: number | undefined;
|
|
28
|
+
/** Value that fills the whole ring. */
|
|
29
|
+
max: number;
|
|
30
|
+
/** Accessible name when nothing is slotted. Defaults to "Loading". */
|
|
31
|
+
label: string;
|
|
32
|
+
private hasText;
|
|
33
|
+
/** Completed fraction, `0` to `1`; `undefined` while indeterminate. */
|
|
34
|
+
get fraction(): number | undefined;
|
|
35
|
+
firstUpdated(): void;
|
|
36
|
+
private handleSlotChange;
|
|
37
|
+
private updateText;
|
|
38
|
+
render(): import("lit-html").TemplateResult<1>;
|
|
39
|
+
}
|
|
40
|
+
declare global {
|
|
41
|
+
interface HTMLElementTagNameMap {
|
|
42
|
+
'c2-spinner': Spinner;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=spinner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spinner.d.ts","sourceRoot":"","sources":["../../src/spinner.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAA4B,MAAM,KAAK,CAAA;AAW1D;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,qBACa,OAAQ,SAAQ,UAAU;IACrC,OAAgB,MAAM,0BAAoB;IAE1C,gFAAgF;IACpD,KAAK,EAAE,MAAM,GAAG,SAAS,CAAY;IAEjE,uCAAuC;IACX,GAAG,SAAM;IAErC,sEAAsE;IAC1D,KAAK,SAAK;IAEb,OAAO,CAAC,OAAO,CAAQ;IAEhC,uEAAuE;IACvE,IAAI,QAAQ,IAAI,MAAM,GAAG,SAAS,CAIjC;IAEQ,YAAY;IAKrB,OAAO,CAAC,gBAAgB;IAIxB,OAAO,CAAC,UAAU;IAIT,MAAM;CAsBhB;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,qBAAqB;QAC7B,YAAY,EAAE,OAAO,CAAA;KACtB;CACF"}
|