sellmate-design-system-react 0.10.0 → 0.12.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.
@@ -0,0 +1 @@
1
+ export { SModal, type SModalRef, type SConfirmOptions, type SLoadingOptions, type SCreateOptions, type SModalCreateComponentProps, } from './SModal';
package/dist/index.cjs CHANGED
@@ -2920,6 +2920,76 @@ var SCard = react.forwardRef(function SCard2({ bordered = false, className, chil
2920
2920
  }
2921
2921
  );
2922
2922
  });
2923
+ var SExpansionItem = react.forwardRef(
2924
+ function SExpansionItem2({
2925
+ title,
2926
+ depth = 1,
2927
+ leading,
2928
+ trailing,
2929
+ bodyPadding = true,
2930
+ expanded,
2931
+ defaultExpanded = false,
2932
+ dense = false,
2933
+ disabled = false,
2934
+ onToggle,
2935
+ className,
2936
+ children,
2937
+ ...rest
2938
+ }, ref) {
2939
+ const [internalExpanded, setInternalExpanded] = react.useState(defaultExpanded);
2940
+ const isExpanded = expanded ?? internalExpanded;
2941
+ const hasBody = react.Children.toArray(children).some(
2942
+ (child) => child !== null && child !== void 0 && (typeof child !== "string" || child.trim() !== "")
2943
+ );
2944
+ const leadingNode = typeof leading === "function" ? leading({ expanded: isExpanded, disabled }) : leading;
2945
+ const id = react.useId();
2946
+ return /* @__PURE__ */ jsxRuntime.jsxs(
2947
+ "div",
2948
+ {
2949
+ ref,
2950
+ className: cn(
2951
+ "group overflow-hidden bg-[var(--cmp-expansionItem-title-bg-default)]",
2952
+ className
2953
+ ),
2954
+ ...rest,
2955
+ children: [
2956
+ /* @__PURE__ */ jsxRuntime.jsxs(
2957
+ "button",
2958
+ {
2959
+ type: "button",
2960
+ disabled,
2961
+ "aria-expanded": isExpanded,
2962
+ "aria-controls": id,
2963
+ onClick: (event) => {
2964
+ if (disabled) return;
2965
+ const next = !isExpanded;
2966
+ if (expanded === void 0) setInternalExpanded(next);
2967
+ onToggle?.(next, event);
2968
+ },
2969
+ className: cn(
2970
+ "flex w-full items-center justify-between border-0 text-left text-[14px] font-bold leading-[24px] text-[var(--cmp-expansionItem-title-text-default)] focus-visible:outline focus-visible:outline-2 focus-visible:outline-[var(--sys-color-border-accent)] disabled:cursor-not-allowed disabled:text-[var(--sys-color-disabled-text)] cursor-pointer",
2971
+ isExpanded && "border-b border-[var(--cmp-expansionItem-title-border)]",
2972
+ !isExpanded && "hover:bg-[var(--cmp-expansionItem-title-bg-hover)]",
2973
+ dense ? "min-h-8 py-[var(--cmp-expansionItem-title-paddingY-dense)]" : "min-h-10 py-[var(--cmp-expansionItem-title-paddingY-normal)]",
2974
+ "gap-[var(--cmp-expansionItem-title-gap)]",
2975
+ "pr-[var(--cmp-expansionItem-title-paddingX)]",
2976
+ depth === 2 ? "pl-[calc(var(--cmp-expansionItem-title-paddingX)+32px)]" : depth === 3 ? "pl-[calc(var(--cmp-expansionItem-title-paddingX)+64px)]" : "pl-[var(--cmp-expansionItem-title-paddingX)]",
2977
+ isExpanded && "bg-[var(--cmp-expansionItem-title-bg-expanded)] text-[var(--cmp-expansionItem-title-text-expanded)]"
2978
+ ),
2979
+ children: [
2980
+ !leading && hasBody && /* @__PURE__ */ jsxRuntime.jsx(SIcon, { name: isExpanded ? "caretUp" : "caretDown", size: 12 }),
2981
+ leadingNode,
2982
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex min-w-0 flex-1 flex-col", children: /* @__PURE__ */ jsxRuntime.jsx("span", { children: title }) }),
2983
+ trailing
2984
+ ]
2985
+ }
2986
+ ),
2987
+ /* @__PURE__ */ jsxRuntime.jsx("div", { id, hidden: !isExpanded, className: "bg-[var(--cmp-expansionItem-body-bg)]", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: bodyPadding ? "p-[var(--cmp-expansionItem-body-padding)]" : void 0, children }) })
2988
+ ]
2989
+ }
2990
+ );
2991
+ }
2992
+ );
2923
2993
  var SDivider = react.forwardRef(function SDivider2({ vertical = false, className, style, ...rest }, ref) {
2924
2994
  return /* @__PURE__ */ jsxRuntime.jsx(
2925
2995
  "hr",
@@ -4969,6 +5039,250 @@ function SLoadingModal({
4969
5039
  }
4970
5040
  );
4971
5041
  }
5042
+ var EXIT_DURATION = 300;
5043
+ var EXIT_BUFFER = 100;
5044
+ var SModalRefImpl = class {
5045
+ constructor() {
5046
+ this.pendingClose = false;
5047
+ this.pendingPatches = [];
5048
+ this.dismissRequested = false;
5049
+ }
5050
+ onOk(fn) {
5051
+ this.okFn = fn;
5052
+ return this;
5053
+ }
5054
+ onCancel(fn) {
5055
+ this.cancelFn = fn;
5056
+ return this;
5057
+ }
5058
+ onClose(fn) {
5059
+ this.closeFn = fn;
5060
+ return this;
5061
+ }
5062
+ onClick(fn) {
5063
+ this.clickFn = fn;
5064
+ return this;
5065
+ }
5066
+ onSubmit(fn) {
5067
+ this.submitFn = fn;
5068
+ return this;
5069
+ }
5070
+ onDismissed(fn) {
5071
+ this.dismissedFn = fn;
5072
+ return this;
5073
+ }
5074
+ update(patch) {
5075
+ if (this.updateBinding) this.updateBinding(patch);
5076
+ else this.pendingPatches.push(patch);
5077
+ return this;
5078
+ }
5079
+ // 닫힘 트리거 — 이미 닫힘이 시작됐으면 무시(중복 콜백 방지)
5080
+ settle(emit2) {
5081
+ if (this.dismissRequested) return;
5082
+ this.dismissRequested = true;
5083
+ emit2();
5084
+ if (this.closeBinding) this.closeBinding();
5085
+ else this.pendingClose = true;
5086
+ }
5087
+ ok() {
5088
+ this.settle(() => this.okFn?.());
5089
+ return this;
5090
+ }
5091
+ cancel() {
5092
+ this.settle(() => this.cancelFn?.());
5093
+ return this;
5094
+ }
5095
+ close() {
5096
+ this.settle(() => this.closeFn?.());
5097
+ return this;
5098
+ }
5099
+ submit() {
5100
+ this.submitFn?.();
5101
+ return this;
5102
+ }
5103
+ /** @internal 마운트 컴포넌트가 닫기 함수를 등록 (등록 전 close() 호출은 지연 반영) */
5104
+ _bindClose(fn) {
5105
+ this.closeBinding = fn;
5106
+ if (this.pendingClose) fn();
5107
+ }
5108
+ /** @internal 마운트 컴포넌트가 옵션 갱신 함수를 등록 (등록 전 update() 호출은 지연 반영) */
5109
+ _bindUpdate(fn) {
5110
+ this.updateBinding = fn;
5111
+ if (this.pendingPatches.length) {
5112
+ this.pendingPatches.forEach(fn);
5113
+ this.pendingPatches = [];
5114
+ }
5115
+ }
5116
+ /** @internal 정리 대상(root·host) 주입 */
5117
+ _setTeardown(root, host) {
5118
+ this.root = root;
5119
+ this.host = host;
5120
+ }
5121
+ /** @internal 닫힘 시작 표시 — 컴포넌트 버튼·백드롭·ESC 닫힘 경로가 호출(중복 트리거 방지) */
5122
+ _markDismissed() {
5123
+ this.dismissRequested = true;
5124
+ }
5125
+ /** @internal confirm 버튼 경로에서 onOk 발화 */
5126
+ _emitOk() {
5127
+ this.okFn?.();
5128
+ }
5129
+ /** @internal confirm 버튼 경로에서 onCancel 발화 */
5130
+ _emitCancel() {
5131
+ this.cancelFn?.();
5132
+ }
5133
+ /** @internal 닫기(X) 경로에서 onClose 발화 */
5134
+ _emitClose() {
5135
+ this.closeFn?.();
5136
+ }
5137
+ /** @internal loading 버튼 경로에서 onClick 발화 */
5138
+ _emitClick() {
5139
+ this.clickFn?.();
5140
+ }
5141
+ /** @internal */
5142
+ _emitDismissed() {
5143
+ this.dismissedFn?.();
5144
+ }
5145
+ /** @internal 닫힘 애니메이션 종료 후 언마운트 */
5146
+ _teardown() {
5147
+ this.root?.unmount();
5148
+ this.host?.remove();
5149
+ }
5150
+ };
5151
+ function useExitTeardown(open, refImpl) {
5152
+ const dismissedRef = react.useRef(false);
5153
+ react.useEffect(() => {
5154
+ if (open || dismissedRef.current) return;
5155
+ dismissedRef.current = true;
5156
+ const t = setTimeout(() => {
5157
+ refImpl._emitDismissed();
5158
+ refImpl._teardown();
5159
+ }, EXIT_DURATION + EXIT_BUFFER);
5160
+ return () => clearTimeout(t);
5161
+ }, [open, refImpl]);
5162
+ }
5163
+ function ConfirmMount({ options, refImpl }) {
5164
+ const [open, setOpen] = react.useState(true);
5165
+ const [opts, setOpts] = react.useState(options);
5166
+ react.useEffect(() => {
5167
+ refImpl._bindClose(() => setOpen(false));
5168
+ refImpl._bindUpdate((patch) => setOpts((prev) => ({ ...prev, ...patch })));
5169
+ }, [refImpl]);
5170
+ useExitTeardown(open, refImpl);
5171
+ return /* @__PURE__ */ jsxRuntime.jsx(
5172
+ SConfirmModal,
5173
+ {
5174
+ ...opts,
5175
+ open,
5176
+ onOpenChange: (o) => {
5177
+ if (!o) refImpl._markDismissed();
5178
+ setOpen(o);
5179
+ },
5180
+ onOk: () => refImpl._emitOk(),
5181
+ onCancel: () => refImpl._emitCancel(),
5182
+ onClose: () => refImpl._emitClose()
5183
+ }
5184
+ );
5185
+ }
5186
+ function LoadingMount({ options, refImpl }) {
5187
+ const [open, setOpen] = react.useState(true);
5188
+ const [opts, setOpts] = react.useState(options);
5189
+ const buttonClicked = react.useRef(false);
5190
+ react.useEffect(() => {
5191
+ refImpl._bindClose(() => setOpen(false));
5192
+ refImpl._bindUpdate((patch) => setOpts((prev) => ({ ...prev, ...patch })));
5193
+ }, [refImpl]);
5194
+ useExitTeardown(open, refImpl);
5195
+ return /* @__PURE__ */ jsxRuntime.jsx(
5196
+ SLoadingModal,
5197
+ {
5198
+ ...opts,
5199
+ open,
5200
+ onOpenChange: (o) => {
5201
+ if (!o && buttonClicked.current) {
5202
+ buttonClicked.current = false;
5203
+ return;
5204
+ }
5205
+ if (!o) refImpl._markDismissed();
5206
+ setOpen(o);
5207
+ },
5208
+ onClose: () => refImpl._emitClose(),
5209
+ onButtonClick: () => {
5210
+ buttonClicked.current = true;
5211
+ refImpl._emitClick();
5212
+ }
5213
+ }
5214
+ );
5215
+ }
5216
+ function CreateMount({ options, refImpl }) {
5217
+ const {
5218
+ component: Component,
5219
+ componentProps,
5220
+ persistent,
5221
+ showClose,
5222
+ width,
5223
+ height,
5224
+ className,
5225
+ ariaTitle
5226
+ } = options;
5227
+ const [open, setOpen] = react.useState(true);
5228
+ react.useEffect(() => {
5229
+ refImpl._bindClose(() => setOpen(false));
5230
+ refImpl._bindUpdate(() => {
5231
+ });
5232
+ }, [refImpl]);
5233
+ useExitTeardown(open, refImpl);
5234
+ const componentAllProps = {
5235
+ ...componentProps,
5236
+ modalRef: refImpl
5237
+ };
5238
+ return /* @__PURE__ */ jsxRuntime.jsx(
5239
+ SModalContainer,
5240
+ {
5241
+ open,
5242
+ onOpenChange: (o) => {
5243
+ if (!o) refImpl._markDismissed();
5244
+ setOpen(o);
5245
+ },
5246
+ persistent,
5247
+ showClose,
5248
+ onClose: () => refImpl.close(),
5249
+ ariaTitle,
5250
+ width,
5251
+ height,
5252
+ className,
5253
+ children: /* @__PURE__ */ jsxRuntime.jsx(Component, { ...componentAllProps })
5254
+ }
5255
+ );
5256
+ }
5257
+ function mount(render) {
5258
+ const host = document.createElement("div");
5259
+ document.body.appendChild(host);
5260
+ const refImpl = new SModalRefImpl();
5261
+ const root = client.createRoot(host);
5262
+ refImpl._setTeardown(root, host);
5263
+ root.render(render(refImpl));
5264
+ return refImpl;
5265
+ }
5266
+ var SModal = {
5267
+ /** 확인/취소 모달을 즉시 띄운다. 반환 핸들에 onOk/onCancel/onClose/onDismissed 체이닝. */
5268
+ confirm(options) {
5269
+ return mount((refImpl) => /* @__PURE__ */ jsxRuntime.jsx(ConfirmMount, { options, refImpl }));
5270
+ },
5271
+ /**
5272
+ * 로딩(스피너/에러) 모달을 즉시 띄운다. 로딩 중 임의 닫힘 방지를 위해 persistent 기본 true.
5273
+ * 버튼 클릭은 자동 닫힘 없이 onClick 만 발화 — update()/close() 로 후속 동작을 결정한다.
5274
+ */
5275
+ loading(options = {}) {
5276
+ return mount((refImpl) => /* @__PURE__ */ jsxRuntime.jsx(LoadingMount, { options: { persistent: true, ...options }, refImpl }));
5277
+ },
5278
+ /**
5279
+ * 커스텀 React 컴포넌트를 SModalContainer(딤+카드) 안에 띄운다. component 에 modalRef 가
5280
+ * prop 으로 주입되어 컴포넌트가 ok()/cancel()/close()/submit()/onSubmit() 으로 자기 모달을 제어한다.
5281
+ */
5282
+ create(options) {
5283
+ return mount((refImpl) => /* @__PURE__ */ jsxRuntime.jsx(CreateMount, { options, refImpl }));
5284
+ }
5285
+ };
4972
5286
 
4973
5287
  // src/components/SGnb/gnb.config.ts
4974
5288
  var gnb = (p) => `var(--cmp-gnb-${p})`;
@@ -11548,6 +11862,7 @@ exports.SDatePicker = SDatePicker;
11548
11862
  exports.SDateRangePicker = SDateRangePicker;
11549
11863
  exports.SDivider = SDivider;
11550
11864
  exports.SDropdownButton = SDropdownButton;
11865
+ exports.SExpansionItem = SExpansionItem;
11551
11866
  exports.SField = SField;
11552
11867
  exports.SFilePicker = SFilePicker;
11553
11868
  exports.SForm = SForm;
@@ -11563,6 +11878,7 @@ exports.SLinearProgress = SLinearProgress;
11563
11878
  exports.SLoadingContainer = SLoadingContainer;
11564
11879
  exports.SLoadingModal = SLoadingModal;
11565
11880
  exports.SLoadingViewport = SLoadingViewport;
11881
+ exports.SModal = SModal;
11566
11882
  exports.SNumberInput = SNumberInput;
11567
11883
  exports.SPage = SPage;
11568
11884
  exports.SPagination = SPagination;