lapikit 0.6.0 → 0.6.1

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.
@@ -1,3 +1,4 @@
1
1
  export type PropValue = string | boolean | number | null | undefined;
2
2
  export type SizeType = 'default' | 'xs' | 'sm' | 'md' | 'lg' | 'xl';
3
3
  export type RoundedType = 0 | '0' | 'xs' | 'sm' | 'md' | 'lg' | 'xl';
4
+ export type DensityType = 'none' | 'compact' | 'default' | 'comfortable';
@@ -1,4 +1,4 @@
1
- import type { RippleProps } from '../utils/types';
1
+ import type { RippleProps } from '../@types';
2
2
  export declare function ripple(el: HTMLElement, options?: RippleProps): {
3
3
  destroy(): void;
4
4
  update(newOptions: RippleProps): void;
@@ -1,7 +1,17 @@
1
1
  const triggerEvents = ['pointerdown', 'touchstart', 'keydown'];
2
2
  const cancelEvents = ['mouseleave', 'dragleave', 'touchmove', 'touchcancel', 'pointerup', 'keyup'];
3
+ function resolveDuration(options) {
4
+ return options.duration && options.duration > 0 ? options.duration : undefined;
5
+ }
6
+ function getCoords(e) {
7
+ if (window.TouchEvent && e instanceof TouchEvent) {
8
+ return { x: e.touches[0].clientX, y: e.touches[0].clientY };
9
+ }
10
+ return { x: e.clientX, y: e.clientY };
11
+ }
3
12
  export function ripple(el, options = {}) {
4
13
  const rippleContainer = document.createElement('div');
14
+ const activeRipples = new Set();
5
15
  addClasses();
6
16
  setOptions(options);
7
17
  function isAriaDisabled() {
@@ -19,24 +29,22 @@ export function ripple(el, options = {}) {
19
29
  rippleContainer.classList.add('kit-ripple--center');
20
30
  }
21
31
  }
22
- function setOptions(options) {
23
- if (options.disabled || isAriaDisabled()) {
32
+ function setOptions(newOptions) {
33
+ if (newOptions.disabled || isAriaDisabled()) {
24
34
  rippleContainer.remove();
25
35
  }
26
36
  else {
27
37
  el.appendChild(rippleContainer);
28
38
  }
29
- if (options.duration && options.duration < 0) {
30
- options.duration = undefined;
39
+ if (newOptions.component) {
40
+ rippleContainer.style.setProperty('--system-ripple-radius', `var(--kit-${newOptions.component}-radius)`);
31
41
  }
32
- if (options.component) {
33
- rippleContainer.style.setProperty('--system-ripple-radius', `var(--${options.component}-shape)`);
42
+ if (newOptions.color) {
43
+ rippleContainer.style.setProperty('--system-ripple-color', newOptions.color);
34
44
  }
35
- if (options.color) {
36
- rippleContainer.style.setProperty('--system-ripple-color', options.color);
37
- }
38
- if (options.duration) {
39
- rippleContainer.style.setProperty('--system-animation-ripple-duration', `${options.duration}ms`);
45
+ const duration = resolveDuration(newOptions);
46
+ if (duration) {
47
+ rippleContainer.style.setProperty('--system-animation-ripple-duration', `${duration}ms`);
40
48
  }
41
49
  }
42
50
  function createRipple(e, center) {
@@ -54,12 +62,7 @@ export function ripple(el, options = {}) {
54
62
  }
55
63
  addClasses(center);
56
64
  const rect = el.getBoundingClientRect();
57
- const clientX = window.TouchEvent && e instanceof TouchEvent
58
- ? e.touches[0].clientX
59
- : e.clientX;
60
- const clientY = window.TouchEvent && e instanceof TouchEvent
61
- ? e.touches[0].clientY
62
- : e.clientY;
65
+ const { x: clientX, y: clientY } = getCoords(e);
63
66
  const x = clientX - rect.left > el.offsetWidth / 2 ? 0 : el.offsetWidth;
64
67
  const y = clientY - rect.top > el.offsetHeight / 2 ? 0 : el.offsetHeight;
65
68
  const radius = Math.hypot(x - (clientX - rect.left), y - (clientY - rect.top));
@@ -69,16 +72,23 @@ export function ripple(el, options = {}) {
69
72
  ripple.style.top = `${clientY - rect.top - radius}px`;
70
73
  ripple.style.width = ripple.style.height = `${radius * 2}px`;
71
74
  rippleContainer.appendChild(ripple);
72
- function removeRipple() {
73
- if (ripple === null) {
74
- return;
75
+ let timeoutId;
76
+ function cleanup() {
77
+ if (timeoutId) {
78
+ clearTimeout(timeoutId);
75
79
  }
80
+ cancelEvents.forEach((event) => el.removeEventListener(event, removeRipple));
81
+ ripple.remove();
82
+ }
83
+ function removeRipple() {
76
84
  ripple.style.opacity = '0';
77
- setTimeout(() => {
78
- ripple.remove();
79
- }, options.duration || 1000);
80
85
  cancelEvents.forEach((event) => el.removeEventListener(event, removeRipple));
86
+ timeoutId = setTimeout(() => {
87
+ ripple.remove();
88
+ activeRipples.delete(cleanup);
89
+ }, resolveDuration(options) || 1000);
81
90
  }
91
+ activeRipples.add(cleanup);
82
92
  cancelEvents.forEach((event) => el.addEventListener(event, removeRipple, { passive: true }));
83
93
  }
84
94
  triggerEvents.forEach((event) => el.addEventListener(event, createRipple, { passive: event === 'touchstart' }));
@@ -87,6 +97,9 @@ export function ripple(el, options = {}) {
87
97
  triggerEvents.forEach((event) => {
88
98
  el.removeEventListener(event, createRipple);
89
99
  });
100
+ activeRipples.forEach((cleanup) => cleanup());
101
+ activeRipples.clear();
102
+ rippleContainer.remove();
90
103
  },
91
104
  update(newOptions) {
92
105
  options = newOptions;
@@ -10,6 +10,7 @@
10
10
  .kit-application {
11
11
  color-scheme: light;
12
12
 
13
+ /* Hue vars — used by components for dynamic semantic color calculations */
13
14
  --kit-h-neutral: 220;
14
15
  --kit-h-success: 145;
15
16
  --kit-h-warning: 35;
@@ -28,19 +29,135 @@
28
29
 
29
30
  --kit-accent: hsl(220 90% 56%);
30
31
 
32
+ --kit-focus: hsl(35, 90%, 56%);
33
+
34
+ /* Layout */
31
35
  --kit-radius-1: 8px;
32
36
  --kit-radius-2: 12px;
33
37
  --kit-space-1: 6px;
34
38
  --kit-space-2: 10px;
35
39
  --kit-space-3: 14px;
36
-
37
- --kit-focus: hsl(35, 90%, 56%);
38
-
39
40
  --kit-disabled-opacity: 0.55;
40
-
41
41
  --kit-font:
42
42
  ui-sans-serif, system-ui, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji',
43
43
  'Segoe UI Symbol', 'Noto Color Emoji';
44
+
45
+ /* ─── Color tokens (light mode defaults) ─────────────────────────── */
46
+
47
+ /* Backgrounds */
48
+ --kit-color-bg: hsl(0 0% 100%);
49
+ --kit-color-bg-secondary: hsl(240 15% 96.5%);
50
+ --kit-color-bg-tertiary: hsl(240 10% 93%);
51
+
52
+ /* Surfaces — elevated layers (cards, sheets, popovers) */
53
+ --kit-color-surface: hsl(0 0% 100%);
54
+ --kit-color-surface-raised: hsl(0 0% 100%);
55
+
56
+ /* Labels — text hierarchy */
57
+ --kit-color-label: hsl(222 20% 10%);
58
+ --kit-color-label-secondary: hsl(220 10% 40%);
59
+ --kit-color-label-tertiary: hsl(220 8% 58%);
60
+ --kit-color-label-quaternary: hsl(220 5% 76%);
61
+
62
+ /* Fills — control & input backgrounds */
63
+ --kit-color-fill: hsl(240 4% 91%);
64
+ --kit-color-fill-secondary: hsl(240 8% 93%);
65
+ --kit-color-fill-tertiary: hsl(240 11% 95%);
66
+
67
+ /* Separator */
68
+ --kit-color-separator: hsl(220 16% 88%);
69
+
70
+ /* Semantic */
71
+ --kit-color-accent: hsl(220 90% 56%);
72
+ --kit-color-success: hsl(145 50% 38%);
73
+ --kit-color-warning: hsl(35 80% 45%);
74
+ --kit-color-danger: hsl(5 65% 48%);
75
+ --kit-color-info: hsl(205 60% 42%);
76
+ --kit-color-focus: hsl(35 90% 56%);
77
+
78
+ /* ─── Backward-compat aliases (removed once components are updated) ─ */
79
+ /* --kit-bg: var(--kit-color-bg);
80
+ --kit-fg: var(--kit-color-label);
81
+ --kit-muted: var(--kit-color-label-tertiary);
82
+ --kit-surface-1: var(--kit-color-surface);
83
+ --kit-surface-2: var(--kit-color-bg-secondary);
84
+ --kit-surface-3: var(--kit-color-bg-tertiary);
85
+ --kit-border: var(--kit-color-separator);
86
+ --kit-accent: var(--kit-color-accent);
87
+ --kit-focus: var(--kit-color-focus); */
88
+ }
89
+
90
+ .kit-application {
91
+ --kit-shape-none: 0;
92
+ --kit-shape-xs: 4px;
93
+ --kit-shape-sm: 6px;
94
+ --kit-shape-md: 10px;
95
+ --kit-shape-lg: 14px;
96
+ --kit-shape-xl: 18px;
97
+
98
+ --kit-space-compact: 6px;
99
+ --kit-space-default: 10px;
100
+ --kit-space-comfortable: 14px;
101
+ }
102
+
103
+ /* Light — explicit override (same values as defaults, forces color-scheme) */
104
+ .light {
105
+ color-scheme: light;
106
+
107
+ --kit-color-bg: hsl(0 0% 100%);
108
+ --kit-color-bg-secondary: hsl(240 15% 96.5%);
109
+ --kit-color-bg-tertiary: hsl(240 10% 93%);
110
+
111
+ --kit-color-surface: hsl(0 0% 100%);
112
+ --kit-color-surface-raised: hsl(0 0% 100%);
113
+
114
+ --kit-color-label: hsl(222 20% 10%);
115
+ --kit-color-label-secondary: hsl(220 10% 40%);
116
+ --kit-color-label-tertiary: hsl(220 8% 58%);
117
+ --kit-color-label-quaternary: hsl(220 5% 76%);
118
+
119
+ --kit-color-fill: hsl(240 4% 91%);
120
+ --kit-color-fill-secondary: hsl(240 8% 93%);
121
+ --kit-color-fill-tertiary: hsl(240 11% 95%);
122
+
123
+ --kit-color-separator: hsl(220 16% 88%);
124
+
125
+ --kit-color-accent: hsl(220 90% 56%);
126
+ --kit-color-success: hsl(145 50% 38%);
127
+ --kit-color-warning: hsl(35 80% 45%);
128
+ --kit-color-danger: hsl(5 65% 48%);
129
+ --kit-color-info: hsl(205 60% 42%);
130
+ --kit-color-focus: hsl(35 90% 56%);
131
+ }
132
+
133
+ /* Dark */
134
+ .dark {
135
+ color-scheme: dark;
136
+
137
+ --kit-color-bg: hsl(240 3% 11%);
138
+ --kit-color-bg-secondary: hsl(240 2% 17.5%);
139
+ --kit-color-bg-tertiary: hsl(240 1.5% 23%);
140
+
141
+ --kit-color-surface: hsl(240 2% 19%);
142
+ --kit-color-surface-raised: hsl(240 1.5% 25%);
143
+
144
+ --kit-color-label: hsl(0 0% 100%);
145
+ --kit-color-label-secondary: hsl(240 3% 56%);
146
+ --kit-color-label-tertiary: hsl(240 2.5% 38%);
147
+ --kit-color-label-quaternary: hsl(240 1.5% 25%);
148
+
149
+ --kit-color-fill: hsl(240 2.5% 28%);
150
+ --kit-color-fill-secondary: hsl(240 2.5% 24%);
151
+ --kit-color-fill-tertiary: hsl(240 2% 20%);
152
+
153
+ --kit-color-separator: hsl(240 2% 26%);
154
+
155
+ --kit-color-accent: hsl(220 85% 65%);
156
+ --kit-color-success: hsl(145 50% 50%);
157
+ --kit-color-warning: hsl(35 85% 58%);
158
+ --kit-color-danger: hsl(5 70% 60%);
159
+ --kit-color-info: hsl(205 65% 58%);
160
+ --kit-color-focus: hsl(35 90% 62%);
44
161
  }
45
162
 
46
163
  :global(.outline) {
@@ -59,13 +176,10 @@
59
176
  position: absolute;
60
177
  border-radius: 50%;
61
178
  pointer-events: none;
62
- -webkit-transition: 0.6s;
63
179
  transition: 0.6s;
64
- -webkit-animation: animation-l-ripple var(--system-animation-ripple-duration, 0.4s)
65
- cubic-bezier(0.4, 0, 0.2, 1);
66
180
  animation: animation-l-ripple var(--system-animation-ripple-duration, 0.4s)
67
181
  cubic-bezier(0.4, 0, 0.2, 1);
68
- border-radius: var(--system-ripple-radius);
182
+ border-radius: 50%;
69
183
  }
70
184
 
71
185
  :global(.kit-ripple--center) {
@@ -1,5 +1,5 @@
1
1
  <script lang="ts">
2
- import { useClassName, useStyles } from '../../utils';
2
+ import { useClassName, useIsInteractive, useStyles } from '../../utils';
3
3
  import { makeComponentProps } from '../../html-mapped';
4
4
  import { ripple } from '../../animations';
5
5
  import type { CardProps } from './card.types.js';
@@ -21,45 +21,20 @@
21
21
  active = false,
22
22
  disabled = false,
23
23
  noRipple,
24
+ color,
25
+ background,
24
26
  ...rest
25
27
  }: CardProps = $props();
26
28
 
27
- let safeVariant = $derived(
28
- variant === 'filled' || variant === 'outline' || variant === 'text' ? variant : 'filled'
29
- );
30
-
31
- let safeDensity = $derived(
32
- density === 'compact' || density === 'default' || density === 'comfortable'
33
- ? density
34
- : 'default'
35
- );
36
-
37
- let safeIs = $derived(
38
- is === 'div' ||
39
- is === 'article' ||
40
- is === 'section' ||
41
- is === 'aside' ||
42
- is === 'a' ||
43
- is === 'button'
44
- ? is
45
- : 'article'
46
- );
47
- let tag = $derived((href && 'a') || safeIs || 'article');
48
- let hasEventHandler = $derived(
49
- typeof rest.onclick === 'function' ||
50
- typeof rest.onpointerdown === 'function' ||
51
- typeof rest.onkeydown === 'function'
52
- );
53
- let isInteractive = $derived(interactive || tag === 'a' || tag === 'button' || hasEventHandler);
29
+ let tag = $derived((href && 'a') || is);
30
+ let isInteractive = $derived(useIsInteractive(rest, tag, ['a', 'button'], interactive));
54
31
  let isDisabled = $derived(!!disabled);
55
32
  let resolvedHref = $derived(tag === 'a' && !isDisabled ? href : undefined);
56
33
  let resolvedType = $derived(tag === 'button' ? (type ?? 'button') : undefined);
57
34
  let resolvedDisabled = $derived(tag === 'button' ? isDisabled : undefined);
58
35
  let resolvedTabIndex = $derived(tag === 'a' && isDisabled ? -1 : undefined);
59
36
 
60
- let { classProps, styleProps, restProps } = $derived(
61
- makeComponentProps(rest as Record<string, unknown>)
62
- );
37
+ let { classProps, styleProps, restProps } = $derived(makeComponentProps(rest));
63
38
 
64
39
  let componentClass = $derived(
65
40
  useClassName({
@@ -88,8 +63,8 @@
88
63
  type={resolvedType}
89
64
  disabled={resolvedDisabled}
90
65
  tabindex={resolvedTabIndex}
91
- data-variant={safeVariant}
92
- data-density={safeDensity}
66
+ data-variant={variant}
67
+ data-density={density}
93
68
  data-rounded={rounded}
94
69
  data-interactive={isInteractive}
95
70
  data-active={active}
@@ -99,9 +74,11 @@
99
74
  component: 'card',
100
75
  disabled: noRipple || disabled || !isInteractive
101
76
  }}
77
+ style:--kit-card-fg={color && `var(--kit-color-${color})`}
78
+ style:--kit-card-bg={background && `var(--kit-color-${background})`}
102
79
  {...restProps}
103
80
  >
104
- {#if safeVariant === 'outline'}
81
+ {#if variant === 'outline'}
105
82
  <span class="outline"></span>
106
83
  {/if}
107
84
 
@@ -110,25 +87,15 @@
110
87
 
111
88
  <style>
112
89
  .kit-card {
113
- --kit-card-bg: var(--kit-surface-2);
114
- --kit-card-fg: var(--kit-fg);
115
- --kit-card-bd: var(--kit-border);
116
- --kit-card-radius: var(--kit-radius-2);
117
- --kit-card-padding-compact: var(--kit-space-1);
118
- --kit-card-padding-default: var(--kit-space-2);
119
- --kit-card-padding-comfortable: var(--kit-space-3);
120
- --kit-card-hover-bg: color-mix(in oklab, var(--kit-card-bg), var(--kit-card-fg) 6%);
121
- --kit-card-active-bg: color-mix(in oklab, var(--kit-card-bg), var(--kit-card-fg) 10%);
122
-
123
90
  display: flex;
91
+ position: relative;
124
92
  flex-direction: column;
125
- gap: var(--kit-space-2);
93
+ gap: var(--kit-card-gap);
126
94
  background: var(--kit-card-bg);
127
95
  color: var(--kit-card-fg);
128
- border: 0;
129
96
  border-radius: var(--kit-card-radius);
97
+ border: 0;
130
98
  box-sizing: border-box;
131
- position: relative;
132
99
  transition:
133
100
  background 140ms ease,
134
101
  color 140ms ease,
@@ -148,53 +115,72 @@
148
115
  text-align: inherit;
149
116
  }
150
117
 
118
+ /**
119
+ * rounded
120
+ * @link ...
121
+ */
151
122
  .kit-card[data-rounded='0'] {
152
- --kit-card-radius: 0;
123
+ --kit-card-radius: var(--kit-shape-none);
153
124
  }
154
125
  .kit-card[data-rounded='xs'] {
155
- --kit-card-radius: 4px;
126
+ --kit-card-radius: var(--kit-shape-xs);
156
127
  }
157
128
  .kit-card[data-rounded='sm'] {
158
- --kit-card-radius: 6px;
129
+ --kit-card-radius: var(--kit-shape-sm);
159
130
  }
160
131
  .kit-card[data-rounded='md'] {
161
- --kit-card-radius: 10px;
132
+ --kit-card-radius: var(--kit-shape-md);
162
133
  }
163
134
  .kit-card[data-rounded='lg'] {
164
- --kit-card-radius: 14px;
135
+ --kit-card-radius: var(--kit-shape-lg);
165
136
  }
166
137
  .kit-card[data-rounded='xl'] {
167
- --kit-card-radius: 18px;
138
+ --kit-card-radius: var(--kit-shape-xl);
168
139
  }
169
140
 
141
+ /**
142
+ * density
143
+ * @link https://lapikit.dev/docs/components/card#density
144
+ */
145
+ .kit-card[data-density='none'] {
146
+ padding: 0;
147
+ }
170
148
  .kit-card[data-density='compact'] {
171
- padding: var(--kit-card-padding-compact);
149
+ --kit-card-gap: var(--kit-space-compact);
150
+ padding: var(--kit-space-compact);
172
151
  }
173
152
  .kit-card[data-density='default'] {
174
- padding: var(--kit-card-padding-default);
153
+ --kit-card-gap: var(--kit-space-default);
154
+ padding: var(--kit-space-default);
175
155
  }
176
156
  .kit-card[data-density='comfortable'] {
177
- padding: var(--kit-card-padding-comfortable);
157
+ --kit-card-gap: var(--kit-space-comfortable);
158
+ padding: var(--kit-space-comfortable);
178
159
  }
179
160
 
161
+ /**
162
+ * variant
163
+ * @link https://lapikit.dev/docs/components/card#variants
164
+ */
180
165
  .kit-card[data-variant='filled'] {
181
- --kit-card-bg: var(--kit-accent);
182
- --kit-card-fg: white;
166
+ --kit-card-bg: var(--kit-color-surface);
167
+ --kit-card-fg: var(--kit-color-label);
168
+
183
169
  --kit-card-hover-bg: color-mix(in oklab, var(--kit-card-bg), black 10%);
184
170
  --kit-card-active-bg: color-mix(in oklab, var(--kit-card-bg), black 16%);
185
171
  }
186
-
187
172
  .kit-card[data-variant='outline'] {
188
173
  --kit-card-bg: transparent;
189
- --kit-card-fg: var(--kit-accent);
190
- --kit-card-bd: var(--kit-accent);
174
+ --kit-card-fg: var(--kit-color-label);
175
+ --kit-card-bd: var(--kit-card-fg);
176
+
191
177
  --kit-card-hover-bg: color-mix(in oklab, var(--kit-card-fg), transparent 80%);
192
178
  --kit-card-active-bg: color-mix(in oklab, var(--kit-card-fg), transparent 92%);
193
179
  }
194
-
195
180
  .kit-card[data-variant='text'] {
196
181
  --kit-card-bg: transparent;
197
- --kit-card-fg: var(--kit-accent);
182
+ --kit-card-fg: var(--kit-color-label);
183
+
198
184
  --kit-card-hover-bg: color-mix(in oklab, var(--kit-card-fg), transparent 80%);
199
185
  --kit-card-active-bg: color-mix(in oklab, var(--kit-card-fg), transparent 92%);
200
186
  }
@@ -238,37 +224,4 @@
238
224
  .kit-card .outline {
239
225
  --outline-color: var(--kit-card-bd);
240
226
  }
241
-
242
- .kit-card__media {
243
- overflow: hidden;
244
- border-radius: calc(var(--kit-card-radius) - 2px);
245
- }
246
-
247
- .kit-card__header {
248
- font-weight: 600;
249
- line-height: 1.3;
250
- }
251
-
252
- .kit-card__body {
253
- display: block;
254
- line-height: 1.45;
255
- }
256
-
257
- .kit-card__footer {
258
- display: flex;
259
- align-items: center;
260
- justify-content: space-between;
261
- gap: var(--kit-space-2);
262
- flex-wrap: wrap;
263
- }
264
-
265
- .kit-card__footer-content {
266
- color: var(--kit-muted);
267
- }
268
-
269
- .kit-card__actions {
270
- display: inline-flex;
271
- align-items: center;
272
- gap: var(--kit-space-1);
273
- }
274
227
  </style>
@@ -1,14 +1,34 @@
1
- import type { Component, RoundedType } from '../../@types';
1
+ import type { Component, DensityType, RoundedType } from '../../@types';
2
2
  export interface CardProps extends Component {
3
3
  ref?: HTMLElement | null;
4
4
  is?: 'div' | 'article' | 'section' | 'aside' | 'a' | 'button';
5
5
  href?: string;
6
6
  type?: 'button' | 'submit' | 'reset';
7
7
  variant?: 'filled' | 'outline' | 'text';
8
- density?: 'compact' | 'default' | 'comfortable';
8
+ density?: DensityType;
9
9
  rounded?: RoundedType;
10
10
  interactive?: boolean;
11
11
  active?: boolean;
12
12
  disabled?: boolean;
13
13
  noRipple?: boolean;
14
14
  }
15
+ export interface CardTitleProps extends Component {
16
+ ref?: HTMLElement | null;
17
+ is?: 'div' | 'p' | 'span' | 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6';
18
+ }
19
+ export interface CardContentProps extends Component {
20
+ ref?: HTMLElement | null;
21
+ is?: 'div' | 'p' | 'span' | 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6';
22
+ }
23
+ export interface CardMediaProps extends Component {
24
+ ref?: HTMLElement | null;
25
+ is?: 'div' | 'span';
26
+ }
27
+ export interface CardContainerProps extends Component {
28
+ ref?: HTMLElement | null;
29
+ is?: 'div' | 'span';
30
+ }
31
+ export interface CardActionsProps extends Component {
32
+ ref?: HTMLElement | null;
33
+ is?: 'div' | 'span';
34
+ }
@@ -0,0 +1,53 @@
1
+ <script lang="ts">
2
+ import type { CardActionsProps } from '../card.types';
3
+ import { makeComponentProps } from '../../../html-mapped';
4
+ import { useClassName, useStyles } from '../../../utils';
5
+
6
+ let {
7
+ ref = $bindable(),
8
+ is = 'div',
9
+ children,
10
+ class: className = '',
11
+ style: styleAttr = '',
12
+ 's-class': sClass,
13
+ 's-style': sStyle,
14
+ ...rest
15
+ }: CardActionsProps = $props();
16
+
17
+ let { classProps, styleProps, restProps } = $derived(makeComponentProps(rest));
18
+
19
+ let componentClass = $derived(
20
+ useClassName({
21
+ baseClass: 'kit-card-actions',
22
+ className: `${className ?? ''}`.trim(),
23
+ sClass,
24
+ classProps
25
+ })
26
+ );
27
+
28
+ let componentStyle = $derived(
29
+ useStyles({
30
+ styleAttr,
31
+ sStyle,
32
+ styleProps
33
+ })
34
+ );
35
+ </script>
36
+
37
+ <svelte:element
38
+ this={is}
39
+ bind:this={ref}
40
+ class={componentClass}
41
+ style={componentStyle}
42
+ {...restProps}
43
+ >
44
+ {@render children?.()}
45
+ </svelte:element>
46
+
47
+ <style>
48
+ .kit-card-actions {
49
+ display: inline-flex;
50
+ align-items: center;
51
+ gap: var(--kit-space-1);
52
+ }
53
+ </style>
@@ -0,0 +1,4 @@
1
+ import type { CardActionsProps } from '../card.types';
2
+ declare const CardActions: import("svelte").Component<CardActionsProps, {}, "ref">;
3
+ type CardActions = ReturnType<typeof CardActions>;
4
+ export default CardActions;
@@ -0,0 +1,51 @@
1
+ <script lang="ts">
2
+ import type { CardContainerProps } from '../card.types';
3
+ import { makeComponentProps } from '../../../html-mapped';
4
+ import { useClassName, useStyles } from '../../../utils';
5
+
6
+ let {
7
+ ref = $bindable(),
8
+ is = 'div',
9
+ children,
10
+ class: className = '',
11
+ style: styleAttr = '',
12
+ 's-class': sClass,
13
+ 's-style': sStyle,
14
+ ...rest
15
+ }: CardContainerProps = $props();
16
+
17
+ let { classProps, styleProps, restProps } = $derived(makeComponentProps(rest));
18
+
19
+ let componentClass = $derived(
20
+ useClassName({
21
+ baseClass: 'kit-card-container',
22
+ className: `${className ?? ''}`.trim(),
23
+ sClass,
24
+ classProps
25
+ })
26
+ );
27
+
28
+ let componentStyle = $derived(
29
+ useStyles({
30
+ styleAttr,
31
+ sStyle,
32
+ styleProps
33
+ })
34
+ );
35
+ </script>
36
+
37
+ <svelte:element
38
+ this={is}
39
+ bind:this={ref}
40
+ class={componentClass}
41
+ style={componentStyle}
42
+ {...restProps}
43
+ >
44
+ {@render children?.()}
45
+ </svelte:element>
46
+
47
+ <style>
48
+ .kit-card-container {
49
+ gap: var(--kit-card-gap);
50
+ }
51
+ </style>
@@ -0,0 +1,4 @@
1
+ import type { CardContainerProps } from '../card.types';
2
+ declare const CardContainer: import("svelte").Component<CardContainerProps, {}, "ref">;
3
+ type CardContainer = ReturnType<typeof CardContainer>;
4
+ export default CardContainer;
@@ -0,0 +1,56 @@
1
+ <script lang="ts">
2
+ import type { CardContentProps } from '../card.types';
3
+ import { makeComponentProps } from '../../../html-mapped';
4
+ import { useClassName, useStyles } from '../../../utils';
5
+
6
+ let {
7
+ ref = $bindable(),
8
+ is = 'div',
9
+ children,
10
+ class: className = '',
11
+ style: styleAttr = '',
12
+ 's-class': sClass,
13
+ 's-style': sStyle,
14
+ ...rest
15
+ }: CardContentProps = $props();
16
+
17
+ let { classProps, styleProps, restProps } = $derived(makeComponentProps(rest));
18
+
19
+ let componentClass = $derived(
20
+ useClassName({
21
+ baseClass: 'kit-card-content',
22
+ className: `${className ?? ''}`.trim(),
23
+ sClass,
24
+ classProps
25
+ })
26
+ );
27
+
28
+ let componentStyle = $derived(
29
+ useStyles({
30
+ styleAttr,
31
+ sStyle,
32
+ styleProps
33
+ })
34
+ );
35
+ </script>
36
+
37
+ <svelte:element
38
+ this={is}
39
+ bind:this={ref}
40
+ class={componentClass}
41
+ style={componentStyle}
42
+ {...restProps}
43
+ >
44
+ {@render children?.()}
45
+ </svelte:element>
46
+
47
+ <style>
48
+ .kit-card-content {
49
+ display: block;
50
+ line-height: 1.45;
51
+ }
52
+
53
+ .kit-card-content > :global(*) {
54
+ margin: 0;
55
+ }
56
+ </style>
@@ -0,0 +1,4 @@
1
+ import type { CardContentProps } from '../card.types';
2
+ declare const CardContent: import("svelte").Component<CardContentProps, {}, "ref">;
3
+ type CardContent = ReturnType<typeof CardContent>;
4
+ export default CardContent;
@@ -0,0 +1,52 @@
1
+ <script lang="ts">
2
+ import type { CardMediaProps } from '../card.types';
3
+ import { makeComponentProps } from '../../../html-mapped';
4
+ import { useClassName, useStyles } from '../../../utils';
5
+
6
+ let {
7
+ ref = $bindable(),
8
+ is = 'div',
9
+ children,
10
+ class: className = '',
11
+ style: styleAttr = '',
12
+ 's-class': sClass,
13
+ 's-style': sStyle,
14
+ ...rest
15
+ }: CardMediaProps = $props();
16
+
17
+ let { classProps, styleProps, restProps } = $derived(makeComponentProps(rest));
18
+
19
+ let componentClass = $derived(
20
+ useClassName({
21
+ baseClass: 'kit-card-media',
22
+ className: `${className ?? ''}`.trim(),
23
+ sClass,
24
+ classProps
25
+ })
26
+ );
27
+
28
+ let componentStyle = $derived(
29
+ useStyles({
30
+ styleAttr,
31
+ sStyle,
32
+ styleProps
33
+ })
34
+ );
35
+ </script>
36
+
37
+ <svelte:element
38
+ this={is}
39
+ bind:this={ref}
40
+ class={componentClass}
41
+ style={componentStyle}
42
+ {...restProps}
43
+ >
44
+ {@render children?.()}
45
+ </svelte:element>
46
+
47
+ <style>
48
+ .kit-card-media {
49
+ overflow: hidden;
50
+ border-radius: calc(var(--kit-card-radius) - 2px);
51
+ }
52
+ </style>
@@ -0,0 +1,4 @@
1
+ import type { CardMediaProps } from '../card.types';
2
+ declare const CardMedia: import("svelte").Component<CardMediaProps, {}, "ref">;
3
+ type CardMedia = ReturnType<typeof CardMedia>;
4
+ export default CardMedia;
@@ -0,0 +1,56 @@
1
+ <script lang="ts">
2
+ import type { CardTitleProps } from '../card.types';
3
+ import { makeComponentProps } from '../../../html-mapped';
4
+ import { useClassName, useStyles } from '../../../utils';
5
+
6
+ let {
7
+ ref = $bindable(),
8
+ is = 'div',
9
+ children,
10
+ class: className = '',
11
+ style: styleAttr = '',
12
+ 's-class': sClass,
13
+ 's-style': sStyle,
14
+ ...rest
15
+ }: CardTitleProps = $props();
16
+
17
+ let { classProps, styleProps, restProps } = $derived(makeComponentProps(rest));
18
+
19
+ let componentClass = $derived(
20
+ useClassName({
21
+ baseClass: 'kit-card-title',
22
+ className: `${className ?? ''}`.trim(),
23
+ sClass,
24
+ classProps
25
+ })
26
+ );
27
+
28
+ let componentStyle = $derived(
29
+ useStyles({
30
+ styleAttr,
31
+ sStyle,
32
+ styleProps
33
+ })
34
+ );
35
+ </script>
36
+
37
+ <svelte:element
38
+ this={is}
39
+ bind:this={ref}
40
+ class={componentClass}
41
+ style={componentStyle}
42
+ {...restProps}
43
+ >
44
+ {@render children?.()}
45
+ </svelte:element>
46
+
47
+ <style>
48
+ .kit-card-title {
49
+ font-weight: 600;
50
+ line-height: 1.3;
51
+ }
52
+
53
+ .kit-card-title > :global(*) {
54
+ margin: 0;
55
+ }
56
+ </style>
@@ -0,0 +1,4 @@
1
+ import type { CardTitleProps } from '../card.types';
2
+ declare const CardTitle: import("svelte").Component<CardTitleProps, {}, "ref">;
3
+ type CardTitle = ReturnType<typeof CardTitle>;
4
+ export default CardTitle;
@@ -6,6 +6,11 @@ export { default as KitAppbar } from './appbar/appbar.svelte';
6
6
  export { default as KitBtn } from './btn/btn.svelte';
7
7
  export { default as KitIcon } from './icon/icon.svelte';
8
8
  export { default as KitCard } from './card/card.svelte';
9
+ export { default as KitCardMedia } from './card/modules/card-media.svelte';
10
+ export { default as KitCardTitle } from './card/modules/card-title.svelte';
11
+ export { default as KitCardContent } from './card/modules/card-content.svelte';
12
+ export { default as KitCardActions } from './card/modules/card-actions.svelte';
13
+ export { default as KitCardContainer } from './card/modules/card-container.svelte';
9
14
  export { default as KitChip } from './chip/chip.svelte';
10
15
  export { default as KitAvatar } from './avatar/avatar.svelte';
11
16
  export { default as KitDropdown } from './dropdown/dropdown.svelte';
@@ -4,6 +4,11 @@ export { default as KitAppbar } from './appbar/appbar.svelte';
4
4
  export { default as KitBtn } from './btn/btn.svelte';
5
5
  export { default as KitIcon } from './icon/icon.svelte';
6
6
  export { default as KitCard } from './card/card.svelte';
7
+ export { default as KitCardMedia } from './card/modules/card-media.svelte';
8
+ export { default as KitCardTitle } from './card/modules/card-title.svelte';
9
+ export { default as KitCardContent } from './card/modules/card-content.svelte';
10
+ export { default as KitCardActions } from './card/modules/card-actions.svelte';
11
+ export { default as KitCardContainer } from './card/modules/card-container.svelte';
7
12
  export { default as KitChip } from './chip/chip.svelte';
8
13
  export { default as KitAvatar } from './avatar/avatar.svelte';
9
14
  export { default as KitDropdown } from './dropdown/dropdown.svelte';
package/dist/constants.js CHANGED
@@ -28,7 +28,12 @@ export const lapikitComponents = [
28
28
  'spacer',
29
29
  'separator',
30
30
  'chip',
31
- 'card'
31
+ 'card',
32
+ 'card-title',
33
+ 'card-content',
34
+ 'card-media',
35
+ 'card-actions',
36
+ 'card-container'
32
37
  ];
33
38
  export const lapikitLabsComponents = ['sheet'];
34
39
  export const lapikitPlugins = {
@@ -8,6 +8,7 @@ import type { useClassNameProps, useStylesProps } from '../@types';
8
8
  * @returns A computed class string.
9
9
  */
10
10
  export declare function useClassName({ baseClass, className, sClass, classProps }?: useClassNameProps): string;
11
+ export declare function useIsInteractive(props: Record<string, unknown>, tag: string, interactiveTags: readonly string[], interactive?: boolean): boolean;
11
12
  /**
12
13
  * useStyles - Utility to compute style declarations for a component (optimized pure function).
13
14
  * @param styleAttr - Inline style attribute as a string.
@@ -54,6 +54,22 @@ export function useClassName({ baseClass = '', className, sClass, classProps } =
54
54
  }
55
55
  return classes.filter(Boolean).join(' ');
56
56
  }
57
+ /**
58
+ * useIsInteractive - Utility to determine whether a component should behave as interactive:
59
+ * either explicitly flagged, rendered as an interactive tag (e.g. 'a', 'button'), or given
60
+ * one of the standard interactive event handlers (onclick, onpointerdown, onkeydown).
61
+ * @param props - The rest/spread props object of a component.
62
+ * @param tag - The resolved element tag the component renders as.
63
+ * @param interactiveTags - The tags considered interactive for this component (e.g. ['a', 'button']).
64
+ * @param interactive - Explicit interactive flag passed to the component.
65
+ * @returns True if the component should behave as interactive.
66
+ */
67
+ const interactiveEventKeys = ['onclick', 'onpointerdown', 'onkeydown'];
68
+ export function useIsInteractive(props, tag, interactiveTags, interactive = false) {
69
+ return (interactive ||
70
+ interactiveTags.includes(tag) ||
71
+ interactiveEventKeys.some((key) => typeof props[key] === 'function'));
72
+ }
57
73
  /**
58
74
  * useStyles - Utility to compute style declarations for a component (optimized pure function).
59
75
  * @param styleAttr - Inline style attribute as a string.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lapikit",
3
- "version": "0.6.0",
3
+ "version": "0.6.1",
4
4
  "license": "MIT",
5
5
  "publishConfig": {
6
6
  "access": "public"