@sproutsocial/seeds-react-card 1.1.53 → 1.1.54

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/dist/card.css ADDED
@@ -0,0 +1,152 @@
1
+ /**
2
+ * Seeds Card component classes
3
+ * Use these instead of writing out individual Tailwind utility classes.
4
+ *
5
+ * Requires @sproutsocial/seeds-react-theme/dist/theme-all.css imported for
6
+ * CSS variable definitions and dark mode support.
7
+ *
8
+ * Usage:
9
+ * <div class="seeds-card seeds-card-interactive" style="--seeds-card-hover-shadow: var(--shadow-low)">…</div>
10
+ *
11
+ * The per-elevation hover shadow is driven by the --seeds-card-hover-shadow
12
+ * custom property set inline by the component from its `elevation` prop.
13
+ *
14
+ * Rules live in `@layer components` so that consumer Tailwind utilities
15
+ * (e.g. mx-200, p-300, ml-300) win in the cascade and can override these
16
+ * component styles, rather than tying/beating them on specificity.
17
+ */
18
+
19
+ @layer components {
20
+ .seeds-card {
21
+ position: relative;
22
+ display: flex;
23
+ flex-direction: column;
24
+ box-sizing: border-box;
25
+ margin: 0;
26
+ background: var(--color-container-bg-base);
27
+ /* borderWidths[500] resolves to 1px; no --border-width var is emitted. */
28
+ border: 1px solid var(--color-container-border-base);
29
+ padding: var(--space-400);
30
+ border-radius: var(--radius-outer);
31
+ transition: box-shadow var(--duration-medium),
32
+ border var(--duration-medium);
33
+ }
34
+
35
+ /* Compositional cards (using CardHeader/Content/Footer) drop their own padding. */
36
+ .seeds-card-compositional {
37
+ padding: 0;
38
+ }
39
+
40
+ .seeds-card-interactive {
41
+ cursor: pointer;
42
+ }
43
+
44
+ .seeds-card-interactive:hover {
45
+ box-shadow: var(--seeds-card-hover-shadow);
46
+ }
47
+
48
+ /* focusRing mixin: button/checkbox cards focus directly; link cards show the
49
+ ring via :focus-within when their inner link is focused. The :focus-within
50
+ ring is scoped to link-role cards only (matching StyledCard's $isRoleLink
51
+ gating), so button/checkbox cards do not show the ring on descendant focus. */
52
+ .seeds-card:focus,
53
+ .seeds-card-link-role:focus-within {
54
+ box-shadow: 0 0 0 1px var(--color-button-primary-bg-base),
55
+ 0 0px 0px 4px
56
+ color-mix(
57
+ in srgb,
58
+ var(--color-button-primary-bg-base),
59
+ transparent 70%
60
+ );
61
+ outline: none;
62
+ }
63
+
64
+ .seeds-card::-moz-focus-inner {
65
+ border: 0;
66
+ }
67
+
68
+ /* The inner link resets its own focus styling; the card shows the ring. */
69
+ .seeds-card-link-role:focus-within .seeds-card-link:focus {
70
+ border: none;
71
+ box-shadow: none;
72
+ outline: none;
73
+ }
74
+
75
+ /* disabled mixin */
76
+ .seeds-card-disabled {
77
+ opacity: 0.4;
78
+ pointer-events: none;
79
+ }
80
+
81
+ .seeds-card-selected {
82
+ border: 1px solid var(--color-container-border-selected);
83
+ }
84
+
85
+ /* Sub-components ------------------------------------------------------ */
86
+
87
+ .seeds-card-content {
88
+ display: flex;
89
+ flex-direction: column;
90
+ padding: var(--space-400);
91
+ box-sizing: border-box;
92
+ }
93
+
94
+ .seeds-card-header {
95
+ display: flex;
96
+ flex-direction: row;
97
+ padding: var(--space-400);
98
+ box-sizing: border-box;
99
+ border-bottom: 1px solid var(--color-container-border-base);
100
+ border-top-left-radius: var(--radius-inner);
101
+ border-top-right-radius: var(--radius-inner);
102
+ }
103
+
104
+ .seeds-card-footer {
105
+ display: flex;
106
+ flex-direction: row;
107
+ padding: var(--space-400);
108
+ box-sizing: border-box;
109
+ border-top: 1px solid var(--color-container-border-base);
110
+ border-bottom-left-radius: var(--radius-inner);
111
+ border-bottom-right-radius: var(--radius-inner);
112
+ }
113
+
114
+ /* Link typography: font-family + bold + headline color + type scale 400. */
115
+ .seeds-card-link {
116
+ font-family: var(--font-family);
117
+ font-weight: var(--font-weight-bold);
118
+ color: var(--color-text-headline);
119
+ font-size: var(--font-size-400);
120
+ line-height: var(--line-height-400);
121
+ }
122
+
123
+ /* Selected-icon overlay (positioned top-right, fades in when selected). */
124
+ .seeds-card-selected-icon-wrapper {
125
+ display: flex;
126
+ align-items: center;
127
+ justify-content: center;
128
+ position: absolute;
129
+ top: -8px;
130
+ right: -8px;
131
+ }
132
+
133
+ .seeds-card-selected-icon {
134
+ border-radius: 50%;
135
+ background: var(--color-container-bg-base);
136
+ opacity: 0;
137
+ transition: opacity var(--duration-medium);
138
+ }
139
+
140
+ .seeds-card-selected-icon-visible {
141
+ opacity: 1;
142
+ }
143
+
144
+ /* Affordance icon slides right on card hover (mirrors StyledCardAffordance). */
145
+ .seeds-card-affordance {
146
+ transition: var(--duration-medium);
147
+ }
148
+
149
+ .seeds-card:hover .seeds-card-affordance {
150
+ transform: translateX(var(--space-200));
151
+ }
152
+ }
package/dist/esm/index.js CHANGED
@@ -1,5 +1,17 @@
1
1
  // src/Card.tsx
