lapikit 0.4.3 → 0.4.4

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.
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-align-end-horizontal-icon lucide-align-end-horizontal"><rect width="6" height="16" x="4" y="2" rx="2"/><rect width="6" height="9" x="14" y="9" rx="2"/><path d="M22 22H2"/></svg>
@@ -1,2 +1,2 @@
1
- const lapikitComponents = ['sheet', 'app', 'btn'];
1
+ const lapikitComponents = ['sheet', 'app', 'btn', 'icon'];
2
2
  export default lapikitComponents;
@@ -16,7 +16,7 @@
16
16
  density = 'default',
17
17
  loading,
18
18
  active = false,
19
- size = 'md',
19
+ size = 'default',
20
20
  rounded,
21
21
  disabled = false,
22
22
  block,
@@ -28,6 +28,10 @@
28
28
  label,
29
29
  wide,
30
30
  noRipple,
31
+ load,
32
+ append,
33
+ prepend,
34
+ icon,
31
35
  ...rest
32
36
  }: ButtonProps = $props();
33
37
 
@@ -37,7 +41,11 @@
37
41
  : 'filled'
38
42
  );
39
43
  let safeSize = $derived(
40
- size === 'xs' || size === 'sm' || size === 'md' || size === 'lg' || size === 'xl' ? size : 'md'
44
+ size === 'default'
45
+ ? 'md'
46
+ : size === 'xs' || size === 'sm' || size === 'md' || size === 'lg' || size === 'xl'
47
+ ? size
48
+ : 'md'
41
49
  );
