sellmate-design-system-react 9.0.0-beta.28 → 9.0.0-beta.29

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/AGENTS.md CHANGED
@@ -27,7 +27,7 @@
27
27
  | 분류 | 컴포넌트 |
28
28
  | --- | --- |
29
29
  | **버튼·링크** | `SButton` `SGhostButton` `SDropdownButton` `SSystemActionButton` `STextLink` `SSwitch` `SToggle` |
30
- | **입력 (폼)** | `SForm` `SField` `SInput` `SSearchInput` `SNumberInput` `STextarea` `SEditor` `SSelect` `SCheckbox` `SRadio` `SRadioGroup` `SRadioButton` `SChip` `SChipInput` `SBarcodeInput` `SFilePicker` |
30
+ | **입력 (폼)** | `SForm` `SField` `SInput` `SSearchInput` `SNumberInput` `STextarea` `SEditor` `SSelect` `SCheckbox` `SRadio` `SRadioGroup` `SRadioButton` `SRadioCard` `SRadioCardGroup` `SChip` `SChipInput` `SBarcodeInput` `SFilePicker` |
31
31
  | **날짜·시간** | `SCalendar` `SDatePicker` `SDatePickerYearListbox` `SDatePickerMonthListbox` `SDateRangePicker` `STimePicker` `STimeRangePicker` |
32
32
  | **표·목록** | `STable` `STableBar` `SChipFilter` `SKeyValueTable` `SList` `SListItem` `SExpansionList` `SDraggableList` `SDraggableItem` `STree` |
33
33
  | **레이아웃** | `SLayout` `SGnb` `SPage`(제목 영역은 `header` prop) `SSectionHeaderCard` `SCard` `SDivider` `SSplitter` `SScrollArea` `SExpansionItem` |
@@ -152,7 +152,7 @@ Tailwind 유틸리티는 **토큰 스케일에 있는 값만** 사용한다.
152
152
  | --- | --- | --- |
153
153
  | **셸** | 앱 전체 뼈대. 페이지가 바뀌어도 남는다 | `SLayout` `SGnb` `SPage`(제목 영역은 `header` prop) |
154
154
  | **블록** | `SPage` 의 직계 자식. 페이지를 세로로 쌓는 단위 | `SSectionHeaderCard` `SCard` `SForm` `SSplitter` `SScrollArea` `STable` `STableBar` `SChipFilter` `SKeyValueTable` `SList` `SExpansionList` `SDraggableList` `STree` `SCallout` `STabs` `SStepper` `SPagination` `SDivider` |
155
- | **요소** | 블록 **안에** 놓이는 컨트롤. 혼자 페이지에 서지 않는다 | `SButton` `SGhostButton` `SDropdownButton` `SSystemActionButton` `SField` `SInput` `SSearchInput` `SNumberInput` `STextarea` `SEditor` `SSelect` `SCheckbox` `SRadio` `SRadioGroup` `SRadioButton` `SSwitch` `SToggle` `SChipInput` `SBarcodeInput` `SFilePicker` `SDatePicker` `SDatePickerYearListbox` `SDatePickerMonthListbox` `SDateRangePicker` `STimePicker` `STimeRangePicker` `SCalendar` `SListItem` `SExpansionItem` `SDraggableItem` `SImage` `SLinearProgress` `SCircleProgress` |
155
+ | **요소** | 블록 **안에** 놓이는 컨트롤. 혼자 페이지에 서지 않는다 | `SButton` `SGhostButton` `SDropdownButton` `SSystemActionButton` `SField` `SInput` `SSearchInput` `SNumberInput` `STextarea` `SEditor` `SSelect` `SCheckbox` `SRadio` `SRadioGroup` `SRadioButton` `SRadioCard` `SRadioCardGroup` `SSwitch` `SToggle` `SChipInput` `SBarcodeInput` `SFilePicker` `SDatePicker` `SDatePickerYearListbox` `SDatePickerMonthListbox` `SDateRangePicker` `STimePicker` `STimeRangePicker` `SCalendar` `SListItem` `SExpansionItem` `SDraggableItem` `SImage` `SLinearProgress` `SCircleProgress` |
156
156
  | **인라인** | 텍스트 흐름·셀·라벨 안에 섞인다. 혼자 블록이 되지 않는다 | `STag` `SBadge` `SIcon` `STextLink` `SChip` |
