tlp-icons 0.1.1 → 0.3.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/README.md +244 -35
- package/dist/core.js +3 -185
- package/dist/core.js.map +7 -0
- package/dist/element.js +3 -75
- package/dist/element.js.map +7 -0
- package/dist/icons/arrows.js +2 -369
- package/dist/icons/brands.js +2 -379
- package/dist/icons/buildings.js +2 -113
- package/dist/icons/charts.js +2 -77
- package/dist/icons/commerce.js +2 -129
- package/dist/icons/communication.js +2 -199
- package/dist/icons/connectivity.js +2 -105
- package/dist/icons/design.js +2 -284
- package/dist/icons/development.js +2 -163
- package/dist/icons/devices.js +2 -425
- package/dist/icons/emoji.js +2 -167
- package/dist/icons/files.js +2 -315
- package/dist/icons/finance.js +2 -223
- package/dist/icons/food.js +2 -119
- package/dist/icons/health.js +2 -145
- package/dist/icons/home.js +2 -127
- package/dist/icons/interface.js +2 -504
- package/dist/icons/layout.js +2 -208
- package/dist/icons/legal.js +2 -123
- package/dist/icons/maps.js +2 -256
- package/dist/icons/math.js +2 -329
- package/dist/icons/media.js +2 -179
- package/dist/icons/nature.js +2 -113
- package/dist/icons/notifications.js +2 -118
- package/dist/icons/people.js +2 -82
- package/dist/icons/photography.js +2 -175
- package/dist/icons/science.js +2 -38
- package/dist/icons/security.js +2 -119
- package/dist/icons/shapes.js +2 -110
- package/dist/icons/sport.js +2 -148
- package/dist/icons/text.js +2 -459
- package/dist/icons/time.js +2 -134
- package/dist/icons/tools.js +2 -52
- package/dist/icons/vehicles.js +2 -134
- package/dist/icons/weather.js +2 -105
- package/dist/index.d.ts +39 -5
- package/dist/index.js +3 -45
- package/dist/index.js.map +7 -0
- package/dist/jsx.d.ts +62 -0
- package/dist/jsx.js +1 -0
- package/dist/manifest.js +3 -6
- package/dist/manifest.js.map +7 -0
- package/dist/react.d.ts +19 -0
- package/dist/react.js +3 -0
- package/dist/react.js.map +7 -0
- package/dist/stats.json +47 -0
- package/dist/tlp-icon.d.ts +75 -0
- package/dist/tlp-icon.js +3 -12
- package/dist/tlp-icon.js.map +7 -0
- package/dist/tlp-icons.css +2 -52
- package/dist/tlp-icons.runtime.js +3 -265
- package/dist/tlp-icons.runtime.js.map +7 -0
- package/package.json +74 -7
- package/dist/tlp-icons.all.js +0 -6888
- package/dist/tlp-icons.json +0 -1
package/README.md
CHANGED
|
@@ -13,25 +13,40 @@
|
|
|
13
13
|
<a href="https://github.com/Tamer-Mansour/tlp-icons">GitHub</a>
|
|
14
14
|
</p>
|
|
15
15
|
|
|
16
|
+
<!-- badges:start -->
|
|
16
17
|
<p align="center">
|
|
17
|
-
<img alt="
|
|
18
|
-
<img alt="
|
|
19
|
-
<img alt="
|
|
20
|
-
<img alt="
|
|
18
|
+
<a href="https://www.npmjs.com/package/tlp-icons"><img alt="npm version" src="https://img.shields.io/npm/v/tlp-icons?color=4f46e5"></a>
|
|
19
|
+
<a href="https://www.npmjs.com/package/tlp-icons"><img alt="npm downloads" src="https://img.shields.io/npm/dm/tlp-icons?color=4f46e5"></a>
|
|
20
|
+
<a href="#bundle-size"><img alt="element size" src="https://img.shields.io/badge/element-6.6%20KB%20gzip-16a34a"></a>
|
|
21
|
+
<a href="https://github.com/Tamer-Mansour/tlp-icons/actions/workflows/ci.yml"><img alt="CI" src="https://github.com/Tamer-Mansour/tlp-icons/actions/workflows/ci.yml/badge.svg"></a>
|
|
22
|
+
<a href="https://github.com/Tamer-Mansour/tlp-icons/blob/main/LICENSE"><img alt="license" src="https://img.shields.io/npm/l/tlp-icons?color=16a34a"></a>
|
|
23
|
+
<img alt="icons" src="https://img.shields.io/badge/icons-7%2C677-4f46e5">
|
|
21
24
|
</p>
|
|
25
|
+
<!-- badges:end -->
|
|
22
26
|
|
|
23
|
-
A free, open-source (MIT) library of
|
|
24
|
-
our own **tlp originals**, sorted into 35 hand-curated categories, including
|
|
27
|
+
A free, open-source (MIT) library of <!--s:icons-->7,677<!--/s--> stroke icons, built from the best free sets (Lucide and Tabler) plus
|
|
28
|
+
our own **tlp originals**, sorted into <!--s:categories-->35<!--/s--> hand-curated categories, including **<!--s:brands-->376<!--/s--> brand logos** (Facebook,
|
|
25
29
|
Instagram, WhatsApp, X, TikTok, YouTube...). Every single one moves on hover: clock hands sweep around the face,
|
|
26
30
|
gears spin on their axle, bells swing from the top, trash lids lift, arrows move the way they point.
|
|
27
31
|
|
|
32
|
+
<table>
|
|
33
|
+
<tr>
|
|
34
|
+
<td align="center"><img src="https://raw.githubusercontent.com/Tamer-Mansour/tlp-icons/main/docs/media/hover.gif" width="420" alt="Hovering a button plays its icon"><br><sub>Hover a button, its icon plays</sub></td>
|
|
35
|
+
<td align="center"><img src="https://raw.githubusercontent.com/Tamer-Mansour/tlp-icons/main/docs/media/loop.gif" width="420" alt="Loaders with trigger=loop"><br><sub><code>trigger="loop"</code></sub></td>
|
|
36
|
+
</tr>
|
|
37
|
+
<tr>
|
|
38
|
+
<td align="center"><img src="https://raw.githubusercontent.com/Tamer-Mansour/tlp-icons/main/docs/media/rtl.gif" width="420" alt="The same arrow in LTR and RTL with flip=rtl"><br><sub><code>flip="rtl"</code> mirrors the icon and its motion</sub></td>
|
|
39
|
+
<td align="center"><img src="https://raw.githubusercontent.com/Tamer-Mansour/tlp-icons/main/docs/media/duotone.gif" width="420" alt="Icons without and with duotone"><br><sub><code>duotone</code> adds a soft tinted body</sub></td>
|
|
40
|
+
</tr>
|
|
41
|
+
</table>
|
|
42
|
+
|
|
28
43
|
[](https://tlp-icons.netlify.app)
|
|
29
44
|
|
|
30
45
|
<table><tr>
|
|
31
46
|
<td><a href="https://tlp-icons.netlify.app/categories/legal/"><img src="https://raw.githubusercontent.com/Tamer-Mansour/tlp-icons/main/docs/category.png" alt="Legal category page"></a></td>
|
|
32
47
|
<td><a href="https://tlp-icons.netlify.app/icons/courthouse/"><img src="https://raw.githubusercontent.com/Tamer-Mansour/tlp-icons/main/docs/icon-page.png" alt="Icon page with live playground and code"></a></td>
|
|
33
48
|
</tr><tr>
|
|
34
|
-
<td align="center">Category pages (legal:
|
|
49
|
+
<td align="center">Category pages (legal: <!--s:cat.legal-->1,177<!--/s--> icons)</td>
|
|
35
50
|
<td align="center">One page per icon: playground + code</td>
|
|
36
51
|
</tr></table>
|
|
37
52
|
|
|
@@ -41,15 +56,15 @@ gears spin on their axle, bells swing from the top, trash lids lift, arrows move
|
|
|
41
56
|

|
|
42
57
|
</details>
|
|
43
58
|
|
|
44
|
-
-
|
|
45
|
-
- **100% animated**:
|
|
46
|
-
- **Deep fields**: we draw our own icons where the free sets stop. Legal went from 18 to
|
|
59
|
+
- **<!--s:icons-->7,677<!--/s--> icons**: 24×24 grid, 2px stroke, round caps, `currentColor`. One consistent style.
|
|
60
|
+
- **100% animated**: <!--s:handmade-->1,496<!--/s--> hand-made choreographies, plus a motion engine that reads each icon's geometry.
|
|
61
|
+
- **Deep fields**: we draw our own icons where the free sets stop. Legal went from 18 to <!--s:cat.legal-->1,177<!--/s--> first; the
|
|
47
62
|
[roadmap](https://github.com/Tamer-Mansour/tlp-icons/blob/main/ROADMAP.md) takes it to 1,180 and the whole library to 26,000.
|
|
48
|
-
- **Brand logos**: 376 brands in the same outline style, animated (trademarks of their owners, see `NOTICE.md`).
|
|
63
|
+
- **Brand logos**: <!--s:brands-->376<!--/s--> brands in the same outline style, animated (trademarks of their owners, see `NOTICE.md`).
|
|
49
64
|
- **Tiny runtime**: one custom element, `<tlp-icon>`. Works in plain HTML, React, Angular, Vue, Svelte...
|
|
50
|
-
- **Lazy**: icons load per category, so a page that uses 10 icons doesn't download
|
|
65
|
+
- **Lazy**: icons load per category, so a page that uses 10 icons doesn't download all <!--s:icons-->7,677<!--/s-->.
|
|
51
66
|
- **Polite motion**: plays once to the end, never animates a whole card at once, respects `prefers-reduced-motion`.
|
|
52
|
-
- **Works offline**:
|
|
67
|
+
- **Works offline**: one classic script with every icon (in `tlp-icons-data`) runs from `file://`, and the build never needs the network.
|
|
53
68
|
|
|
54
69
|
## Install
|
|
55
70
|
|
|
@@ -64,10 +79,12 @@ Or with no build step, from a CDN:
|
|
|
64
79
|
<tlp-icon name="bell"></tlp-icon>
|
|
65
80
|
```
|
|
66
81
|
|
|
67
|
-
Offline / no modules (for example opened from `file://`): one classic script with every icon inside (about 2.
|
|
82
|
+
Offline / no modules (for example opened from `file://`): one classic script with every icon inside (about 2.3 MB,
|
|
83
|
+
390 KB gzip). Since 0.2.0 it ships in the [`tlp-icons-data`](packages/tlp-icons-data) package:
|
|
68
84
|
|
|
69
85
|
```html
|
|
70
|
-
<script src="node_modules/tlp-icons/
|
|
86
|
+
<script src="node_modules/tlp-icons-data/tlp-icons.all.js"></script>
|
|
87
|
+
<!-- or https://cdn.jsdelivr.net/npm/tlp-icons-data/tlp-icons.all.js -->
|
|
71
88
|
<tlp-icon name="bell"></tlp-icon>
|
|
72
89
|
```
|
|
73
90
|
|
|
@@ -104,39 +121,179 @@ exactly one icon (a button, a menu item) and animates that icon. If it reaches a
|
|
|
104
121
|
(a card, a menu, a table row), nothing animates. So hovering a button animates its icon, while a big card never
|
|
105
122
|
lights up all its icons at once.
|
|
106
123
|
|
|
107
|
-
|
|
124
|
+
## Framework recipes
|
|
108
125
|
|
|
109
|
-
|
|
110
|
-
|
|
126
|
+
Copy-paste setups. Every snippet below is a real file in [`test/recipes/`](test/recipes) that the test suite
|
|
127
|
+
compiles with that framework's own compiler (TypeScript, `vue/compiler-sfc`, `svelte/compiler`,
|
|
128
|
+
`@astrojs/compiler`), so they stay correct.
|
|
129
|
+
|
|
130
|
+
`import 'tlp-icons/element'` is safe during server rendering: on the server it registers nothing and never touches
|
|
131
|
+
the DOM.
|
|
132
|
+
|
|
133
|
+
### React 18: the wrapper
|
|
111
134
|
|
|
112
|
-
|
|
113
|
-
|
|
135
|
+
React 18 turns boolean props into strings and can't set custom element properties, so use the tiny optional
|
|
136
|
+
wrapper (`react` is an optional peer dependency and is never bundled):
|
|
137
|
+
|
|
138
|
+
<!-- recipe:react18/Toolbar.tsx -->
|
|
139
|
+
```tsx
|
|
140
|
+
import { TlpIcon } from 'tlp-icons/react';
|
|
141
|
+
|
|
142
|
+
export function Toolbar() {
|
|
143
|
+
return (
|
|
144
|
+
<nav>
|
|
145
|
+
<button type="button">
|
|
146
|
+
<TlpIcon name="bell" /> Alerts
|
|
147
|
+
</button>
|
|
148
|
+
<TlpIcon name="loader-circle" trigger="loop" aria-label="Loading" />
|
|
149
|
+
<TlpIcon name="heart" size={32} duotone className="like" />
|
|
150
|
+
</nav>
|
|
151
|
+
);
|
|
114
152
|
}
|
|
115
153
|
```
|
|
154
|
+
<!-- /recipe -->
|
|
155
|
+
|
|
156
|
+
### React 19: the tag directly
|
|
157
|
+
|
|
158
|
+
React 19 supports custom elements natively. `tlp-icons/jsx` types the tag (`name` is required, `trigger` and
|
|
159
|
+
`flip` are checked):
|
|
160
|
+
|
|
161
|
+
<!-- recipe:react19/Toolbar.tsx -->
|
|
162
|
+
```tsx
|
|
163
|
+
import 'tlp-icons/element';
|
|
164
|
+
import 'tlp-icons/jsx'; // types <tlp-icon> in JSX
|
|
165
|
+
|
|
166
|
+
export function Toolbar() {
|
|
167
|
+
return (
|
|
168
|
+
<nav>
|
|
169
|
+
<button type="button">
|
|
170
|
+
<tlp-icon name="bell" /> Alerts
|
|
171
|
+
</button>
|
|
172
|
+
<tlp-icon name="loader-circle" trigger="loop" aria-label="Loading" />
|
|
173
|
+
<tlp-icon name="heart" size={32} duotone className="like" />
|
|
174
|
+
</nav>
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
```
|
|
178
|
+
<!-- /recipe -->
|
|
179
|
+
|
|
180
|
+
### Next.js (App Router)
|
|
116
181
|
|
|
117
|
-
|
|
182
|
+
Put the element behind one small client component and use it from server components. Server rendering outputs the
|
|
183
|
+
tag; the browser upgrades it.
|
|
118
184
|
|
|
119
|
-
|
|
185
|
+
<!-- recipe:nextjs/app/components/Icon.tsx -->
|
|
186
|
+
```tsx
|
|
187
|
+
'use client';
|
|
188
|
+
// app/components/Icon.tsx - use <Icon name="bell" /> from any server or client component.
|
|
189
|
+
// The import is safe during server rendering (it registers the element only in the browser).
|
|
190
|
+
import 'tlp-icons/element';
|
|
191
|
+
import 'tlp-icons/jsx';
|
|
192
|
+
|
|
193
|
+
export function Icon(props: { name: string; size?: number; trigger?: 'hover' | 'loop' | 'click' | 'none'; duotone?: boolean }) {
|
|
194
|
+
return <tlp-icon {...props} />;
|
|
195
|
+
}
|
|
196
|
+
```
|
|
197
|
+
<!-- /recipe -->
|
|
120
198
|
|
|
199
|
+
### Angular 17+ (standalone)
|
|
200
|
+
|
|
201
|
+
<!-- recipe:angular/toolbar.component.ts -->
|
|
121
202
|
```ts
|
|
122
|
-
//
|
|
203
|
+
// toolbar.component.ts (Angular 17+, standalone)
|
|
204
|
+
import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
|
123
205
|
import 'tlp-icons/element';
|
|
124
206
|
|
|
125
|
-
|
|
126
|
-
|
|
207
|
+
@Component({
|
|
208
|
+
selector: 'app-toolbar',
|
|
209
|
+
standalone: true,
|
|
210
|
+
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
|
211
|
+
template: `
|
|
212
|
+
<button type="button"><tlp-icon name="bell"></tlp-icon> Alerts</button>
|
|
213
|
+
<tlp-icon [attr.name]="icon" trigger="loop"></tlp-icon>
|
|
214
|
+
`,
|
|
215
|
+
})
|
|
216
|
+
export class ToolbarComponent {
|
|
217
|
+
icon = 'loader-circle';
|
|
218
|
+
}
|
|
127
219
|
```
|
|
220
|
+
<!-- /recipe -->
|
|
221
|
+
|
|
222
|
+
### Vue 3 + Vite
|
|
128
223
|
|
|
129
|
-
|
|
224
|
+
Tell Vue that `tlp-icon` is a custom element, then use it in any template:
|
|
225
|
+
|
|
226
|
+
<!-- recipe:vue/vite.config.ts,vue/App.vue -->
|
|
227
|
+
`vite.config.ts`
|
|
130
228
|
|
|
131
229
|
```ts
|
|
132
|
-
//
|
|
230
|
+
// vite.config.ts
|
|
231
|
+
import { defineConfig } from 'vite';
|
|
232
|
+
import vue from '@vitejs/plugin-vue';
|
|
233
|
+
|
|
234
|
+
export default defineConfig({
|
|
235
|
+
plugins: [
|
|
236
|
+
vue({ template: { compilerOptions: { isCustomElement: tag => tag === 'tlp-icon' } } }),
|
|
237
|
+
],
|
|
238
|
+
});
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
`App.vue`
|
|
242
|
+
|
|
243
|
+
```vue
|
|
244
|
+
<script setup>
|
|
245
|
+
import { ref } from 'vue';
|
|
133
246
|
import 'tlp-icons/element';
|
|
134
|
-
|
|
247
|
+
|
|
248
|
+
const done = ref(false);
|
|
249
|
+
</script>
|
|
250
|
+
|
|
251
|
+
<template>
|
|
252
|
+
<button type="button" @click="done = !done">
|
|
253
|
+
<tlp-icon :name="done ? 'circle-check' : 'circle'" duotone></tlp-icon>
|
|
254
|
+
{{ done ? 'Done' : 'Mark done' }}
|
|
255
|
+
</button>
|
|
256
|
+
</template>
|
|
135
257
|
```
|
|
258
|
+
<!-- /recipe -->
|
|
136
259
|
|
|
137
|
-
|
|
138
|
-
|
|
260
|
+
### SvelteKit
|
|
261
|
+
|
|
262
|
+
<!-- recipe:sveltekit/+page.svelte -->
|
|
263
|
+
```svelte
|
|
264
|
+
<script>
|
|
265
|
+
import { onMount } from 'svelte';
|
|
266
|
+
|
|
267
|
+
// load the element in the browser only (SvelteKit renders on the server first)
|
|
268
|
+
onMount(() => import('tlp-icons/element'));
|
|
269
|
+
</script>
|
|
270
|
+
|
|
271
|
+
<button type="button">
|
|
272
|
+
<tlp-icon name="bell"></tlp-icon> Alerts
|
|
273
|
+
</button>
|
|
274
|
+
<tlp-icon name="loader-circle" trigger="loop"></tlp-icon>
|
|
139
275
|
```
|
|
276
|
+
<!-- /recipe -->
|
|
277
|
+
|
|
278
|
+
### Astro
|
|
279
|
+
|
|
280
|
+
<!-- recipe:astro/index.astro -->
|
|
281
|
+
```astro
|
|
282
|
+
---
|
|
283
|
+
// src/pages/index.astro
|
|
284
|
+
const icon = 'bell';
|
|
285
|
+
---
|
|
286
|
+
<button type="button">
|
|
287
|
+
<tlp-icon name={icon}></tlp-icon> Alerts
|
|
288
|
+
</button>
|
|
289
|
+
<tlp-icon name="loader-circle" trigger="loop"></tlp-icon>
|
|
290
|
+
|
|
291
|
+
<script>
|
|
292
|
+
// Astro bundles and runs this in the browser
|
|
293
|
+
import 'tlp-icons/element';
|
|
294
|
+
</script>
|
|
295
|
+
```
|
|
296
|
+
<!-- /recipe -->
|
|
140
297
|
|
|
141
298
|
### Raw data and SVG strings (Node / SSR)
|
|
142
299
|
|
|
@@ -151,6 +308,51 @@ icons.bell; // { svg, m: [[part, anim, dur, d
|
|
|
151
308
|
|
|
152
309
|
Importing `icons` pulls in the whole set. For pages, use `<tlp-icon>`, which loads one category chunk at a time.
|
|
153
310
|
|
|
311
|
+
**One category only:** import a category chunk directly and skip the manifest entirely:
|
|
312
|
+
|
|
313
|
+
```js
|
|
314
|
+
import legal from 'tlp-icons/icons/legal.js'; // { 'courthouse': { svg, m }, ... }
|
|
315
|
+
import { register } from 'tlp-icons/element';
|
|
316
|
+
register(legal); // <tlp-icon name="courthouse"> now renders with no lookup
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
### Full dataset: `tlp-icons-data`
|
|
320
|
+
|
|
321
|
+
Every icon's SVG, motion, category, tags and source in one JSON file (3.4 MB) ships as a separate package, so
|
|
322
|
+
`npm install tlp-icons` stays small:
|
|
323
|
+
|
|
324
|
+
```bash
|
|
325
|
+
npm install tlp-icons-data
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
```js
|
|
329
|
+
import data from 'tlp-icons-data' with { type: 'json' }; // bundlers: import data from 'tlp-icons-data'
|
|
330
|
+
data.icons.bell; // { category, source, tags, svg, m }
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
It replaces the old `tlp-icons/data` export (removed in 0.2.0). Typed (`TlpIconsData`).
|
|
334
|
+
|
|
335
|
+
## Bundle size
|
|
336
|
+
|
|
337
|
+
Measured on every build (minified, gzip level 9) and written to `dist/size-report.json`. The build fails if the
|
|
338
|
+
element core goes over 8 KB gzip or the manifest over 15 KB gzip.
|
|
339
|
+
|
|
340
|
+
<!-- sizes:start -->
|
|
341
|
+
| What | Minified | Gzip |
|
|
342
|
+
| --- | ---: | ---: |
|
|
343
|
+
| element core (tlp-icon + core + element) | 22.5 KB | **6.6 KB** |
|
|
344
|
+
| manifest | 23.3 KB | **12.0 KB** |
|
|
345
|
+
| smallest category chunk (science.js) | 11.3 KB | **3.4 KB** |
|
|
346
|
+
| largest category chunk (legal.js) | 388.0 KB | **78.2 KB** |
|
|
347
|
+
| all icons, offline bundle (tlp-icons.all.js) | 2608.1 KB | **457.1 KB** |
|
|
348
|
+
| stylesheet (tlp-icons.css) | 11.6 KB | **2.4 KB** |
|
|
349
|
+
<!-- sizes:end -->
|
|
350
|
+
|
|
351
|
+
What a page actually downloads: the element core, the manifest, then **one chunk per category it uses**. A page
|
|
352
|
+
with a few interface and arrow icons loads about <!--s:size.0.gzip-->6.6 KB<!--/s--> + <!--s:size.1.gzip-->12.0 KB<!--/s-->
|
|
353
|
+
plus those two chunks. Import a chunk directly (`tlp-icons/icons/<category>.js`) to skip the manifest. The
|
|
354
|
+
stylesheet is injected by the element; `tlp-icons/css` is there if you prefer a `<link>`.
|
|
355
|
+
|
|
154
356
|
## Website
|
|
155
357
|
|
|
156
358
|
**https://tlp-icons.netlify.app** is built with **Next.js (React)** in `web/` and runs on the **published npm
|
|
@@ -161,7 +363,7 @@ pre-rendered static HTML for search engines, and the whole site is about 45 MB:
|
|
|
161
363
|
| --- | --- |
|
|
162
364
|
| Home | `/` |
|
|
163
365
|
| Icon gallery (search, filters, size / stroke / color / duotone) | `/icons/` |
|
|
164
|
-
| One page per icon: preview, sizes, buttons, code for React / HTML / Angular / Vue / SVG, related icons | `/icons/<name>/` (
|
|
366
|
+
| One page per icon: preview, sizes, buttons, code for React / HTML / Angular / Vue / SVG, related icons | `/icons/<name>/` (<!--s:icons-->7,677<!--/s--> pages) |
|
|
165
367
|
| Categories, one page each, including Brands | `/categories/`, `/categories/<id>/` |
|
|
166
368
|
| Documentation with live examples and the animation library | `/docs/` |
|
|
167
369
|
| Roadmap with live progress | `/roadmap/` |
|
|
@@ -178,7 +380,7 @@ Netlify runs the same steps (see `netlify.toml`) and publishes `web/out` on ever
|
|
|
178
380
|
After publishing a new tlp-icons version to npm, run `npm install tlp-icons@latest` in `web/` and redeploy.
|
|
179
381
|
For a manual deploy, drag the `web/out` folder onto the site's **Deploys** tab in Netlify.
|
|
180
382
|
|
|
181
|
-
**Offline use:** `
|
|
383
|
+
**Offline use:** `tlp-icons-data/tlp-icons.all.js` is a single classic script with every icon; it works from `file://`
|
|
182
384
|
with no server and no internet.
|
|
183
385
|
|
|
184
386
|
## How it works
|
|
@@ -201,7 +403,7 @@ npm run test:browser optional Playwright tests + frame strips (npm run setup:b
|
|
|
201
403
|
- `scripts/adapters/`: one adapter per icon source (`latest`, `fetch`, `load`). Add a set by adding an adapter.
|
|
202
404
|
- `scripts/engine/`: SVG parsing, geometry, the motion engine and serialisation.
|
|
203
405
|
- `src/runtime/`: `core.js` (registry, baking, run engine, hover rule), `element.js` (`<tlp-icon>`) and
|
|
204
|
-
`tlp-icons.css` (the
|
|
406
|
+
`tlp-icons.css` (the <!--s:animations-->66<!--/s--> `tlp-` keyframes).
|
|
205
407
|
|
|
206
408
|
**Motion engine.** Every icon is split into atoms (one per element or sub-path) and measured. Then:
|
|
207
409
|
|
|
@@ -230,11 +432,18 @@ Every motion is small and springy and ends exactly at rest. The default `speed`
|
|
|
230
432
|
- Wrong category: add a name pattern or an explicit entry in `config/categories.json`.
|
|
231
433
|
- Run `npm run build && npm test` before opening a PR.
|
|
232
434
|
|
|
233
|
-
|
|
435
|
+
See [CONTRIBUTING.md](https://github.com/Tamer-Mansour/tlp-icons/blob/main/CONTRIBUTING.md) for the full workflow
|
|
436
|
+
and the release process, and [CHANGELOG.md](https://github.com/Tamer-Mansour/tlp-icons/blob/main/CHANGELOG.md) for
|
|
437
|
+
what changed in each version.
|
|
234
438
|
|
|
235
439
|
## License
|
|
236
440
|
|
|
237
441
|
MIT © tlp-icons contributors. Icon geometry from [Lucide](https://lucide.dev) (ISC) and
|
|
238
442
|
[Tabler Icons](https://tabler.io/icons) (MIT), plus tlp originals (MIT). See [NOTICE.md](https://github.com/Tamer-Mansour/tlp-icons/blob/main/NOTICE.md) and `licenses/`.
|
|
239
443
|
|
|
240
|
-
|
|
444
|
+
### Trademarks
|
|
445
|
+
|
|
446
|
+
The <!--s:brands-->376<!--/s--> brand icons (the Brands category, names starting with `brand-`) are provided for
|
|
447
|
+
referential, nominative use only: to identify a product or service, for example a "Sign in with GitHub" button or a
|
|
448
|
+
list of supported platforms. The logos remain the property of their respective owners, and their inclusion does not
|
|
449
|
+
imply any affiliation with or endorsement by those owners. Follow each owner's brand guidelines when you use them.
|
package/dist/core.js
CHANGED
|
@@ -1,185 +1,3 @@
|
|
|
1
|
-
/* tlp-icons 0.
|
|
2
|
-
/* tlp-icons runtime core: icon registry, choreography baking, run engine and the hover rule.
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
export const config = { speed: 0.6 };
|
|
6
|
-
|
|
7
|
-
const OUT = 'cubic-bezier(.22,1,.36,1)';
|
|
8
|
-
const TURN = 'cubic-bezier(.65,0,.35,1)';
|
|
9
|
-
const SINE = 'cubic-bezier(.45,.05,.55,.95)';
|
|
10
|
-
export const EASE = {
|
|
11
|
-
draw: 'cubic-bezier(.65,.05,.36,1)', fillIn: 'ease', popIn: OUT, fadeIn: 'ease', slideIn: OUT, riseIn: OUT, dropIn: OUT,
|
|
12
|
-
grow: OUT, growX: OUT, spin: TURN, spinBack: TURN, spinHalf: TURN, sweep: TURN, sweepHour: TURN, orbit: TURN,
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
const NS = 'http://www.w3.org/2000/svg';
|
|
16
|
-
const DRAWABLE = 'path,circle,rect,line,polyline,polygon,ellipse';
|
|
17
|
-
const registry = Object.create(null);
|
|
18
|
-
const aliases = Object.create(null);
|
|
19
|
-
let loader = null;
|
|
20
|
-
|
|
21
|
-
/** Adds icons: { name: { svg, m } }. */
|
|
22
|
-
const waiting = Object.create(null);
|
|
23
|
-
export function register(icons, aliasMap) {
|
|
24
|
-
for (const k in icons) registry[k] = icons[k];
|
|
25
|
-
if (aliasMap) for (const k in aliasMap) aliases[k] = aliasMap[k];
|
|
26
|
-
for (const k in waiting) {
|
|
27
|
-
const icon = getIcon(k);
|
|
28
|
-
if (icon) { waiting[k].forEach(fn => fn(icon)); delete waiting[k]; }
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
export function setLoader(fn) { loader = fn; }
|
|
32
|
-
export function resolve(name) { return registry[name] ? name : aliases[name] || name; }
|
|
33
|
-
export function getIcon(name) { return registry[resolve(name)] || null; }
|
|
34
|
-
/** Resolves with the icon data. Without a loader it waits for a later register() (classic script bundles). */
|
|
35
|
-
export function loadIcon(name) {
|
|
36
|
-
const icon = getIcon(name);
|
|
37
|
-
if (icon) return Promise.resolve(icon);
|
|
38
|
-
if (loader) return Promise.resolve(loader(resolve(name))).then(() => getIcon(name));
|
|
39
|
-
return new Promise(res => { (waiting[name] = waiting[name] || []).push(res); });
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/** Scaled timing of every step and the total length in ms. */
|
|
43
|
-
export function timings(icon, speed = config.speed) {
|
|
44
|
-
let total = 0;
|
|
45
|
-
const steps = (icon.m || []).map(([part, anim, dur, delay, origin]) => {
|
|
46
|
-
const t = Math.round(dur * speed), dl = Math.round((delay || 0) * speed);
|
|
47
|
-
total = Math.max(total, t + dl);
|
|
48
|
-
return { part, anim, t, dl, origin };
|
|
49
|
-
});
|
|
50
|
-
return { steps, total };
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
const varsOf = s => {
|
|
54
|
-
const v = { '--a': `tlp-${s.anim}`, '--t': `${s.t}ms`, '--dl': `${s.dl}ms`, '--e': EASE[s.anim] || SINE };
|
|
55
|
-
if (s.origin) v['--o'] = s.origin;
|
|
56
|
-
return v;
|
|
57
|
-
};
|
|
58
|
-
const clsOf = s => `p${s.origin && /px/.test(s.origin) ? ' vb' : ''}${s.anim === 'sweepHour' ? ' hh' : ''}`;
|
|
59
|
-
const hasClass = (cls, part) => (` ${cls} `).indexOf(` ${part} `) >= 0;
|
|
60
|
-
|
|
61
|
-
/** Builds a live <svg> element (CSP friendly: CSS variables are set through the CSSOM). */
|
|
62
|
-
export function createSvg(icon, doc = document) {
|
|
63
|
-
const svg = doc.createElementNS(NS, 'svg');
|
|
64
|
-
svg.setAttribute('viewBox', '0 0 24 24');
|
|
65
|
-
svg.setAttribute('class', 'tlp');
|
|
66
|
-
svg.setAttribute('aria-hidden', 'true');
|
|
67
|
-
svg.setAttribute('focusable', 'false');
|
|
68
|
-
svg.innerHTML = icon.svg;
|
|
69
|
-
const { steps, total } = timings(icon);
|
|
70
|
-
svg.dataset.total = String(total);
|
|
71
|
-
for (const s of steps) {
|
|
72
|
-
let targets;
|
|
73
|
-
if (s.part === 'all') {
|
|
74
|
-
const g = doc.createElementNS(NS, 'g');
|
|
75
|
-
while (svg.firstChild) g.appendChild(svg.firstChild);
|
|
76
|
-
svg.appendChild(g);
|
|
77
|
-
targets = [g];
|
|
78
|
-
} else {
|
|
79
|
-
targets = Array.prototype.filter.call(svg.querySelectorAll('[class]'), el => hasClass(el.getAttribute('class'), s.part));
|
|
80
|
-
}
|
|
81
|
-
const vars = varsOf(s);
|
|
82
|
-
for (const el of targets) {
|
|
83
|
-
el.setAttribute('class', `${el.getAttribute('class') || ''} ${clsOf(s)}`.trim());
|
|
84
|
-
for (const k in vars) el.style.setProperty(k, vars[k]);
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
svg.querySelectorAll(DRAWABLE).forEach(el => el.setAttribute('pathLength', '1'));
|
|
88
|
-
return svg;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/** Static or animated SVG markup string (animated markup uses inline style for the CSS variables). */
|
|
92
|
-
export function toSvgString(icon, opts = {}) {
|
|
93
|
-
const sw = opts.stroke ?? 2, size = opts.size ?? 24;
|
|
94
|
-
const head = `<svg xmlns="${NS}" width="${size}" height="${size}" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="${sw}" stroke-linecap="round" stroke-linejoin="round"`;
|
|
95
|
-
if (!opts.animated) {
|
|
96
|
-
const clean = icon.svg.replace(/ class="([^"]*)"/g, (_, c) => {
|
|
97
|
-
const keep = c.split(/\s+/).filter(x => x === 'd' || x === 'f');
|
|
98
|
-
return keep.length ? ` class="${keep.join(' ')}"` : '';
|
|
99
|
-
});
|
|
100
|
-
const duo = opts.duotone ? '<style>.d{fill:currentColor;fill-opacity:.16}.f{fill:currentColor}</style>' : '<style>.f{fill:currentColor}</style>';
|
|
101
|
-
return `${head}>${clean.includes('class="') ? duo : ''}${clean}</svg>`;
|
|
102
|
-
}
|
|
103
|
-
let inner = icon.svg;
|
|
104
|
-
const { steps, total } = timings(icon);
|
|
105
|
-
for (const s of steps) {
|
|
106
|
-
const style = Object.entries(varsOf(s)).map(([k, v]) => `${k}:${v}`).join(';');
|
|
107
|
-
if (s.part === 'all') { inner = `<g class="${clsOf(s)}" style="${style}">${inner}</g>`; continue; }
|
|
108
|
-
inner = inner.replace(new RegExp(`<([a-z]+)([^>]*?) class="([^"]*\\b${s.part}\\b[^"]*)"`, 'g'), (_, tag, pre, c) => `<${tag}${pre} class="${c} ${clsOf(s)}" style="${style}"`);
|
|
109
|
-
}
|
|
110
|
-
inner = inner.replace(/<(path|circle|rect|line|polyline|polygon|ellipse)\b/g, '<$1 pathLength="1"');
|
|
111
|
-
return `${head} class="tlp" data-tlp-icon data-total="${total}">${inner}</svg>`;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
const reduced = () => typeof matchMedia === 'function' && matchMedia('(prefers-reduced-motion: reduce)').matches;
|
|
115
|
-
|
|
116
|
-
/** Plays an icon's choreography once. It always finishes, even if the pointer leaves. */
|
|
117
|
-
export function run(svg, delay = 0) {
|
|
118
|
-
if (!svg || svg._tlpBusy || reduced()) return false;
|
|
119
|
-
svg._tlpBusy = true;
|
|
120
|
-
const go = () => {
|
|
121
|
-
svg.classList.remove('tlp-run');
|
|
122
|
-
void svg.getBoundingClientRect();
|
|
123
|
-
svg.classList.add('tlp-run');
|
|
124
|
-
setTimeout(() => {
|
|
125
|
-
svg.querySelectorAll('.hh').forEach(h => h.style.setProperty('--h0', `${(parseFloat(h.style.getPropertyValue('--h0')) || 0) + 30}deg`));
|
|
126
|
-
svg.classList.remove('tlp-run');
|
|
127
|
-
svg._tlpBusy = false;
|
|
128
|
-
}, (+svg.dataset.total || 0) + 40);
|
|
129
|
-
};
|
|
130
|
-
if (delay) setTimeout(go, delay); else go();
|
|
131
|
-
return true;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
export const ICON_SELECTOR = 'tlp-icon,svg[data-tlp-icon]';
|
|
135
|
-
const HOST = 'button,a,[role=button],[role=menuitem],[role=tab],[role=option],li,tr,td,th,label,summary,input,select,textarea';
|
|
136
|
-
|
|
137
|
-
function svgOf(icon) { return icon.tagName.toLowerCase() === 'svg' ? icon : icon.querySelector('svg.tlp'); }
|
|
138
|
-
function triggerOf(icon) { return icon.getAttribute('trigger') || icon.getAttribute('data-trigger') || 'hover'; }
|
|
139
|
-
|
|
140
|
-
/**
|
|
141
|
-
* Hover rule: hovering an icon animates it. Otherwise walk up to the nearest element that contains exactly one
|
|
142
|
-
* icon and animate that one. If an element with several icons is reached first (card, menu, table row), do nothing.
|
|
143
|
-
*/
|
|
144
|
-
export function hoverTarget(target) {
|
|
145
|
-
let el = target && target.nodeType === 1 ? target : target && target.parentElement;
|
|
146
|
-
if (!el) return null;
|
|
147
|
-
const own = el.closest(ICON_SELECTOR);
|
|
148
|
-
if (own) return own.closest('tlp-icon') || own;
|
|
149
|
-
for (let depth = 0; el && el.nodeType === 1 && el !== el.ownerDocument.body && depth < 8; depth++, el = el.parentElement) {
|
|
150
|
-
const list = el.querySelectorAll(ICON_SELECTOR);
|
|
151
|
-
let n = 0, found = null;
|
|
152
|
-
for (const i of list) { if (i.tagName.toLowerCase() === 'svg' && i.closest('tlp-icon')) continue; n++; found = i; }
|
|
153
|
-
if (n > 1) return null;
|
|
154
|
-
if (n === 1) return { icon: found, scope: el };
|
|
155
|
-
if (el.matches(HOST)) return null;
|
|
156
|
-
}
|
|
157
|
-
return null;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
let hoverInstalled = false;
|
|
161
|
-
export function installHover(doc = document) {
|
|
162
|
-
if (hoverInstalled) return;
|
|
163
|
-
hoverInstalled = true;
|
|
164
|
-
doc.addEventListener('mouseover', e => {
|
|
165
|
-
const hit = hoverTarget(e.target);
|
|
166
|
-
if (!hit) return;
|
|
167
|
-
const icon = hit.icon || hit;
|
|
168
|
-
const scope = hit.scope || icon;
|
|
169
|
-
if (scope.contains(e.relatedTarget)) return;
|
|
170
|
-
if (triggerOf(icon) !== 'hover') return;
|
|
171
|
-
run(svgOf(icon));
|
|
172
|
-
}, true);
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
let styleInjected = false;
|
|
176
|
-
export const CSS = "tlp-icon{display:inline-flex;align-items:center;justify-content:center;flex:none;width:var(--tlp-size,24px);height:var(--tlp-size,24px);line-height:0;vertical-align:middle;color:inherit}tlp-icon>svg,svg.tlp{width:100%;height:100%}svg.tlp{fill:none;stroke:currentColor;stroke-width:var(--tlp-stroke,2);stroke-linecap:round;stroke-linejoin:round;overflow:visible}svg.tlp .d{fill:currentColor;fill-opacity:var(--tlp-duo,0)}tlp-icon[duotone],.tlp-duotone{--tlp-duo:.16}tlp-icon[duotone=false]{--tlp-duo:0}svg.tlp .f{fill:currentColor}svg.tlp .p{transform-box:fill-box;transform-origin:var(--o,center)}svg.tlp .p.vb{transform-box:view-box}svg.tlp .p.hh{transform:rotate(var(--h0,0deg))}svg.tlp.tlp-run .p{animation:var(--a) var(--t,1000ms) var(--e,cubic-bezier(.45,.05,.55,.95)) var(--dl,0ms) 1 both}tlp-icon[flip=x]>svg,[dir=rtl] tlp-icon[flip=rtl]>svg{transform:scaleX(-1)}@keyframes tlp-draw{0%{stroke-dasharray:1;stroke-dashoffset:1}100%{stroke-dasharray:1;stroke-dashoffset:0}}@keyframes tlp-fillIn{0%{fill-opacity:0}100%{fill-opacity:var(--tlp-duo,0)}}@keyframes tlp-popIn{0%{opacity:0;transform:scale(.4)}55%{opacity:1;transform:scale(1.08)}78%{transform:scale(.98)}100%{opacity:1;transform:scale(1)}}@keyframes tlp-fadeIn{0%{opacity:0}100%{opacity:1}}@keyframes tlp-slideIn{0%{opacity:0;transform:translateX(-4px)}100%{opacity:1;transform:translateX(0)}}@keyframes tlp-riseIn{0%{opacity:0;transform:translateY(4px)}100%{opacity:1;transform:translateY(0)}}@keyframes tlp-dropIn{0%{opacity:0;transform:translateY(-5px)}60%{opacity:1;transform:translateY(.8px)}80%{transform:translateY(-.3px)}100%{opacity:1;transform:translateY(0)}}@keyframes tlp-grow{0%{transform:scaleY(0)}70%{transform:scaleY(1.06)}100%{transform:scaleY(1)}}@keyframes tlp-growX{0%{transform:scaleX(0)}100%{transform:scaleX(1)}}@keyframes tlp-pop{0%,100%{transform:scale(1)}40%{transform:scale(1.14)}68%{transform:scale(.97)}86%{transform:scale(1.02)}}@keyframes tlp-breathe{0%,100%{transform:scale(1)}50%{transform:scale(1.07)}}@keyframes tlp-jelly{0%,100%{transform:scale(1,1)}30%{transform:scale(1.1,.9)}50%{transform:scale(.94,1.06)}70%{transform:scale(1.03,.97)}85%{transform:scale(.99,1.01)}}@keyframes tlp-swing{0%,100%{transform:rotate(0)}15%{transform:rotate(13deg)}32%{transform:rotate(-10deg)}50%{transform:rotate(6deg)}68%{transform:rotate(-3deg)}84%{transform:rotate(1deg)}}@keyframes tlp-wiggle{0%,100%{transform:rotate(0)}20%{transform:rotate(-8deg)}45%{transform:rotate(6deg)}68%{transform:rotate(-3deg)}86%{transform:rotate(1deg)}}@keyframes tlp-tilt{0%,100%{transform:rotate(0)}40%{transform:rotate(-12deg)}72%{transform:rotate(3deg)}88%{transform:rotate(-1deg)}}@keyframes tlp-nod{0%,100%{transform:rotate(0)}45%{transform:rotate(7deg)}75%{transform:rotate(-1.5deg)}}@keyframes tlp-strike{0%,100%{transform:rotate(0)}38%{transform:rotate(-24deg)}56%{transform:rotate(7deg)}70%{transform:rotate(-2deg)}84%{transform:rotate(.6deg)}}@keyframes tlp-hop{0%,100%{transform:translateY(0) scale(1,1)}10%{transform:translateY(0) scale(1.04,.95)}40%{transform:translateY(-3px) scale(.97,1.04)}68%{transform:translateY(0) scale(1.04,.96)}84%{transform:translateY(0) scale(.99,1.01)}}@keyframes tlp-lift{0%,100%{transform:translateY(0)}45%{transform:translateY(-2.5px)}75%{transform:translateY(.4px)}}@keyframes tlp-sink{0%,100%{transform:translateY(0)}45%{transform:translateY(2.5px)}75%{transform:translateY(-.4px)}}@keyframes tlp-nudge{0%,100%{transform:translateX(0)}45%{transform:translateX(2.6px)}75%{transform:translateX(-.4px)}}@keyframes tlp-nudgeBack{0%,100%{transform:translateX(0)}45%{transform:translateX(-2.6px)}75%{transform:translateX(.4px)}}@keyframes tlp-float{0%,100%{transform:translateY(0)}25%{transform:translateY(-2px)}50%{transform:translateY(0)}75%{transform:translateY(-1.2px)}}@keyframes tlp-flip{0%,100%{transform:scaleX(1)}50%{transform:scaleX(-1)}}@keyframes tlp-spin{0%{transform:rotate(0)}100%{transform:rotate(360deg)}}@keyframes tlp-spinBack{0%{transform:rotate(0)}100%{transform:rotate(-360deg)}}@keyframes tlp-spinHalf{0%{transform:rotate(0)}80%{transform:rotate(188deg)}100%{transform:rotate(180deg)}}@keyframes tlp-sweep{0%{transform:rotate(0)}100%{transform:rotate(360deg)}}@keyframes tlp-sweepHour{0%{transform:rotate(var(--h0,0deg))}100%{transform:rotate(calc(var(--h0,0deg) + 30deg))}}@keyframes tlp-blink{0%,100%{transform:scaleY(1)}35%{transform:scaleY(.08)}60%{transform:scaleY(1)}}@keyframes tlp-shake{0%,100%{transform:translateX(0)}18%{transform:translateX(-1.6px)}36%{transform:translateX(1.4px)}54%{transform:translateX(-1px)}72%{transform:translateX(.6px)}88%{transform:translateX(-.2px)}}@keyframes tlp-pulseRing{0%,100%{opacity:1;transform:scale(1)}55%{opacity:0;transform:scale(1.5)}56%{opacity:0;transform:scale(.6)}}@keyframes tlp-orbit{0%{transform:rotate(0)}100%{transform:rotate(360deg)}}@keyframes tlp-stretch{0%,100%{transform:scaleX(1)}45%{transform:scaleX(1.12)}75%{transform:scaleX(.98)}}@media (prefers-reduced-motion:reduce){svg.tlp .p{animation:none!important}}";
|
|
177
|
-
export function ensureStyles(doc = document) {
|
|
178
|
-
if (styleInjected || !doc || !doc.head) return;
|
|
179
|
-
styleInjected = true;
|
|
180
|
-
if (doc.getElementById('tlp-icons-style') || CSS.length < 20) return;
|
|
181
|
-
const style = doc.createElement('style');
|
|
182
|
-
style.id = 'tlp-icons-style';
|
|
183
|
-
style.textContent = CSS;
|
|
184
|
-
doc.head.appendChild(style);
|
|
185
|
-
}
|
|
1
|
+
/* tlp-icons 0.3.0 - MIT. Icons from Lucide 1.55.0 (ISC), Tabler Icons 3.49.0 (MIT). Generated file, do not edit. */
|
|
2
|
+
const z={speed:.6},i="cubic-bezier(.22,1,.36,1)",d="cubic-bezier(.65,0,.35,1)",O="cubic-bezier(.45,.05,.55,.95)",L={draw:"cubic-bezier(.65,.05,.36,1)",fillIn:"ease",popIn:i,fadeIn:"ease",slideIn:i,riseIn:i,dropIn:i,grow:i,growX:i,spin:d,spinBack:d,spinHalf:d,sweep:d,sweepHour:d,orbit:d,launch:"cubic-bezier(.4,.1,.5,.95)",roll:"cubic-bezier(.5,.05,.4,1)"},x="http://www.w3.org/2000/svg",h=()=>typeof document>"u"?null:document,j="path,circle,rect,line,polyline,polygon,ellipse",Y=Object.create(null),w=Object.create(null);let b=null;const m=Object.create(null);function q(t,r){for(const e in t)Y[e]=t[e];if(r)for(const e in r)w[e]=r[e];for(const e in m){const a=k(e);a&&(m[e].forEach(n=>n(a)),delete m[e])}}function M(t){if(b=t,!!t)for(const r in m)Promise.resolve(t(v(r))).then(()=>{const e=m[r];if(!e)return;delete m[r];const a=k(r);e.forEach(n=>n(a))},()=>{})}function v(t){return Y[t]?t:w[t]||t}function k(t){return Y[v(t)]||null}function D(t){const r=k(t);return r?Promise.resolve(r):b?Promise.resolve(b(v(t))).then(()=>k(t)):new Promise(e=>{(m[t]=m[t]||[]).push(e)})}function $(t,r=z.speed){let e=0;return{steps:(t.m||[]).map(([n,s,l,f,o])=>{const c=Math.round(l*r),p=Math.round((f||0)*r);return e=Math.max(e,c+p),{part:n,anim:s,t:c,dl:p,origin:o}}),total:e}}const I=t=>{const r={"--a":`tlp-${t.anim}`,"--t":`${t.t}ms`,"--dl":`${t.dl}ms`,"--e":L[t.anim]||O};return t.origin&&(r["--o"]=t.origin),r},X=t=>`p${t.origin&&/px/.test(t.origin)?" vb":""}${t.anim==="sweepHour"?" hh":""}`,B=(t,r)=>` ${t} `.indexOf(` ${r} `)>=0;function U(t,r=h()){if(!r)return null;const e=r.createElementNS(x,"svg");e.setAttribute("viewBox","0 0 24 24"),e.setAttribute("class","tlp"),e.setAttribute("aria-hidden","true"),e.setAttribute("focusable","false"),e.innerHTML=t.svg;const{steps:a,total:n}=$(t);e.dataset.total=String(n);for(const s of a){let l;if(s.part==="all"){const o=r.createElementNS(x,"g");for(;e.firstChild;)o.appendChild(e.firstChild);e.appendChild(o),l=[o]}else l=Array.prototype.filter.call(e.querySelectorAll("[class]"),o=>B(o.getAttribute("class"),s.part));const f=I(s);for(const o of l){o.setAttribute("class",`${o.getAttribute("class")||""} ${X(s)}`.trim());for(const c in f)o.style.setProperty(c,f[c])}}return e.querySelectorAll(j).forEach(s=>s.setAttribute("pathLength","1")),e}function F(t,r={}){const e=r.stroke??2,a=r.size??24,n=`<svg xmlns="${x}" width="${a}" height="${a}" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="${e}" stroke-linecap="round" stroke-linejoin="round"`;if(!r.animated){const o=t.svg.replace(/ class="([^"]*)"/g,(p,y)=>{const g=y.split(/\s+/).filter(u=>u==="d"||u==="f");return g.length?` class="${g.join(" ")}"`:""}),c=r.duotone?"<style>.d{fill:currentColor;fill-opacity:.16}.f{fill:currentColor}</style>":"<style>.f{fill:currentColor}</style>";return`${n}>${o.includes('class="')?c:""}${o}</svg>`}let s=t.svg;const{steps:l,total:f}=$(t);for(const o of l){const c=Object.entries(I(o)).map(([p,y])=>`${p}:${y}`).join(";");if(o.part==="all"){s=`<g class="${X(o)}" style="${c}">${s}</g>`;continue}s=s.replace(new RegExp(`<([a-z]+)([^>]*?) class="([^"]*\\b${o.part}\\b[^"]*)"`,"g"),(p,y,g,u)=>`<${y}${g} class="${u} ${X(o)}" style="${c}"`)}return s=s.replace(/<(path|circle|rect|line|polyline|polygon|ellipse)\b/g,'<$1 pathLength="1"'),`${n} class="tlp" data-tlp-icon data-total="${f}">${s}</svg>`}const T=()=>typeof matchMedia=="function"&&matchMedia("(prefers-reduced-motion: reduce)").matches;function H(t,r=0){if(!t||t._tlpBusy||T())return!1;t._tlpBusy=!0;const e=()=>{t.classList.remove("tlp-run"),t.getBoundingClientRect(),t.classList.add("tlp-run"),setTimeout(()=>{t.querySelectorAll(".hh").forEach(a=>a.style.setProperty("--h0",`${(parseFloat(a.style.getPropertyValue("--h0"))||0)+30}deg`)),t.classList.remove("tlp-run"),t._tlpBusy=!1},(+t.dataset.total||0)+40)};return r?setTimeout(e,r):e(),!0}const C="tlp-icon,svg[data-tlp-icon]",N="button,a,[role=button],[role=menuitem],[role=tab],[role=option],li,tr,td,th,label,summary,input,select,textarea";function P(t){return t.tagName.toLowerCase()==="svg"?t:t.querySelector("svg.tlp")}function R(t){return t.getAttribute("trigger")||t.getAttribute("data-trigger")||"hover"}function _(t){let r=t&&t.nodeType===1?t:t&&t.parentElement;if(!r)return null;const e=r.closest(C);if(e)return e.closest("tlp-icon")||e;for(let a=0;r&&r.nodeType===1&&r!==r.ownerDocument.body&&a<8;a++,r=r.parentElement){const n=r.querySelectorAll(C);let s=0,l=null;for(const f of n)f.tagName.toLowerCase()==="svg"&&f.closest("tlp-icon")||(s++,l=f);if(s>1)return null;if(s===1)return{icon:l,scope:r};if(r.matches(N))return null}return null}let S=!1;function V(t=h()){S||!t||(S=!0,t.addEventListener("mouseover",r=>{const e=_(r.target);if(!e)return;const a=e.icon||e;(e.scope||a).contains(r.relatedTarget)||R(a)==="hover"&&H(P(a))},!0))}let A=!1;const E="tlp-icon{display:inline-flex;align-items:center;justify-content:center;flex:none;width:var(--tlp-size,24px);height:var(--tlp-size,24px);line-height:0;vertical-align:middle;color:inherit}tlp-icon>svg,svg.tlp{width:100%;height:100%}svg.tlp{fill:none;stroke:currentColor;stroke-width:var(--tlp-stroke,2);stroke-linecap:round;stroke-linejoin:round;overflow:visible}svg.tlp .d{fill:currentColor;fill-opacity:var(--tlp-duo,0)}tlp-icon[duotone],.tlp-duotone{--tlp-duo:.16}tlp-icon[duotone=false]{--tlp-duo:0}svg.tlp .f{fill:currentColor}svg.tlp .p{transform-box:fill-box;transform-origin:var(--o,center)}svg.tlp .p.vb{transform-box:view-box}svg.tlp .p.hh{transform:rotate(var(--h0,0deg))}svg.tlp.tlp-run .p{animation:var(--a) var(--t,1000ms) var(--e,cubic-bezier(.45,.05,.55,.95)) var(--dl,0ms) 1 both}tlp-icon[flip=x]>svg,[dir=rtl] tlp-icon[flip=rtl]>svg{transform:scaleX(-1)}@keyframes tlp-draw{0%{stroke-dasharray:1;stroke-dashoffset:1}100%{stroke-dasharray:1;stroke-dashoffset:0}}@keyframes tlp-fillIn{0%{fill-opacity:0}100%{fill-opacity:var(--tlp-duo,0)}}@keyframes tlp-popIn{0%{opacity:0;transform:scale(.4)}55%{opacity:1;transform:scale(1.08)}78%{transform:scale(.98)}100%{opacity:1;transform:scale(1)}}@keyframes tlp-fadeIn{0%{opacity:0}100%{opacity:1}}@keyframes tlp-slideIn{0%{opacity:0;transform:translateX(-4px)}100%{opacity:1;transform:translateX(0)}}@keyframes tlp-riseIn{0%{opacity:0;transform:translateY(4px)}100%{opacity:1;transform:translateY(0)}}@keyframes tlp-dropIn{0%{opacity:0;transform:translateY(-5px)}60%{opacity:1;transform:translateY(.8px)}80%{transform:translateY(-.3px)}100%{opacity:1;transform:translateY(0)}}@keyframes tlp-grow{0%{transform:scaleY(0)}70%{transform:scaleY(1.06)}100%{transform:scaleY(1)}}@keyframes tlp-growX{0%{transform:scaleX(0)}100%{transform:scaleX(1)}}@keyframes tlp-pop{0%,100%{transform:scale(1)}40%{transform:scale(1.14)}68%{transform:scale(.97)}86%{transform:scale(1.02)}}@keyframes tlp-breathe{0%,100%{transform:scale(1)}50%{transform:scale(1.07)}}@keyframes tlp-jelly{0%,100%{transform:scale(1,1)}30%{transform:scale(1.1,.9)}50%{transform:scale(.94,1.06)}70%{transform:scale(1.03,.97)}85%{transform:scale(.99,1.01)}}@keyframes tlp-swing{0%,100%{transform:rotate(0)}15%{transform:rotate(13deg)}32%{transform:rotate(-10deg)}50%{transform:rotate(6deg)}68%{transform:rotate(-3deg)}84%{transform:rotate(1deg)}}@keyframes tlp-wiggle{0%,100%{transform:rotate(0)}20%{transform:rotate(-8deg)}45%{transform:rotate(6deg)}68%{transform:rotate(-3deg)}86%{transform:rotate(1deg)}}@keyframes tlp-tilt{0%,100%{transform:rotate(0)}40%{transform:rotate(-12deg)}72%{transform:rotate(3deg)}88%{transform:rotate(-1deg)}}@keyframes tlp-nod{0%,100%{transform:rotate(0)}45%{transform:rotate(7deg)}75%{transform:rotate(-1.5deg)}}@keyframes tlp-strike{0%,100%{transform:rotate(0)}38%{transform:rotate(-24deg)}56%{transform:rotate(7deg)}70%{transform:rotate(-2deg)}84%{transform:rotate(.6deg)}}@keyframes tlp-hop{0%,100%{transform:translateY(0) scale(1,1)}10%{transform:translateY(0) scale(1.04,.95)}40%{transform:translateY(-3px) scale(.97,1.04)}68%{transform:translateY(0) scale(1.04,.96)}84%{transform:translateY(0) scale(.99,1.01)}}@keyframes tlp-lift{0%,100%{transform:translateY(0)}45%{transform:translateY(-2.5px)}75%{transform:translateY(.4px)}}@keyframes tlp-sink{0%,100%{transform:translateY(0)}45%{transform:translateY(2.5px)}75%{transform:translateY(-.4px)}}@keyframes tlp-nudge{0%,100%{transform:translateX(0)}45%{transform:translateX(2.6px)}75%{transform:translateX(-.4px)}}@keyframes tlp-nudgeBack{0%,100%{transform:translateX(0)}45%{transform:translateX(-2.6px)}75%{transform:translateX(.4px)}}@keyframes tlp-float{0%,100%{transform:translateY(0)}25%{transform:translateY(-2px)}50%{transform:translateY(0)}75%{transform:translateY(-1.2px)}}@keyframes tlp-flip{0%,100%{transform:scaleX(1)}50%{transform:scaleX(-1)}}@keyframes tlp-spin{0%{transform:rotate(0)}100%{transform:rotate(360deg)}}@keyframes tlp-spinBack{0%{transform:rotate(0)}100%{transform:rotate(-360deg)}}@keyframes tlp-spinHalf{0%{transform:rotate(0)}80%{transform:rotate(188deg)}100%{transform:rotate(180deg)}}@keyframes tlp-sweep{0%{transform:rotate(0)}100%{transform:rotate(360deg)}}@keyframes tlp-sweepHour{0%{transform:rotate(var(--h0,0deg))}100%{transform:rotate(calc(var(--h0,0deg) + 30deg))}}@keyframes tlp-blink{0%,100%{transform:scaleY(1)}35%{transform:scaleY(.08)}60%{transform:scaleY(1)}}@keyframes tlp-shake{0%,100%{transform:translateX(0)}18%{transform:translateX(-1.6px)}36%{transform:translateX(1.4px)}54%{transform:translateX(-1px)}72%{transform:translateX(.6px)}88%{transform:translateX(-.2px)}}@keyframes tlp-pulseRing{0%,100%{opacity:1;transform:scale(1)}55%{opacity:0;transform:scale(1.5)}56%{opacity:0;transform:scale(.6)}}@keyframes tlp-orbit{0%{transform:rotate(0)}100%{transform:rotate(360deg)}}@keyframes tlp-stretch{0%,100%{transform:scaleX(1)}45%{transform:scaleX(1.12)}75%{transform:scaleX(.98)}}@keyframes tlp-buzz{0%,100%{transform:translate(0,0) rotate(0)}8%{transform:translate(-.9px,.4px) rotate(-3deg)}16%{transform:translate(.9px,-.4px) rotate(3deg)}24%{transform:translate(-.8px,.4px) rotate(-2.6deg)}32%{transform:translate(.8px,-.3px) rotate(2.6deg)}40%{transform:translate(-.6px,.3px) rotate(-2deg)}48%{transform:translate(.6px,-.2px) rotate(2deg)}58%{transform:translate(-.4px,.2px) rotate(-1.2deg)}68%{transform:translate(.3px,-.1px) rotate(1deg)}80%{transform:translate(-.15px,0) rotate(-.4deg)}}@keyframes tlp-tap{0%,100%{transform:translateY(0) scale(1)}16%{transform:translateY(1.8px) scale(.94)}30%{transform:translateY(-.3px) scale(1.02)}44%{transform:translateY(0) scale(1)}60%{transform:translateY(1.8px) scale(.94)}78%{transform:translateY(-.4px) scale(1.02)}90%{transform:translateY(.1px) scale(.995)}}@keyframes tlp-press{0%,100%{transform:scale(1)}20%{transform:scale(.88)}42%{transform:scale(.88)}62%{transform:scale(1.07)}80%{transform:scale(.98)}91%{transform:scale(1.01)}}@keyframes tlp-beat{0%,100%{transform:scale(1)}14%{transform:scale(1.16)}28%{transform:scale(.98)}42%{transform:scale(1.09)}58%{transform:scale(1)}}@keyframes tlp-flicker{0%,100%{opacity:1;transform:scale(1)}8%{opacity:.55;transform:scale(.97)}16%{opacity:.95;transform:scale(1.02)}26%{opacity:.4;transform:scale(.96)}38%{opacity:1;transform:scale(1.03)}50%{opacity:.7;transform:scale(.99)}62%{opacity:1;transform:scale(1.015)}78%{opacity:.85;transform:scale(1)}}@keyframes tlp-twinkle{0%,100%{transform:scale(1) rotate(0)}30%{transform:scale(.8) rotate(-14deg)}60%{transform:scale(1.18) rotate(8deg)}80%{transform:scale(.97) rotate(-2deg)}}@keyframes tlp-sway{0%,100%{transform:rotate(0)}24%{transform:rotate(6deg)}48%{transform:rotate(-4.5deg)}70%{transform:rotate(2.5deg)}86%{transform:rotate(-1deg)}}@keyframes tlp-flutter{0%,100%{transform:scale(1,1)}20%{transform:skewY(-7deg) scaleX(.95)}42%{transform:skewY(6deg) scaleX(1.03)}62%{transform:skewY(-3.5deg) scaleX(.98)}82%{transform:skewY(1.5deg) scaleX(1.01)}}@keyframes tlp-bob{0%,100%{transform:translateY(0) rotate(0)}25%{transform:translateY(-1.8px) rotate(-5deg)}52%{transform:translateY(1.2px) rotate(4deg)}76%{transform:translateY(-.8px) rotate(-2deg)}}@keyframes tlp-drive{0%,100%{transform:translateX(0) rotate(0)}22%{transform:translateX(-1px) rotate(-3.5deg)}55%{transform:translateX(2.4px) rotate(1.2deg)}78%{transform:translateX(-.3px) rotate(-.4deg)}}@keyframes tlp-launch{0%,100%{opacity:1;transform:translate(0,0)}14%{opacity:1;transform:translate(-.8px,.8px)}42%{opacity:0;transform:translate(5px,-5px)}43%{opacity:0;transform:translate(-5px,5px)}72%{opacity:1;transform:translate(.5px,-.5px)}}@keyframes tlp-roll{0%{transform:rotate(0) translateX(0)}50%{transform:rotate(180deg) translateX(1.2px)}100%{transform:rotate(360deg) translateX(0)}}@keyframes tlp-zoomIn{0%{opacity:0;transform:scale(1.35)}55%{opacity:1;transform:scale(.96)}78%{transform:scale(1.02)}100%{opacity:1;transform:scale(1)}}@keyframes tlp-spiralIn{0%{opacity:0;transform:rotate(-120deg) scale(0)}55%{opacity:1;transform:rotate(7deg) scale(1.1)}78%{transform:rotate(-2deg) scale(.98)}100%{opacity:1;transform:rotate(0) scale(1)}}@keyframes tlp-swingIn{0%{opacity:0;transform:rotate(-25deg)}30%{opacity:1;transform:rotate(9deg)}55%{transform:rotate(-4deg)}78%{transform:rotate(1.5deg)}100%{opacity:1;transform:rotate(0)}}@keyframes tlp-flipIn{0%{opacity:0;transform:scaleX(0)}50%{opacity:1;transform:scaleX(1.1)}75%{transform:scaleX(.96)}100%{opacity:1;transform:scaleX(1)}}@keyframes tlp-focusIn{0%{opacity:.15;filter:blur(1.6px);transform:scale(1.12)}60%{opacity:1;filter:blur(.5px);transform:scale(.98)}100%{opacity:1;filter:blur(0);transform:scale(1)}}@keyframes tlp-bloom{0%{opacity:0;transform:rotate(-45deg) scale(0)}55%{opacity:1;transform:rotate(8deg) scale(1.12)}78%{transform:rotate(-2deg) scale(.98)}100%{opacity:1;transform:rotate(0) scale(1)}}@keyframes tlp-rollIn{0%{opacity:0;transform:translateX(-5px) rotate(-180deg)}70%{opacity:1;transform:translateX(.6px) rotate(8deg)}100%{opacity:1;transform:translateX(0) rotate(0)}}@keyframes tlp-tada{0%,100%{transform:scale(1) rotate(0)}10%,20%{transform:scale(.92) rotate(-3deg)}30%,50%,70%{transform:scale(1.1) rotate(3deg)}40%,60%{transform:scale(1.1) rotate(-3deg)}82%{transform:scale(1.02) rotate(0)}}@keyframes tlp-rubber{0%,100%{transform:scale(1,1)}30%{transform:scale(1.2,.8)}45%{transform:scale(.85,1.15)}62%{transform:scale(1.08,.92)}78%{transform:scale(.97,1.03)}90%{transform:scale(1.01,.99)}}@keyframes tlp-rattle{0%,100%{transform:rotate(0)}10%{transform:rotate(-9deg)}22%{transform:rotate(8deg)}34%{transform:rotate(-7deg)}46%{transform:rotate(6deg)}58%{transform:rotate(-4deg)}70%{transform:rotate(3deg)}82%{transform:rotate(-1.5deg)}92%{transform:rotate(.6deg)}}@keyframes tlp-spring{0%,100%{transform:translateY(0) scale(1,1)}16%{transform:translateY(0) scale(1.08,.82)}40%{transform:translateY(-3px) scale(.94,1.12)}62%{transform:translateY(0) scale(1.05,.94)}80%{transform:translateY(0) scale(.99,1.02)}}@keyframes tlp-knock{0%,100%{transform:translateX(0) scale(1,1)}14%{transform:translateX(1.9px) scale(.96,1)}28%{transform:translateX(0) scale(1,1)}42%{transform:translateX(1.9px) scale(.96,1)}58%{transform:translateX(0) scale(1,1)}72%{transform:translateX(.5px) scale(.99,1)}}@keyframes tlp-wave{0%,100%{transform:rotate(0)}18%{transform:rotate(15deg)}36%{transform:rotate(-13deg)}54%{transform:rotate(10deg)}70%{transform:rotate(-6deg)}85%{transform:rotate(2deg)}}@keyframes tlp-pendulum{0%,100%{transform:rotate(0)}20%{transform:rotate(22deg)}42%{transform:rotate(-16deg)}62%{transform:rotate(9deg)}80%{transform:rotate(-4deg)}92%{transform:rotate(1.5deg)}}@keyframes tlp-twirl{0%{transform:rotate(0) scale(1)}50%{transform:rotate(180deg) scale(.82)}100%{transform:rotate(360deg) scale(1)}}@keyframes tlp-flipY{0%,100%{transform:scaleY(1)}38%{transform:scaleY(-1.08)}52%{transform:scaleY(-1)}66%{transform:scaleY(-1.05)}}@keyframes tlp-drip{0%,100%{opacity:1;transform:translateY(0) scale(1)}34%{opacity:0;transform:translateY(3px) scale(.82)}35%{opacity:0;transform:translateY(-3px) scale(.82)}68%{opacity:1;transform:translateY(.6px) scale(1.04)}84%{opacity:1;transform:translateY(-.2px) scale(1)}}@keyframes tlp-fall{0%,100%{opacity:1;transform:translateY(0)}38%{opacity:0;transform:translateY(3.5px)}39%{opacity:0;transform:translateY(-3.5px)}72%{opacity:1;transform:translateY(.7px)}86%{opacity:1;transform:translateY(-.2px)}}@keyframes tlp-retrace{0%,100%{opacity:1;stroke-dasharray:1;stroke-dashoffset:0}30%{opacity:1;stroke-dasharray:1;stroke-dashoffset:.96}33%{opacity:0;stroke-dasharray:1;stroke-dashoffset:1}40%{opacity:0;stroke-dasharray:1;stroke-dashoffset:1}41%{opacity:1;stroke-dasharray:1;stroke-dashoffset:1}}@keyframes tlp-peek{0%,100%{transform:translateY(0)}28%{transform:translateY(2px)}62%{transform:translateY(-1.1px)}82%{transform:translateY(.3px)}}@media (prefers-reduced-motion:reduce){svg.tlp .p{animation:none!important}}";function W(t=h()){if(A||!t||!t.head||(A=!0,t.getElementById("tlp-icons-style")||E.length<20))return;const r=t.createElement("style");r.id="tlp-icons-style",r.textContent=E,t.head.appendChild(r)}export{E as CSS,L as EASE,C as ICON_SELECTOR,z as config,U as createSvg,W as ensureStyles,k as getIcon,_ as hoverTarget,V as installHover,D as loadIcon,q as register,v as resolve,H as run,M as setLoader,$ as timings,F as toSvgString};
|
|
3
|
+
//# sourceMappingURL=core.js.map
|