42
50
  let safeDensity = $derived(
43
51
  density === 'compact' || density === 'comfortable' || density === 'default'
@@ -96,6 +104,7 @@
96
104
  aria-disabled={isDisabled || undefined}
97
105
  data-block={block}
98
106
  data-wide={wide}
107
+ data-icon={icon}
99
108
  use:ripple={{
100
109
  component: 'btn',
101
110
  disabled: noRipple || disabled
@@ -137,6 +146,7 @@
137
146
  aria-disabled={isDisabled || undefined}
138
147
  data-block={block}
139
148
  data-wide={wide}
149
+ data-icon={icon}
140
150
  use:ripple={{
141
151
  component: 'btn',
142
152
  disabled: noRipple || disabled
@@ -147,13 +157,29 @@
147
157
  {/if}
148
158
 
149
159
  <span class="kit-btn__inner">
160
+ {#if prepend}
161
+ <span class="kit-btn__prepend">
162
+ {@render prepend?.()}
163
+ </span>
164
+ {/if}
150
165
  <span class="kit-btn__content">
151
166
  {@render children?.()}
152
167
  </span>
168
+ {#if append}
169
+ <span class="kit-btn__append">
170
+ {@render append?.()}
171
+ </span>
172
+ {/if}
153
173
  </span>
154
174
 
155
175
  {#if loading}
156
- <span class="spinner">...</span>
176
+ <span class="spinner">
177
+ {#if load}
178
+ {@render load?.()}
179
+ {:else}
180
+ ...
181
+ {/if}
182
+ </span>
157
183
  {/if}
158
184
  </svelte:element>
159
185
  {/if}
@@ -204,6 +230,7 @@
204
230
  --btn-density-height-scale: 1;
205
231
  --btn-radius: 8px;
206
232
  --btn-shape: var(--btn-radius);
233
+ --kit-btn-gap: 0.45em;
207
234
 
208
235
  position: relative;
209
236
  display: inline-flex;
@@ -322,6 +349,9 @@
322
349
  --btn-px: var(--kit-btn-px-xs);
323
350
  font-size: 12px;
324
351
  }
352
+ .kit-btn[data-size='xs'] :global(.kit-icon:not([data-size])) {
353
+ --kit-icon-current-size: var(--kit-icon-size-xs);
354
+ }
325
355
 
326
356
  .kit-btn[data-size='sm'] {
327
357
  --btn-h: var(--kit-btn-h-sm);
@@ -329,6 +359,9 @@
329
359
  --btn-px: var(--kit-btn-px-sm);
330
360
  font-size: 13px;
331
361
  }
362
+ .kit-btn[data-size='sm'] :global(.kit-icon:not([data-size])) {
363
+ --kit-icon-current-size: var(--kit-icon-size-sm);
364
+ }
332
365
 
333
366
  .kit-btn[data-size='md'] {
334
367
  --btn-h: var(--kit-btn-h-md);
@@ -336,6 +369,9 @@
336
369
  --btn-px: var(--kit-btn-px-md);
337
370
  font-size: 14px;
338
371
  }
372
+ .kit-btn[data-size='md'] :global(.kit-icon:not([data-size])) {
373
+ --kit-icon-current-size: var(--kit-icon-size-md);
374
+ }
339
375
 
340
376
  .kit-btn[data-size='lg'] {
341
377
  --btn-h: var(--kit-btn-h-lg);
@@ -343,6 +379,9 @@
343
379
  --btn-px: var(--kit-btn-px-lg);
344
380
  font-size: 15px;
345
381
  }
382
+ .kit-btn[data-size='lg'] :global(.kit-icon:not([data-size])) {
383
+ --kit-icon-current-size: var(--kit-icon-size-lg);
384
+ }
346
385
 
347
386
  .kit-btn[data-size='xl'] {
348
387
  --btn-h: var(--kit-btn-h-xl);
@@ -350,6 +389,9 @@
350
389
  --btn-px: var(--kit-btn-px-xl);
351
390
  font-size: 16px;
352
391
  }
392
+ .kit-btn[data-size='xl'] :global(.kit-icon:not([data-size])) {
393
+ --kit-icon-current-size: var(--kit-icon-size-xl);
394
+ }
353
395
 
354
396
  /* density */
355
397
  .kit-btn[data-density='default'] {
@@ -402,9 +444,38 @@
402
444
  }
403
445
 
404
446
  .kit-btn__inner {
405
- display: flex;
447
+ display: inline-flex;
406
448
  align-items: center;
449
+ justify-content: center;
407
450
  gap: var(--kit-btn-gap);
451
+ line-height: 1;
452
+ }
453
+
454
+ .kit-btn__content,
455
+ .kit-btn__prepend,
456
+ .kit-btn__append {
457
+ display: inline-flex;
458
+ align-items: center;
459
+ justify-content: center;
460
+ line-height: 1;
461
+ }
462
+
463
+ .kit-btn__content :global(.kit-icon),
464
+ .kit-btn__prepend :global(.kit-icon),
465
+ .kit-btn__append :global(.kit-icon) {
466
+ display: inline-flex;
467
+ align-items: center;
468
+ justify-content: center;
469
+ vertical-align: middle;
470
+ }
471
+
472
+ .kit-btn__content :global(svg),
473
+ .kit-btn__prepend :global(svg),
474
+ .kit-btn__append :global(svg),
475
+ .kit-btn__content :global(img),
476
+ .kit-btn__prepend :global(img),
477
+ .kit-btn__append :global(img) {
478
+ display: block;
408
479
  }
409
480
 
410
481
  .kit-btn[data-loading='true'] .kit-btn__inner,
@@ -431,6 +502,16 @@
431
502
  width: 100%;
432
503
  }
433
504
 
505
+ .kit-btn[data-icon='true'] {
506
+ width: max(28px, calc(var(--btn-h) * var(--btn-density-height-scale)));
507
+ min-width: max(28px, calc(var(--btn-h) * var(--btn-density-height-scale)));
508
+ padding-inline: 0;
509
+ }
510
+
511
+ .kit-btn[data-icon='true'] .kit-btn__inner {
512
+ gap: 0;
513
+ }
514
+
434
515
  .kit-btn[data-disabled='true'] {
435
516
  pointer-events: none;
436
517
  background: color-mix(in oklab, var(--btn-bg), transparent 70%);
@@ -440,6 +521,17 @@
440
521
  cursor: not-allowed;
441
522
  }
442
523
 
524
+ .kit-btn[data-disabled='true'] :global(.kit-icon) {
525
+ color: color-mix(in oklab, var(--btn-fg), transparent 45%) !important;
526
+ --kit-icon-color: color-mix(in oklab, var(--btn-fg), transparent 45%) !important;
527
+ }
528
+
529
+ .kit-btn[data-disabled='true'] :global(.kit-icon img),
530
+ .kit-btn[data-disabled='true'] :global(.kit-icon .kit-icon__mask) {
531
+ opacity: 0.7;
532
+ filter: grayscale(0.2);
533
+ }
534
+
443
535
  .kit-btn[data-disabled='true'] > input {
444
536
  cursor: not-allowed;
445
537
  }
@@ -1,4 +1,5 @@
1
1
  import type { Component, RoundedType, SizeType } from '../../utils/types/index.js';
2
+ import type { Snippet } from 'svelte';
2
3
  export interface ButtonProps extends Component {
3
4
  ref?: HTMLElement | null;
4
5
  is?: 'button' | 'a' | 'input';
@@ -18,4 +19,8 @@ export interface ButtonProps extends Component {
18
19
  block?: boolean;
19
20
  wide?: boolean;
20
21
  noRipple?: boolean;
22
+ icon?: boolean;
23
+ load?: Snippet;
24
+ append?: Snippet;
25
+ prepend?: Snippet;
21
26
  }
@@ -0,0 +1,186 @@
1
+ <script lang="ts">
2
+ import { useClassName, useStyles } from '../../utils/index.js';
3
+ import { makeComponentProps } from '../../compiler/mapped-code.js';
4
+ import type { IconProps } from './icon.types.js';
5
+
6
+ let {
7
+ ref = $bindable(),
8
+ is = 'i',
9
+ children = undefined,
10
+ class: className = '',
11
+ style: styleAttr = '',
12
+ 's-class': sClass,
13
+ 's-style': sStyle,
14
+ name = undefined,
15
+ src = undefined,
16
+ icon = undefined,
17
+ size = 'default',
18
+ alt = '',
19
+ label = undefined,
20
+ decorative = true,
21
+ loading = 'eager',
22
+ decoding = 'async',
23
+ color = undefined,
24
+ colorMode = 'auto',
25
+ imgFilter = undefined,
26
+ ...rest
27
+ }: IconProps = $props();
28
+
29
+ let { classProps, styleProps, restProps } = $derived(
30
+ makeComponentProps(rest as Record<string, unknown>)
31
+ );
32
+
33
+ let componentClass = $derived(
34
+ useClassName({
35
+ baseClass: 'kit-icon',
36
+ className: `${className ?? ''}`.trim(),
37
+ sClass,
38
+ classProps
39
+ })
40
+ );
41
+
42
+ let componentStyle = $derived(
43
+ useStyles({
44
+ styleAttr,
45
+ sStyle,
46
+ styleProps
47
+ })
48
+ );
49
+
50
+ let safeSize = $derived(
51
+ size === 'default' || size === undefined
52
+ ? undefined
53
+ : size === 'xs' || size === 'sm' || size === 'md' || size === 'lg' || size === 'xl'
54
+ ? size
55
+ : 'md'
56
+ );
57
+
58
+ let resolvedSrc = $derived(src ?? (icon?.includes('/') ? icon : undefined));
59
+ let resolvedName = $derived(name ?? (!icon?.includes('/') ? icon : undefined));
60
+ let hasChildren = $derived(!!children);
61
+ let hasContent = $derived(hasChildren || !!resolvedSrc || !!resolvedName);
62
+ let classWithIconName = $derived(
63
+ !hasChildren && !resolvedSrc && resolvedName
64
+ ? `${componentClass} ${resolvedName}`.trim()
65
+ : componentClass
66
+ );
67
+
68
+ let safeLoading = $derived(loading === 'lazy' || loading === 'eager' ? loading : 'eager');
69
+ let safeDecoding = $derived(
70
+ decoding === 'async' || decoding === 'sync' || decoding === 'auto' ? decoding : 'async'
71
+ );
72
+ let safeColorMode = $derived(
73
+ colorMode === 'auto' || colorMode === 'none' || colorMode === 'mask' || colorMode === 'filter'
74
+ ? colorMode
75
+ : 'auto'
76
+ );
77
+
78
+ let iconRole = $derived(!decorative && (label || alt) ? 'img' : undefined);
79
+ let iconAriaLabel = $derived(!decorative ? label || alt || undefined : undefined);
80
+ let iconAriaHidden = $derived(decorative ? true : undefined);
81
+ let imgAlt = $derived(decorative ? '' : alt || label || '');
82
+ let isSvgSrc = $derived(!!resolvedSrc && /\.svg($|\?)/i.test(resolvedSrc));
83
+ let useMaskMode = $derived(
84
+ !!resolvedSrc &&
85
+ (color ? safeColorMode === 'mask' || (safeColorMode === 'auto' && isSvgSrc) : false)
86
+ );
87
+ let useFilterMode = $derived(
88
+ !!resolvedSrc && !!imgFilter && (safeColorMode === 'filter' || safeColorMode === 'auto')
89
+ );
90
+ let mergedStyle = $derived(
91
+ [componentStyle, color ? `color:${color}` : '', color ? `--kit-icon-color:${color}` : '']
92
+ .filter(Boolean)
93
+ .join('; ')
94
+ );
95
+ </script>
96
+
97
+ {#if hasContent}
98
+ <svelte:element
99
+ this={is}
100
+ bind:this={ref}
101
+ class={classWithIconName}
102
+ style={mergedStyle}
103
+ data-size={safeSize}
104
+ role={iconRole}
105
+ aria-label={iconAriaLabel}
106
+ aria-hidden={iconAriaHidden}
107
+ {...restProps}
108
+ >
109
+ {#if hasChildren}
110
+ {@render children?.()}
111
+ {:else if resolvedSrc}
112
+ {#if useMaskMode}
113
+ <span
114
+ class="kit-icon__mask"
115
+ style={`--kit-icon-mask-image: url("${resolvedSrc}")`}
116
+ aria-hidden="true"
117
+ ></span>
118
+ {:else}
119
+ <img
120
+ src={resolvedSrc}
121
+ alt={imgAlt}
122
+ loading={safeLoading}
123
+ decoding={safeDecoding}
124
+ style={useFilterMode ? `filter:${imgFilter}` : undefined}
125
+ />
126
+ {/if}
127
+ {/if}
128
+ </svelte:element>
129
+ {/if}
130
+
131
+ <style>
132
+ .kit-icon {
133
+ --kit-icon-size-xs: 0.875rem;
134
+ --kit-icon-size-sm: 1rem;
135
+ --kit-icon-size-md: 1.125rem;
136
+ --kit-icon-size-lg: 1.25rem;
137
+ --kit-icon-size-xl: 1.375rem;
138
+ --kit-icon-current-size: var(--kit-icon-size-md);
139
+
140
+ display: inline-flex;
141
+ align-items: center;
142
+ justify-content: center;
143
+ text-indent: 0;
144
+ line-height: 1;
145
+ font-size: var(--kit-icon-current-size);
146
+ vertical-align: middle;
147
+ }
148
+
149
+ .kit-icon[data-size='xs'] {
150
+ --kit-icon-current-size: var(--kit-icon-size-xs);
151
+ }
152
+ .kit-icon[data-size='sm'] {
153
+ --kit-icon-current-size: var(--kit-icon-size-sm);
154
+ }
155
+ .kit-icon[data-size='md'] {
156
+ --kit-icon-current-size: var(--kit-icon-size-md);
157
+ }
158
+ .kit-icon[data-size='lg'] {
159
+ --kit-icon-current-size: var(--kit-icon-size-lg);
160
+ }
161
+ .kit-icon[data-size='xl'] {
162
+ --kit-icon-current-size: var(--kit-icon-size-xl);
163
+ }
164
+
165
+ .kit-icon :global(svg),
166
+ .kit-icon img,
167
+ .kit-icon .kit-icon__mask {
168
+ width: var(--kit-icon-current-size);
169
+ height: var(--kit-icon-current-size);
170
+ flex-shrink: 0;
171
+ display: block;
172
+ }
173
+
174
+ .kit-icon .kit-icon__mask {
175
+ display: inline-block;
176
+ background-color: var(--kit-icon-color, currentColor);
177
+ mask-image: var(--kit-icon-mask-image);
178
+ mask-repeat: no-repeat;
179
+ mask-size: contain;
180
+ mask-position: center;
181
+ -webkit-mask-image: var(--kit-icon-mask-image);
182
+ -webkit-mask-repeat: no-repeat;
183
+ -webkit-mask-size: contain;
184
+ -webkit-mask-position: center;
185
+ }
186
+ </style>
@@ -0,0 +1,4 @@
1
+ import type { IconProps } from './icon.types.ts';
2
+ declare const Icon: import("svelte").Component<IconProps, {}, "ref">;
3
+ type Icon = ReturnType<typeof Icon>;
4
+ export default Icon;
@@ -0,0 +1,16 @@
1
+ import type { Component, SizeType } from '../../utils/types/index.js';
2
+ export interface IconProps extends Component {
3
+ ref?: HTMLElement | null;
4
+ name?: string;
5
+ src?: string;
6
+ icon?: string;
7
+ size?: SizeType;
8
+ alt?: string;
9
+ label?: string;
10
+ decorative?: boolean;
11
+ loading?: 'eager' | 'lazy';
12
+ decoding?: 'sync' | 'async' | 'auto';
13
+ color?: string;
14
+ colorMode?: 'auto' | 'none' | 'mask' | 'filter';
15
+ imgFilter?: string;
16
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -1,3 +1,4 @@
1
1
  export { default as KitSheet } from './sheet/sheet.svelte';
2
2
  export { default as KitApp } from './app/app.svelte';
3
3
  export { default as KitBtn } from './btn/btn.svelte';
4
+ export { default as KitIcon } from './icon/icon.svelte';
@@ -2,3 +2,4 @@
2
2
  export { default as KitSheet } from './sheet/sheet.svelte';
3
3
  export { default as KitApp } from './app/app.svelte';
4
4
  export { default as KitBtn } from './btn/btn.svelte';
5
+ export { default as KitIcon } from './icon/icon.svelte';
@@ -6,7 +6,7 @@ type KitClassNameType = string | string[] | undefined;
6
6
  type KitStylePropertiesType = Record<string, boolean | string> | undefined;
7
7
  type StylePropertiesType = string | undefined;
8
8
  export type PropValue = string | boolean | number | null | undefined;
9
- export type SizeType = 'xs' | 'sm' | 'md' | 'lg' | 'xl';
9
+ export type SizeType = 'default' | 'xs' | 'sm' | 'md' | 'lg' | 'xl';
10
10
  export type RoundedType = 0 | '0' | 'xs' | 'sm' | 'md' | 'lg' | 'xl';
11
11
  export interface useClassNameProps {
12
12
  baseClass?: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lapikit",
3
- "version": "0.4.3",
3
+ "version": "0.4.4",
4
4
  "license": "MIT",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -89,10 +89,12 @@
89
89
  "devDependencies": {
90
90
  "@eslint/compat": "^1.2.5",
91
91
  "@eslint/js": "^9.18.0",
92
+ "@mdi/js": "^7.4.47",
92
93
  "@sveltejs/adapter-auto": "^4.0.0",
93
94
  "@sveltejs/kit": "^2.16.0",
94
95
  "@sveltejs/package": "^2.0.0",
95
96
  "@sveltejs/vite-plugin-svelte": "^6.2.1",
97
+ "@tabler/icons-svelte": "^3.40.0",
96
98
  "@testing-library/jest-dom": "^6.6.3",
97
99
  "@testing-library/svelte": "^5.2.4",
98
100
  "eslint": "^9.18.0",
@@ -100,6 +102,8 @@
100
102
  "eslint-plugin-svelte": "^3.0.0",
101
103
  "globals": "^16.0.0",
102
104
  "jsdom": "^26.0.0",
105
+ "lucide-svelte": "^0.577.0",
106
+ "mingcute_icon": "^2.9.71",
103
107
  "prettier": "^3.4.2",
104
108
  "prettier-plugin-svelte": "^3.3.3",
105
109
  "publint": "^0.3.2",