157
157
  | **레이어** | 문서 흐름 **밖**에 떠서 그려진다. 어느 층에서 띄우든 레이아웃에 영향이 없다 | `SModal` `SActionModal` `SConfirmModal` `SPopup` `SDrawer` `SPopover` `STooltip` `SPortal` `SToast` `SLoadingModal` `SLoadingContainer` `SGuide` |
158
158
 
@@ -300,6 +300,7 @@ Tailwind 유틸리티는 **토큰 스케일에 있는 값만** 사용한다.
300
300
 
301
301
  - **라디오는 `SRadioGroup` 을 쓴다.** `direction="horizontal" | "vertical"` 만 주면 간격을 알아서 맞춘다 — 직접 `flex` 로 감싸지 않는다.
302
302
  - `SRadioButton` 그룹의 간격은 `-1px`(테두리 겹침 처리)이라 손으로 만들지 않는다.
303
+ - **카드형 라디오는 `SRadioCardGroup` 을 쓴다.** 카드 사이 간격은 수평·수직이 같고 `SRadioGroup` 과 다르므로, 손으로 `flex gap-…` 을 맞추지 말고 `direction` 만 준다.
303
304
  - 정확한 값이 필요하면 토큰을 직접 참조해도 된다: `gap-[var(--cmp-checkbox-group-gap-horizontal)]`
304
305
 
305
306
  #### 섹션·패널 안쪽 여백
@@ -480,6 +481,8 @@ Tailwind 유틸리티는 **토큰 스케일에 있는 값만** 사용한다.
480
481
  | 목록에서 하나 고르게 한다 | `SSelect` | §3-7-2 |
481
482
  | 선택지를 항상 펼쳐 두고 하나 고르게 한다 | `SRadioGroup` | §3-7-2 |
482
483
  | 버튼 모양으로 모드를 하나 고르게 한다 | `SRadioButton` | §3-7-2 |
484
+ | 선택지마다 설명이 붙어 한 줄로는 안 된다 | `SRadioCardGroup` | §3-7-2 |
485
+ | 설명이 붙은 선택지 카드 하나를 직접 배치한다 | `SRadioCard` | §3-7-2 |
483
486
  | 라디오 하나를 표 셀 등에 직접 배치한다 | `SRadio` | §3-7-2 |
484
487
  | 여러 개를 고르게 한다 / 동의를 받는다 | `SCheckbox` | §3-7-3 |
485
488
  | 켜는 즉시 반영되는 설정을 준다 | `SSwitch` | §3-7-3 |