2
- import { useRef, useState } from "react";
2
+ import React6 from "react";
3
+
4
+ // src/CardHybrid.tsx
5
+ import React5 from "react";
6
+ import {
7
+ hasStyledProps as hasStyledProps2,
8
+ hasBorderProps as hasBorderProps2,
9
+ isStyledExtension as isStyledExtension2
10
+ } from "@sproutsocial/seeds-react-system-props";
11
+
12
+ // src/CardStyled.tsx
13
+ import React3 from "react";
14
+ import { mergeRefs } from "@sproutsocial/seeds-react-utilities";
3
15
 
4
16
  // src/styles.tsx
5
17
  import styled from "styled-components";
@@ -150,6 +162,9 @@ var StyledCardAffordance = styled(Icon)`
150
162
  transition: ${(p) => p.theme.duration.medium};
151
163
  `;
152
164
 
165
+ // src/useCardBehavior.ts
166
+ import { useRef, useState } from "react";
167
+
153
168
  // src/utils.ts
154
169
  import { createContext, useContext, useEffect } from "react";
155
170
  import { assertIsElement } from "@sproutsocial/seeds-react-utilities";
@@ -189,46 +204,151 @@ var onKeyDown = ({ e, href, onClick, ref, role }) => {
189
204
  }
190
205
  };
191
206
 
207
+ // src/useCardBehavior.ts
208
+ function useCardBehavior({
209
+ disabled: disabled2 = false,
210
+ href,
211
+ onClick,
212
+ role = "presentation",
213
+ selected
214
+ }) {
215
+ const [hasSubComponent, setHasSubComponent] = useState(false);
216
+ const containerRef = useRef(null);
217
+ const linkRef = useRef(null);
218
+ const isRoleLink = role === "link";
219
+ const isRolePresentation = role === "presentation";
220
+ const checkedConditions = role === "checkbox" ? selected : void 0;
221
+ const cardContext = {
222
+ setHasSubComponent,
223
+ href,
224
+ linkRef
225
+ };
226
+ const handleClickConditions = (e) => isRoleLink ? linkRef.current?.click() : onClick?.(e);
227
+ const handleKeyDown = (e) => onKeyDown({ e, href, onClick, ref: containerRef, role });
228
+ const shouldBeInteractive = !isRolePresentation && !disabled2;
229
+ const containerProps = {
230
+ tabIndex: isRoleLink || isRolePresentation || disabled2 ? -1 : 0,
231
+ role: isRoleLink ? void 0 : role,
232
+ onClick: shouldBeInteractive ? handleClickConditions : void 0,
233
+ onKeyDown: shouldBeInteractive ? handleKeyDown : void 0,
234
+ "aria-checked": checkedConditions,
235
+ "aria-disabled": disabled2 && disabled2
236
+ };
237
+ return {
238
+ containerRef,
239
+ linkRef,
240
+ cardContext,
241
+ hasSubComponent,
242
+ isRoleLink,
243
+ SubComponentContext,
244
+ containerProps
245
+ };
246
+ }
247
+
192
248
  // src/subComponents.tsx
193
249
  import { useContext as useContext2 } from "react";