@@ -1267,7 +1270,7 @@ const columns: STableColumn[] = [
1267
1270
 
1268
1271
  `SSearchInput` 은 폼 필드가 아니다 — 라벨·힌트·유효성 규칙·에러 메시지를 받지 않고, `SForm` 의 제출 검증 대상에도 들어가지 않는다. 돋보기 아이콘이 항상 앞에 붙어 "여기는 검색"임을 스스로 밝히므로 라벨을 따로 붙이지 않는다. 검색 실행은 `onSearch`(Enter) 로 받고, 값이 바뀔 때마다 좁히는 실시간 필터라면 `onValueChange` 만 쓴다. 반대로 검색어를 **저장하거나 검증해야 한다면** 그것은 폼 값이므로 `SInput` 이다.
1269
1272
 
1270
- #### 3-7-2. 하나를 고르게 하는 다섯 — SSelect vs SRadioGroup vs SRadioButton vs STabs vs SRadio
1273
+ #### 3-7-2. 하나를 고르게 하는 여섯 — SSelect vs SRadioGroup vs SRadioCardGroup vs SRadioButton vs STabs vs SRadio
1271
1274
 
1272
1275
  **먼저 "고르면 무엇이 바뀌는가"를 본다.**
1273
1276
 
@@ -1278,10 +1281,14 @@ const columns: STableColumn[] = [
1278
1281
  | --- | --- |
1279
1282
  | 6개 이상, 또는 서버에서 오는 동적 목록 | `SSelect` |
1280
1283
  | 2~5개 고정 + 선택지를 항상 보여야 함 | `SRadioGroup` |
1284
+ | 2~5개 고정 + **선택지마다 설명 한 줄이 필요함** | `SRadioCardGroup` |
1281
1285
  | 2~4개 + 짧은 라벨의 배타적 모드 전환 (세그먼트) | `SRadioButton` |
1282
1286
 
1283
1287
  - **`SRadio` 를 직접 나열하지 않는다.** 그룹 간격은 `SRadioGroup` 이 맞춘다 (§2-2). `SRadio` 단독은 `SRadioGroup` 이 만들 수 없는 배치 — 표 셀 안에 행마다 하나씩 놓는 경우 — 에만 쓴다.
1284
1288
  - `SRadioButton` 은 `options` 를 통째로 받는 세그먼트 컨트롤이라 `SRadio` 를 여러 개 넣는 게 아니다.
1289
+ - **`SRadioGroup` 과 `SRadioCardGroup` 의 갈림길은 "설명이 필요한가" 하나다.** 라벨만으로 뜻이 통하면 `SRadioGroup`, 고르기 전에 한 줄 설명을 읽어야 하면 `SRadioCardGroup` 이다. 라벨 옆에 설명을 괄호로 덧붙여 늘이지 않는다.
1290
+ - `SRadioCard` 단독은 `SRadioCardGroup` 이 만들 수 없는 배치에만 쓴다 — 나열은 그룹이 맡는다 (§2-2).
1291
+ - 카드 안에 상태 배지를 넣으려면 `tag` 슬롯에 `STag` 를 준다. 라벨 문자열에 "(추천)" 처럼 섞어 쓰지 않는다.
1285
1292
 
1286
1293
  #### 3-7-3. 켜고 끄는 셋 — SCheckbox vs SSwitch vs SToggle
1287
1294
 
@@ -259,7 +259,10 @@ export interface SChipFilterFieldBase {
259
259
  /** 칩에 표시할 레이블 */
260
260
  label: string;
261
261
  /** 필수 필터 표시. true면 값이 비어 있을 때 기본값(defaultValue 또는 타입별 내장 기본값)이
262
- * 자동으로 채워지고, clearable은 현재 값이 기본값과 같을 땐 숨겨지며 클릭 시 기본값으로 되돌아간다 */
262
+ * 자동으로 채워지고, clearable은 현재 값이 기본값과 같을 땐 숨겨지며 클릭 시 기본값으로 되돌아간다.
263
+ * 값이 채워졌다가 다시 비면(예: value를 제어하는 부모가 onReset에서 자기 상태를 비우는 경우)
264
+ * 기본값이 한 번 더 채워진다 — required 필드가 빈 채로 남으면 onSearch가 막히기 때문이다.
265
+ * 단 그 값을 부모가 value로 받아 주지 않으면 반복해서 채우지는 않는다 */
263
266
  required?: boolean;
264
267
  /** 고정 필터. true면 activeKeys와 무관하게 항상 노출되고 "필터 추가" 목록에는 나타나지 않는다.
265
268
  * required도 같은 효과를 낸다 — 처음부터 바에 보이는 것은 fixed이거나 required인 필드뿐이고,
@@ -46,7 +46,10 @@ export interface SChipFilterFieldBase {
46
46
  /** 칩에 표시할 레이블 */
47
47
  label: string;
48
48
  /** 필수 필터 표시. true면 값이 비어 있을 때 기본값(defaultValue 또는 타입별 내장 기본값)이
49
- * 자동으로 채워지고, clearable은 현재 값이 기본값과 같을 땐 숨겨지며 클릭 시 기본값으로 되돌아간다 */
49
+ * 자동으로 채워지고, clearable은 현재 값이 기본값과 같을 땐 숨겨지며 클릭 시 기본값으로 되돌아간다.
50
+ * 값이 채워졌다가 다시 비면(예: value를 제어하는 부모가 onReset에서 자기 상태를 비우는 경우)
51
+ * 기본값이 한 번 더 채워진다 — required 필드가 빈 채로 남으면 onSearch가 막히기 때문이다.
52
+ * 단 그 값을 부모가 value로 받아 주지 않으면 반복해서 채우지는 않는다 */
50
53
  required?: boolean;
51
54
  /** 고정 필터. true면 activeKeys와 무관하게 항상 노출되고 "필터 추가" 목록에는 나타나지 않는다.
52
55
  * required도 같은 효과를 낸다 — 처음부터 바에 보이는 것은 fixed이거나 required인 필드뿐이고,
@@ -0,0 +1,78 @@
1
+ # SRadioCard
2
+
3
+ > 자동 생성 문서 — `npm run docs:gen`. 소스: 각 컴포넌트의 Props/Handle 인터페이스 + import 의존성.
4
+
5
+ ### SRadioCardGroup
6
+
7
+ #### Props
8
+
9
+ | Prop | Type | Default | Description |
10
+ |------|------|---------|-------------|
11
+ | `options?` | `SRadioCardOption[]` | `[]` | 옵션 목록 |
12
+ | `value?` | `SRadioValue \| null` | — | 현재 선택 값 |
13
+ | `direction?` | `'vertical' \| 'horizontal'` | `'vertical'` | 배치 방향 |
14
+ | `disabled?` | `boolean` | `false` | 전체 비활성 |
15
+ | `groupName?` | `string` | — | 라디오 name 속성 (form-agnostic 그룹핑) |
16
+ | `className?` | `string` | — | |
17
+ | `style?` | `CSSProperties` | — | |
18
+
19
+ #### Events
20
+
21
+ | Event | Type | Description |
22
+ |-------|------|-------------|
23
+ | `onValueChange` | `(val: SRadioValue) => void` | 선택 변경 (sdUpdate) |
24
+
25
+ ### SRadioCard
26
+
27
+ #### Props
28
+
29
+ | Prop | Type | Default | Description |
30
+ |------|------|---------|-------------|
31
+ | `val` | `SRadioValue` | — | 이 카드의 값 |
32
+ | `value?` | `SRadioValue \| null` | — | 그룹의 현재 선택 값 |
33
+ | `label` | `string` | — | 라벨 (첫 줄) |
34
+ | `subtitle?` | `string` | — | 서브타이틀 (둘째 줄). 라벨 왼쪽 끝에 맞춰 들여쓴다 |
35
+ | `tag?` | `Omit<STagProps, 'size'>` | — | 라벨 오른쪽에 붙일 태그. `STag` 의 props 를 그대로 받되 크기는 `xs` 로 고정된다 |
36
+ | `disabled?` | `boolean` | `false` | 비활성 |
37
+ | `name?` | `string` | — | native radio name (form-agnostic 그룹핑) |
38
+ | `className?` | `string` | — | |
39
+ | `style?` | `CSSProperties` | — | |
40
+
41
+ #### Events
42
+
43
+ | Event | Type | Description |
44
+ |-------|------|-------------|
45
+ | `onValueChange` | `(val: SRadioValue) => void` | 선택 변경 (sdUpdate) — 선택된 val 전달 |
46
+
47
+ ## Types
48
+
49
+ ### SRadioCardOption
50
+
51
+ ```ts
52
+ export interface SRadioCardOption {
53
+ /** 옵션 값 */
54
+ value: SRadioValue;
55
+ /** 라벨 (첫 줄) */
56
+ label: string;
57
+ /** 서브타이틀 (둘째 줄) */
58
+ subtitle?: string;
59
+ /** 라벨 오른쪽에 붙일 태그 (`STag` props, 크기는 `xs` 고정) */
60
+ tag?: Omit<STagProps, 'size'>;
61
+ /** 이 옵션만 비활성 */
62
+ disabled?: boolean;
63
+ }
64
+ ```
65
+
66
+ ## Dependencies
67
+
68
+ ### Depends on
69
+
70
+ - [STag](../STag)
71
+
72
+ ### Graph
73
+
74
+ ```mermaid
75
+ graph TD;
76
+ SRadioCard --> STag
77
+ style SRadioCard fill:#f9f,stroke:#333,stroke-width:4px
78
+ ```
@@ -0,0 +1,29 @@
1
+ import { type CSSProperties } from 'react';
2
+ import { type SRadioValue } from '../SRadio';
3
+ import { type STagProps } from '../STag';
4
+ export interface SRadioCardProps {
5
+ /** 이 카드의 값 */
6
+ val: SRadioValue;
7
+ /** 그룹의 현재 선택 값 */
8
+ value?: SRadioValue | null;
9
+ /** 라벨 (첫 줄) */
10
+ label: string;
11
+ /** 서브타이틀 (둘째 줄). 라벨 왼쪽 끝에 맞춰 들여쓴다 */
12
+ subtitle?: string;
13
+ /** 라벨 오른쪽에 붙일 태그. `STag` 의 props 를 그대로 받되 크기는 `xs` 로 고정된다 */
14
+ tag?: Omit<STagProps, 'size'>;
15
+ /** 비활성 */
16
+ disabled?: boolean;
17
+ /** native radio name (form-agnostic 그룹핑) */
18
+ name?: string;
19
+ /** 선택 변경 (sdUpdate) — 선택된 val 전달 */
20
+ onValueChange?: (val: SRadioValue) => void;
21
+ className?: string;
22
+ style?: CSSProperties;
23
+ }
24
+ /**
25
+ * SRadioCard — 라디오 + 라벨 + 서브타이틀을 카드 한 장에 담은 선택지.
26
+ * 카드 전체가 클릭 영역이며 `value === val` 이면 선택 상태다.
27
+ * 선택지마다 설명이 붙어 한 줄짜리 `SRadio` 로는 담기 어려울 때 쓴다.
28
+ */
29
+ export declare function SRadioCard({ val, value, label, subtitle, tag, disabled, name, onValueChange, className, style, }: SRadioCardProps): import("react").JSX.Element;
@@ -0,0 +1,36 @@
1
+ import { type CSSProperties } from 'react';
2
+ import { type SRadioValue } from '../SRadio';
3
+ import { type STagProps } from '../STag';
4
+ export interface SRadioCardOption {
5
+ /** 옵션 값 */
6
+ value: SRadioValue;
7
+ /** 라벨 (첫 줄) */
8
+ label: string;
9
+ /** 서브타이틀 (둘째 줄) */
10
+ subtitle?: string;
11
+ /** 라벨 오른쪽에 붙일 태그 (`STag` props, 크기는 `xs` 고정) */
12
+ tag?: Omit<STagProps, 'size'>;
13
+ /** 이 옵션만 비활성 */
14
+ disabled?: boolean;
15
+ }
16
+ export interface SRadioCardGroupProps {
17
+ /** 옵션 목록 */
18
+ options?: SRadioCardOption[];
19
+ /** 현재 선택 값 */
20
+ value?: SRadioValue | null;
21
+ /** 배치 방향 */
22
+ direction?: 'vertical' | 'horizontal';
23
+ /** 전체 비활성 */
24
+ disabled?: boolean;
25
+ /** 라디오 name 속성 (form-agnostic 그룹핑) */
26
+ groupName?: string;
27
+ /** 선택 변경 (sdUpdate) */
28
+ onValueChange?: (val: SRadioValue) => void;
29
+ className?: string;
30
+ style?: CSSProperties;
31
+ }
32
+ /**
33
+ * SRadioCardGroup — `options` 를 SRadioCard 로 렌더하고 카드 사이 간격을 맞춘다.
34
+ * 카드는 컨테이너 폭을 채우므로 horizontal 이면 카드들이 폭을 균등하게 나눠 갖는다.
35
+ */
36
+ export declare function SRadioCardGroup({ options, value, direction, disabled, groupName, onValueChange, className, style, }: SRadioCardGroupProps): import("react").JSX.Element;
@@ -0,0 +1,2 @@
1
+ export { SRadioCard, type SRadioCardProps } from './SRadioCard';
2
+ export { SRadioCardGroup, type SRadioCardGroupProps, type SRadioCardOption, } from './SRadioCardGroup';
@@ -72,6 +72,7 @@ export const TAG_COLORS = [
72
72
  - [SConfirmModal](../SConfirmModal)
73
73
  - [SGhostButton](../SGhostButton)
74
74
  - [SGnb](../SGnb)
75
+ - [SRadioCard](../SRadioCard)
75
76
  - [SSystemActionButton](../SSystemActionButton)
76
77
  - [STabs](../STabs)
77
78
  - [STextLink](../STextLink)
@@ -89,6 +90,7 @@ graph TD;
89
90
  SConfirmModal --> STag
90
91
  SGhostButton --> STag
91
92
  SGnb --> STag
93
+ SRadioCard --> STag
92
94
  SSystemActionButton --> STag
93
95
  STabs --> STag
94
96
  STextLink --> STag
package/dist/index.cjs CHANGED
@@ -15465,6 +15465,145 @@ function SRadioButton({
15465
15465
  }
15466
15466
  );
15467
15467
  }
15468
+ function SRadioCard({
15469
+ val,
15470
+ value,
15471
+ label,
15472
+ subtitle,
15473
+ tag,
15474
+ disabled = false,
15475
+ name,
15476
+ onValueChange,
15477
+ className,
15478
+ style
15479
+ }) {
15480
+ const valStr = String(val);
15481
+ const checked = value === val;
15482
+ const select = () => {
15483
+ if (!disabled && !checked) onValueChange?.(val);
15484
+ };
15485
+ const native = name != null && name !== "";
15486
+ const cardInteractiveProps = native ? {} : {
15487
+ "role": "radio",
15488
+ "aria-checked": checked,
15489
+ "tabIndex": disabled ? -1 : 0,
15490
+ "onClick": select,
15491
+ "onKeyDown": (e) => {
15492
+ if (e.key === " " || e.key === "Enter") {
15493
+ e.preventDefault();
15494
+ select();
15495
+ }
15496
+ }
15497
+ };
15498
+ const textColor = disabled ? "text-(--sys-color-disabled-text)" : null;
15499
+ return /* @__PURE__ */ jsxRuntime.jsxs(
15500
+ "label",
15501
+ {
15502
+ ...cardInteractiveProps,
15503
+ "aria-disabled": disabled || void 0,
15504
+ className: cn(
15505
+ "box-border flex w-full flex-col rounded-(--cmp-radioCard-radius) border-(length:--cmp-radioCard-border-width) border-solid px-(--cmp-radioCard-paddingX) py-(--cmp-radioCard-paddingY) transition-colors",
15506
+ disabled ? "cursor-not-allowed border-(--cmp-radioCard-border-default) bg-(--cmp-radioCard-bg-disabled)" : checked ? "cursor-pointer border-(--cmp-radioCard-border-selected) bg-(--cmp-radioCard-bg-selected)" : "cursor-pointer border-(--cmp-radioCard-border-default) bg-(--cmp-radioCard-bg-default) hover:border-(--cmp-radioCard-border-hover) hover:bg-(--cmp-radioCard-bg-hover)",
15507
+ className
15508
+ ),
15509
+ style,
15510
+ children: [
15511
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex items-center gap-(--cmp-radioCard-gap)", children: [
15512
+ native && /* @__PURE__ */ jsxRuntime.jsx(
15513
+ "input",
15514
+ {
15515
+ type: "radio",
15516
+ name,
15517
+ value: valStr,
15518
+ checked,
15519
+ disabled,
15520
+ onChange: () => onValueChange?.(val),
15521
+ className: "sr-only"
15522
+ }
15523
+ ),
15524
+ /* @__PURE__ */ jsxRuntime.jsx(
15525
+ "span",
15526
+ {
15527
+ className: "flex h-(--cmp-radio-size) w-(--cmp-radio-size) shrink-0 items-center justify-center rounded-(--cmp-radio-radius) border-(length:--cmp-radio-borderWidth) border-solid border-(--rc-dot-border) bg-(--rc-dot-bg)",
15528
+ style: {
15529
+ "--rc-dot-bg": disabled ? "var(--cmp-radio-bg-disabled)" : "var(--cmp-radio-bg-default)",
15530
+ "--rc-dot-border": disabled ? "var(--cmp-radio-border-disabled)" : checked ? "var(--cmp-radio-border-active)" : "var(--cmp-radio-border-default)"
15531
+ },
15532
+ children: checked && /* @__PURE__ */ jsxRuntime.jsx(
15533
+ "span",
15534
+ {
15535
+ className: "h-(--cmp-radio-dot-size) w-(--cmp-radio-dot-size) rounded-(--cmp-radio-radius) bg-(--rc-dot-fill)",
15536
+ style: {
15537
+ "--rc-dot-fill": disabled ? "var(--cmp-radio-dot-disabled)" : "var(--cmp-radio-dot-default)"
15538
+ }
15539
+ }
15540
+ )
15541
+ }
15542
+ ),
15543
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: cn("grid min-w-0", textColor ?? "text-(--cmp-radioCard-label-color)"), children: [
15544
+ /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": true, className: "typo-body-md-bold invisible", style: { gridArea: "1 / 1" }, children: label }),
15545
+ /* @__PURE__ */ jsxRuntime.jsx(
15546
+ "span",
15547
+ {
15548
+ className: checked ? "typo-body-md-bold" : "typo-body-md-semibold",
15549
+ style: { gridArea: "1 / 1" },
15550
+ children: label
15551
+ }
15552
+ )
15553
+ ] }),
15554
+ tag && /* @__PURE__ */ jsxRuntime.jsx(STag, { ...tag, size: "xs", className: "ml-auto" })
15555
+ ] }),
15556
+ subtitle && /* @__PURE__ */ jsxRuntime.jsx(
15557
+ "span",
15558
+ {
15559
+ className: cn(
15560
+ "typo-body-sm-default pl-(--cmp-radioCard-subtitle-paddingLeft)",
15561
+ textColor ?? "text-(--cmp-radioCard-subtitle-color)"
15562
+ ),
15563
+ children: subtitle
15564
+ }
15565
+ )
15566
+ ]
15567
+ }
15568
+ );
15569
+ }
15570
+ function SRadioCardGroup({
15571
+ options = [],
15572
+ value,
15573
+ direction = "vertical",
15574
+ disabled = false,
15575
+ groupName,
15576
+ onValueChange,
15577
+ className,
15578
+ style
15579
+ }) {
15580
+ return /* @__PURE__ */ jsxRuntime.jsx(
15581
+ "div",
15582
+ {
15583
+ role: "radiogroup",
15584
+ className: cn(
15585
+ "flex gap-(--cmp-radioCard-group-gap)",
15586
+ direction === "vertical" ? "flex-col" : "flex-row",
15587
+ className
15588
+ ),
15589
+ style,
15590
+ children: options.map((o) => /* @__PURE__ */ jsxRuntime.jsx(
15591
+ SRadioCard,
15592
+ {
15593
+ val: o.value,
15594
+ value,
15595
+ label: o.label,
15596
+ subtitle: o.subtitle,
15597
+ tag: o.tag,
15598
+ name: groupName,
15599
+ disabled: disabled || o.disabled,
15600
+ onValueChange
15601
+ },
15602
+ String(o.value)
15603
+ ))
15604
+ }
15605
+ );
15606
+ }
15468
15607
  var Frame = ({ size = 16, children, ...rest }) => /* @__PURE__ */ jsxRuntime.jsx(
15469
15608
  "svg",
15470
15609
  {
@@ -17755,6 +17894,7 @@ var SChipFilter = /* @__PURE__ */ react.forwardRef(function SChipFilter2({
17755
17894
  const pendingOpenKeyRef = react.useRef(null);
17756
17895
  const triggerRefs = react.useRef(/* @__PURE__ */ new Map());
17757
17896
  const initializedDefaultsRef = react.useRef(/* @__PURE__ */ new Set());
17897
+ const pendingDefaultRef = react.useRef(/* @__PURE__ */ new Set());
17758
17898
  const misuseWarningSignatureRef = react.useRef(null);
17759
17899
  const prevOpenKeyRef = react.useRef(null);
17760
17900
  const valuesRef = react.useRef(values);
@@ -17805,15 +17945,18 @@ var SChipFilter = /* @__PURE__ */ react.forwardRef(function SChipFilter2({
17805
17945
  const patch = {};
17806
17946
  let changed = false;
17807
17947
  fields.forEach((field) => {
17808
- if (initializedDefaultsRef.current.has(field.key)) return;
17809
17948
  if (!isEmptyValue(values[field.key])) {
17810
17949
  initializedDefaultsRef.current.add(field.key);
17950
+ pendingDefaultRef.current.delete(field.key);
17811
17951
  return;
17812
17952
  }
17953
+ if (!field.required && initializedDefaultsRef.current.has(field.key)) return;
17954
+ if (field.required && pendingDefaultRef.current.has(field.key)) return;
17813
17955
  const def = resolveFieldDefault(field);
17814
17956
  if (def === void 0) return;
17815
17957
  patch[field.key] = def;
17816
17958
  initializedDefaultsRef.current.add(field.key);
17959
+ pendingDefaultRef.current.add(field.key);
17817
17960
  changed = true;
17818
17961
  });
17819
17962
  if (changed) onValueChange?.({ ...values, ...patch });
@@ -20676,6 +20819,8 @@ exports.SPopup = SPopup;
20676
20819
  exports.SPortal = SPortal;
20677
20820
  exports.SRadio = SRadio;
20678
20821
  exports.SRadioButton = SRadioButton;
20822
+ exports.SRadioCard = SRadioCard;
20823
+ exports.SRadioCardGroup = SRadioCardGroup;
20679
20824
  exports.SRadioGroup = SRadioGroup;
20680
20825
  exports.SSPLITTER_UNITS = SSPLITTER_UNITS;
20681
20826
  exports.SScrollArea = SScrollArea;