250
+ import {
251
+ hasStyledProps,
252
+ hasBorderProps,
253
+ isStyledExtension
254
+ } from "@sproutsocial/seeds-react-system-props";
255
+ import Icon2 from "@sproutsocial/seeds-react-icon";
194
256
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
195
- var CardContent = ({
196
- children,
197
- ...rest
198
- }) => {
257
+ function cn(...inputs) {
258
+ const classes = [];
259
+ for (const input of inputs) {
260
+ if (!input) continue;
261
+ if (typeof input === "string") {
262
+ classes.push(input);
263
+ } else if (typeof input === "object") {
264
+ for (const [key, value] of Object.entries(input)) {
265
+ if (value) {
266
+ classes.push(key);
267
+ }
268
+ }
269
+ }
270
+ }
271
+ return classes.join(" ");
272
+ }
273
+ function needsStyled(props) {
274
+ return hasStyledProps(props) || hasBorderProps(props) || isStyledExtension(props);
275
+ }
276
+ var CardContent = (props) => {
199
277
  useChildContext();
200
- return /* @__PURE__ */ jsx(StyledCardContent, { ...rest, children });
278
+ const { children, className, ...rest } = props;
279
+ if (needsStyled(props)) {
280
+ return /* @__PURE__ */ jsx(StyledCardContent, { ...rest, className, children });
281
+ }
282
+ return /* @__PURE__ */ jsx(
283
+ "div",
284
+ {
285
+ ...rest,
286
+ className: cn("seeds-card-content", className),
287
+ children
288
+ }
289
+ );
201
290
  };
202
- var CardHeader = ({
203
- children,
204
- ...rest
205
- }) => {
291
+ var CardHeader = (props) => {
206
292
  useChildContext();
207
- return /* @__PURE__ */ jsx(StyledCardHeader, { ...rest, children });
293
+ const { children, className, ...rest } = props;
294
+ if (needsStyled(props)) {
295
+ return /* @__PURE__ */ jsx(StyledCardHeader, { ...rest, className, children });
296
+ }
297
+ return /* @__PURE__ */ jsx(
298
+ "div",
299
+ {
300
+ ...rest,
301
+ className: cn("seeds-card-header", className),
302
+ children
303
+ }
304
+ );
208
305
  };
209
- var CardFooter = ({
210
- children,
211
- ...rest
212
- }) => {
306
+ var CardFooter = (props) => {
213
307
  useChildContext();
214
- return /* @__PURE__ */ jsx(StyledCardFooter, { ...rest, children });
308
+ const { children, className, ...rest } = props;
309
+ if (needsStyled(props)) {
310
+ return /* @__PURE__ */ jsx(StyledCardFooter, { ...rest, className, children });
311
+ }
312
+ return /* @__PURE__ */ jsx(
313
+ "div",
314
+ {
315
+ ...rest,
316
+ className: cn("seeds-card-footer", className),
317
+ children
318
+ }
319
+ );
215
320
  };
216
321
  var SelectedIcon = ({ $selected }) => {
217
- return /* @__PURE__ */ jsx(SelectedIconWrapper, { children: /* @__PURE__ */ jsx(
218
- StyledSelectedIcon,
322
+ return /* @__PURE__ */ jsx("div", { className: "seeds-card-selected-icon-wrapper", children: /* @__PURE__ */ jsx(
323
+ Icon2,
219
324
  {
220
325
  "aria-hidden": true,
221
326
  color: "icon.base",
222
327
  name: "circle-check-solid",
223
- $selected
328
+ className: cn("seeds-card-selected-icon", {
329
+ "seeds-card-selected-icon-visible": !!$selected
330
+ })
224
331
  }
225
332
  ) });
226
333
  };
227
- var CardAffordance = ({ ...rest }) => {
334
+ var CardAffordance = ({ className, ...rest }) => {
335
+ if (needsStyled(rest) || isStyledExtension({ className })) {
336
+ return /* @__PURE__ */ jsx(
337
+ StyledCardAffordance,
338
+ {
339
+ ...rest,
340
+ className,
341
+ size: "mini",
342
+ name: "arrow-right-solid",
343
+ "aria-hidden": true
344
+ }
345
+ );
346
+ }
228
347
  return /* @__PURE__ */ jsx(
229
- StyledCardAffordance,
348
+ Icon2,
230
349
  {
231
350
  ...rest,
351
+ className: cn("seeds-card-affordance", className),
232
352
  size: "mini",
233
353
  name: "arrow-right-solid",
234
354
  "aria-hidden": true
@@ -240,79 +360,183 @@ var CardLink = ({
240
360
  children,
241
361
  external = false,
242
362
  color: color2,
363
+ className,
243
364
  ...rest
244
365
  }) => {
245
366
  const { href, linkRef } = useContext2(SubComponentContext);
246
367
  const handleClick = (e) => {
247
368
  e.stopPropagation();
248
369
  };
370
+ const sharedProps = {
371
+ target: external ? "_blank" : void 0,
372
+ rel: external ? "noreferrer" : void 0,
373
+ href,
374
+ onClick: handleClick
375
+ };
376
+ if (color2 !== void 0 || hasStyledProps(rest) || hasBorderProps(rest) || isStyledExtension({ ...rest, className })) {
377
+ return /* @__PURE__ */ jsx(
378
+ StyledCardLink,
379
+ {
380
+ ...rest,
381
+ ...sharedProps,
382
+ ref: linkRef,
383
+ className,
384
+ color: color2,
385
+ children: /* @__PURE__ */ jsxs(Fragment, { children: [
386
+ children,
387
+ affordance ? /* @__PURE__ */ jsx(CardAffordance, { ml: 300 }) : null
388
+ ] })
389
+ }
390
+ );
391
+ }
249
392
  return /* @__PURE__ */ jsx(
250
- StyledCardLink,
393
+ "a",
251
394
  {
252
395
  ...rest,
253
- target: external ? "_blank" : void 0,
254
- rel: external ? "noreferrer" : void 0,
255
- href,
256
- onClick: handleClick,
396
+ ...sharedProps,
257
397
  ref: linkRef,
258
- color: color2,
398
+ className: cn("seeds-card-link", className),
259
399
  children: /* @__PURE__ */ jsxs(Fragment, { children: [
260
400
  children,
261
- affordance ? /* @__PURE__ */ jsx(CardAffordance, { ml: 300 }) : null
401
+ affordance ? /* @__PURE__ */ jsx(CardAffordance, { className: "ml-300" }) : null
262
402
  ] })
263
403
  }
264
404
  );
265
405
  };
266
406
 
267
- // src/Card.tsx
407
+ // src/CardStyled.tsx
268
408
  import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
269
- var Card = ({
270
- children,
271
- disabled: disabled2 = false,
272
- elevation = "low",
273
- href,
274
- onClick,
275
- role = "presentation",
276
- selected,
277
- ...rest
278
- }) => {
279
- const [hasSubComponent, setHasSubComponent] = useState(false);
280
- const containerRef = useRef(null);
281
- const linkRef = useRef(null);
282
- const isRoleLink = role === "link";
283
- const isRolePresentation = role === "presentation";
284
- const checkedConditions = role === "checkbox" ? selected : void 0;
285
- const cardContext = {
286
- setHasSubComponent,
409
+ var CardStyled = React3.forwardRef(
410
+ ({
411
+ children,
412
+ disabled: disabled2 = false,
413
+ elevation = "low",
287
414
  href,
288
- linkRef
289
- };
290
- const handleClickConditions = (e) => isRoleLink ? linkRef.current?.click() : onClick?.(e);
291
- const handleKeyDown = (e) => onKeyDown({ e, href, onClick, ref: containerRef, role });
292
- const shouldBeInteractive = !isRolePresentation && !disabled2;
293
- return /* @__PURE__ */ jsxs2(
294
- StyledCard,
295
- {
296
- tabIndex: isRoleLink || isRolePresentation || disabled2 ? -1 : 0,
297
- role: isRoleLink ? void 0 : role,
298
- onClick: shouldBeInteractive ? handleClickConditions : void 0,
299
- onKeyDown: shouldBeInteractive ? handleKeyDown : void 0,
300
- $elevation: elevation,
301
- ref: containerRef,
302
- $selected: selected,
303
- "aria-checked": checkedConditions,
304
- $disabled: disabled2,
305
- "aria-disabled": disabled2 && disabled2,
306
- $compositionalComponents: hasSubComponent,
307
- $isRoleLink: isRoleLink,
308
- ...rest,
309
- children: [
310
- /* @__PURE__ */ jsx2(SelectedIcon, { $selected: selected }),
311
- /* @__PURE__ */ jsx2(SubComponentContext.Provider, { value: cardContext, children })
312
- ]
415
+ onClick,
416
+ role = "presentation",
417
+ selected,
418
+ ...rest
419
+ }, ref) => {
420
+ const {
421
+ containerRef,
422
+ cardContext,
423
+ hasSubComponent,
424
+ isRoleLink,
425
+ SubComponentContext: SubComponentContext2,
426
+ containerProps
427
+ } = useCardBehavior({ disabled: disabled2, href, onClick, role, selected });
428
+ return /* @__PURE__ */ jsxs2(
429
+ StyledCard,
430
+ {
431
+ ...containerProps,
432
+ $elevation: elevation,
433
+ ref: mergeRefs(containerRef, ref),
434
+ $selected: selected,
435
+ $disabled: disabled2,
436
+ $compositionalComponents: hasSubComponent,
437
+ $isRoleLink: isRoleLink,
438
+ ...rest,
439
+ children: [
440
+ /* @__PURE__ */ jsx2(SelectedIcon, { $selected: selected }),
441
+ /* @__PURE__ */ jsx2(SubComponentContext2.Provider, { value: cardContext, children })
442
+ ]
443
+ }
444
+ );
445
+ }
446
+ );
447
+ CardStyled.displayName = "Card";
448
+ var CardStyled_default = CardStyled;
449
+
450
+ // src/CardTailwind.tsx
451
+ import React4 from "react";
452
+ import { mergeRefs as mergeRefs2 } from "@sproutsocial/seeds-react-utilities";
453
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
454
+ function cn2(...inputs) {
455
+ const classes = [];
456
+ for (const input of inputs) {
457
+ if (!input) continue;
458
+ if (typeof input === "string") {
459
+ classes.push(input);
460
+ } else if (typeof input === "object") {
461
+ for (const [key, value] of Object.entries(input)) {
462
+ if (value) {
463
+ classes.push(key);
464
+ }
465
+ }
313
466
  }
314
- );
315
- };
467
+ }
468
+ return classes.join(" ");
469
+ }
470
+ var CardTailwind = React4.forwardRef(
471
+ ({
472
+ children,
473
+ className,
474
+ disabled: disabled2 = false,
475
+ elevation = "low",
476
+ href,
477
+ onClick,
478
+ role = "presentation",
479
+ selected,
480
+ ...rest
481
+ }, ref) => {
482
+ const {
483
+ containerRef,
484
+ cardContext,
485
+ hasSubComponent,
486
+ isRoleLink,
487
+ SubComponentContext: SubComponentContext2,
488
+ containerProps
489
+ } = useCardBehavior({ disabled: disabled2, href, onClick, role, selected });
490
+ const classes = cn2(
491
+ "seeds-card",
492
+ {
493
+ "seeds-card-interactive": isRoleLink || role === "button" || role === "checkbox",
494
+ "seeds-card-link-role": isRoleLink
495
+ },
496
+ {
497
+ "seeds-card-selected": !!selected,
498
+ "seeds-card-disabled": disabled2,
499
+ "seeds-card-compositional": hasSubComponent
500
+ },
501
+ className
502
+ );
503
+ return /* @__PURE__ */ jsxs3(
504
+ "div",
505
+ {
506
+ ...containerProps,
507
+ ...rest,
508
+ ref: mergeRefs2(containerRef, ref),
509
+ className: classes,
510
+ style: {
511
+ "--seeds-card-hover-shadow": `var(--shadow-${elevation})`
512
+ },
513
+ children: [
514
+ /* @__PURE__ */ jsx3(SelectedIcon, { $selected: selected }),
515
+ /* @__PURE__ */ jsx3(SubComponentContext2.Provider, { value: cardContext, children })
516
+ ]
517
+ }
518
+ );
519
+ }
520
+ );
521
+ CardTailwind.displayName = "CardTailwind";
522
+
523
+ // src/CardHybrid.tsx
524
+ import { jsx as jsx4 } from "react/jsx-runtime";
525
+ var CardHybrid = React5.forwardRef(
526
+ (props, ref) => {
527
+ if (hasStyledProps2(props) || hasBorderProps2(props) || isStyledExtension2(props)) {
528
+ return /* @__PURE__ */ jsx4(CardStyled_default, { ...props, ref });
529
+ }
530
+ return /* @__PURE__ */ jsx4(CardTailwind, { ...props, ref });
531
+ }
532
+ );
533
+ CardHybrid.displayName = "Card";
534
+ var CardHybrid_default = CardHybrid;
535
+
536
+ // src/Card.tsx
537
+ import { jsx as jsx5 } from "react/jsx-runtime";
538
+ var Card = React6.forwardRef((props, ref) => /* @__PURE__ */ jsx5(CardHybrid_default, { ...props, ref }));
539
+ Card.displayName = "Card";
316
540
  var Card_default = Card;
317
541
 
318
542
  // src/CardTypes.ts