@designbasekorea/figma-ui 0.1.0 → 0.1.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.
package/dist/index.esm.js CHANGED
@@ -1,11 +1,11 @@
1
- import React, { useState } from 'react';
2
- import { ExternalLinkIcon } from '@designbasekorea/icons';
3
- import { Badge } from '@designbasekorea/ui';
1
+ import React, { useState, useEffect, useRef } from 'react';
2
+ import { ExternalLinkIcon, HeartFilledIcon, CoffeeFilledIcon, StarIcon, KeyIcon, MoreHorizontalIcon, CloseIcon, ExpandIcon, GripVerticalIcon, CircleCheckFilledIcon } from '@designbasekorea/icons';
3
+ import { Spinner, Badge, Input, Button, SegmentControl, Modal, Toggle, Progressbar } from '@designbasekorea/ui';
4
4
  export * from '@designbasekorea/ui';
5
5
 
6
6
  function r(e){var t,f,n="";if("string"==typeof e||"number"==typeof e)n+=e;else if("object"==typeof e)if(Array.isArray(e)){var o=e.length;for(t=0;t<o;t++)e[t]&&(f=r(e[t]))&&(n&&(n+=" "),n+=f);}else for(f in e)e[f]&&(n&&(n+=" "),n+=f);return n}function clsx(){for(var e,t,f=0,n="",o=arguments.length;f<o;f++)(e=arguments[f])&&(t=r(e))&&(n&&(n+=" "),n+=t);return n}
7
7
 
8
- const LogoDropdown = ({ logoSrc, logoAlt = 'Logo', links, position = 'bottom-left', className, t = (key) => key, }) => {
8
+ const LogoDropdown = ({ logoSrc, logoAlt = 'Logo', links, position = 'top-left', className, t = (key) => key, }) => {
9
9
  const [isOpen, setIsOpen] = useState(false);
10
10
  const toggleDropdown = () => setIsOpen(!isOpen);
11
11
  const handleLinkClick = (url) => {
@@ -27,7 +27,56 @@ const LogoDropdown = ({ logoSrc, logoAlt = 'Logo', links, position = 'bottom-lef
27
27
  };
28
28
  LogoDropdown.displayName = 'LogoDropdown';
29
29
 
30
- const Footer = ({ logoSrc, logoAlt = 'Logo', logoLinks = [], onLicensePageClick, paymentStatus = 'FREE', usageCount = 0, isLoading = false, showPaymentStatus = true, maxDailyUsage = 20, className, t = (key) => key, children, }) => {
30
+ const PaymentBadge = ({ isActive, onClick, isLoading = false, text, t = (key) => key, className, }) => {
31
+ const getBadgeText = () => {
32
+ if (text)
33
+ return text;
34
+ return isActive
35
+ ? (t('proUser') || '프로 계정')
36
+ : (t('usingFree') || '무료 사용');
37
+ };
38
+ const getBadgeVariant = () => {
39
+ return isActive ? 'success' : 'secondary';
40
+ };
41
+ return (React.createElement("div", { className: `designbase-figma-payment-badge ${className || ''} ${isActive ? 'active' : 'free'}`, onClick: onClick, style: { cursor: onClick ? 'pointer' : 'default' } }, isLoading ? (React.createElement(Spinner, { size: "s" })) : (React.createElement(Badge, { variant: getBadgeVariant(), size: "s", className: "designbase-figma-payment-badge__badge" }, getBadgeText()))));
42
+ };
43
+ PaymentBadge.displayName = 'PaymentBadge';
44
+
45
+ const DonationBadge = ({ donationUrl = 'https://buymeacoffee.com/designbase', text = 'Buy me a coffee', iconType = 'heart', size = 'm', className, onClick, }) => {
46
+ const handleClick = () => {
47
+ if (onClick) {
48
+ onClick();
49
+ }
50
+ else {
51
+ window.open(donationUrl, '_blank', 'noopener,noreferrer');
52
+ }
53
+ };
54
+ const Icon = iconType === 'heart' ? HeartFilledIcon : CoffeeFilledIcon;
55
+ const classes = clsx('designbase-figma-donation-badge', `designbase-figma-donation-badge--${size}`, `designbase-figma-donation-badge--${iconType}`, className);
56
+ return (React.createElement("button", { type: "button", className: classes, onClick: handleClick, "aria-label": text },
57
+ React.createElement(Icon, { className: "designbase-figma-donation-badge__icon" }),
58
+ React.createElement("span", { className: "designbase-figma-donation-badge__text" }, text)));
59
+ };
60
+ DonationBadge.displayName = 'DonationBadge';
61
+
62
+ const DEFAULT_LANGUAGES = [
63
+ { code: 'ko', label: 'KO' },
64
+ { code: 'en', label: 'EN' },
65
+ ];
66
+ const LanguageSelector = ({ currentLanguage = 'ko', languages = DEFAULT_LANGUAGES, onLanguageChange, size = 's', className, }) => {
67
+ const handleLanguageChange = (languageCode) => {
68
+ if (onLanguageChange) {
69
+ onLanguageChange(languageCode);
70
+ }
71
+ };
72
+ const classes = clsx('designbase-figma-language-selector', `designbase-figma-language-selector--${size}`, className);
73
+ return (React.createElement("div", { className: classes }, languages.map((language) => (React.createElement("button", { key: language.code, type: "button", className: clsx('designbase-figma-language-selector__button', {
74
+ 'designbase-figma-language-selector__button--active': currentLanguage === language.code,
75
+ }), onClick: () => handleLanguageChange(language.code), "aria-label": `${language.label} 언어 선택`, "aria-pressed": currentLanguage === language.code }, language.label)))));
76
+ };
77
+ LanguageSelector.displayName = 'LanguageSelector';
78
+
79
+ const Footer = ({ logoSrc, logoAlt = 'Logo', logoLinks = [], onLicensePageClick, paymentStatus = 'FREE', usageCount = 0, isLoading = false, showPaymentStatus = true, maxDailyUsage = 20, showDonation = false, donationUrl = 'https://buymeacoffee.com/designbase', donationText = 'Buy me a coffee', showLanguageSelector = false, currentLanguage = 'ko', languages, onLanguageChange, className, t = (key) => key, children, }) => {
31
80
  const isActive = paymentStatus === 'PAID';
32
81
  const hasChildren = React.Children.count(children) > 0;
33
82
  const classes = clsx('designbase-figma-footer', {
@@ -35,7 +84,9 @@ const Footer = ({ logoSrc, logoAlt = 'Logo', logoLinks = [], onLicensePageClick,
35
84
  }, className);
36
85
  return (React.createElement("footer", { className: classes },
37
86
  React.createElement("div", { className: "designbase-figma-footer__wrap" },
38
- React.createElement(LogoDropdown, { logoSrc: logoSrc, logoAlt: logoAlt, links: logoLinks, position: "bottom-left", t: t }),
87
+ React.createElement("div", { className: "designbase-figma-footer__left" },
88
+ React.createElement(LogoDropdown, { logoSrc: logoSrc, logoAlt: logoAlt, links: logoLinks, position: "top-left", t: t }),
89
+ showLanguageSelector && (React.createElement(LanguageSelector, { currentLanguage: currentLanguage, languages: languages, onLanguageChange: onLanguageChange, size: "s" }))),
39
90
  showPaymentStatus && (React.createElement("div", { className: "designbase-figma-footer__payment-states" },
40
91
  !isLoading && (React.createElement("div", { className: "designbase-figma-footer__usage-info" }, isActive ? (React.createElement("span", { className: "designbase-figma-footer__unlimited-usage" }, t('unlimitedUsage'))) : (React.createElement(React.Fragment, null,
41
92
  React.createElement("span", { className: "designbase-figma-footer__usage-count" }, usageCount),
@@ -45,10 +96,442 @@ const Footer = ({ logoSrc, logoAlt = 'Logo', logoLinks = [], onLicensePageClick,
45
96
  " ",
46
97
  t('perDay')),
47
98
  React.createElement("span", { className: "designbase-figma-footer__reset-info" }, t('resetsDaily')))))),
48
- React.createElement(Badge, { variant: isActive ? 'success' : 'secondary', size: "s", onClick: onLicensePageClick, style: { cursor: 'pointer' } }, isLoading ? t('loading') : isActive ? t('premium') : t('free'))))),
99
+ React.createElement(PaymentBadge, { isActive: isActive, onClick: onLicensePageClick, isLoading: isLoading, t: t }))),
100
+ showDonation && (React.createElement("div", { className: "designbase-figma-footer__donation" },
101
+ React.createElement(DonationBadge, { donationUrl: donationUrl, text: donationText, iconType: "heart", size: "s" })))),
49
102
  children));
50
103
  };
51
104
  Footer.displayName = 'Footer';
52
105
 
53
- export { Footer, LogoDropdown };
106
+ const FormWithSubmit = ({ onLicenseSubmit, disabled = false, isSubmitting = false, value = '', onValueChange, label = 'License Key', placeholder = 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx', submitText = 'Submit', submittingText = 'Verifying...', className, }) => {
107
+ const [inputValue, setInputValue] = useState(value);
108
+ useEffect(() => {
109
+ setInputValue(value);
110
+ }, [value]);
111
+ const handleSubmit = async (e) => {
112
+ e.preventDefault();
113
+ await onLicenseSubmit(inputValue);
114
+ };
115
+ const handleChange = (e) => {
116
+ const newValue = e.target.value;
117
+ setInputValue(newValue);
118
+ onValueChange?.(newValue);
119
+ };
120
+ return (React.createElement("form", { className: `designbase-figma-form-with-submit ${className || ''}`, onSubmit: handleSubmit },
121
+ React.createElement("div", { className: "designbase-figma-form-with-submit__field" },
122
+ React.createElement("label", { className: "designbase-figma-form-with-submit__label" }, label),
123
+ React.createElement(Input, { value: inputValue, onChange: handleChange, placeholder: placeholder, disabled: disabled || isSubmitting, size: "m" })),
124
+ React.createElement(Button, { type: "submit", variant: "primary", size: "m", loading: isSubmitting, disabled: !inputValue.trim() || isSubmitting || disabled, fullWidth: true }, isSubmitting ? submittingText : submitText)));
125
+ };
126
+ FormWithSubmit.displayName = 'FormWithSubmit';
127
+
128
+ const PaymentStatusSection = ({ status, usageCount, activationLimit, activationUsage, licenseKey, onDeactivate, isDeactivating = false, showDetails = false, className, }) => {
129
+ const remainingActivations = activationLimit - activationUsage;
130
+ return (React.createElement("div", { className: `designbase-figma-payment-status ${className || ''}` },
131
+ React.createElement("div", { className: "designbase-figma-payment-status__license-key" },
132
+ "\uB77C\uC774\uC120\uC2A4 \uD0A4: ",
133
+ React.createElement("span", null, licenseKey)),
134
+ showDetails && (React.createElement("div", { className: "designbase-figma-payment-status__details" },
135
+ React.createElement("div", { className: "designbase-figma-payment-status__activation-info" },
136
+ React.createElement("div", { className: "designbase-figma-payment-status__remaining" },
137
+ remainingActivations,
138
+ " ",
139
+ remainingActivations === 1 ? '활성' : '활성',
140
+ " \uC790\uB9AC \uB0A8\uC74C"),
141
+ React.createElement("div", { className: "designbase-figma-payment-status__usage" },
142
+ activationUsage,
143
+ "/",
144
+ activationLimit)),
145
+ React.createElement(Button, { onClick: onDeactivate, variant: "tertiary", size: "s", disabled: isDeactivating }, isDeactivating ? '비활성화중...' : '라이선스 비활성화')))));
146
+ };
147
+ PaymentStatusSection.displayName = 'PaymentStatusSection';
148
+
149
+ const defaultFeatures = [
150
+ { name: '사용 제한', free: '일일 제한', pro: '무제한' },
151
+ { name: '모든 주제 사용', free: false, pro: true },
152
+ { name: '최대 선택 제한', free: '최대 5개', pro: '최대 30개' },
153
+ { name: '더미 개수', free: '1,400개', pro: '4,400개 +' },
154
+ { name: '커스텀 숫자 사용', free: false, pro: true },
155
+ ];
156
+ const defaultPricing = {
157
+ monthly: 2,
158
+ yearly: 21.6,
159
+ };
160
+ const PricingComparison = ({ features = defaultFeatures, pricing = defaultPricing, t = (key) => key, className, }) => {
161
+ const [isYearly, setIsYearly] = useState(false);
162
+ const getPrice = () => {
163
+ const price = isYearly ? pricing.yearly : pricing.monthly;
164
+ return `$${price.toLocaleString()} / ${isYearly ? t('perYear') : t('perMonth')}`;
165
+ };
166
+ const getDiscountPercentage = () => {
167
+ const monthlyPrice = pricing.monthly * 12;
168
+ const yearlyPrice = pricing.yearly;
169
+ const discount = Math.round(((monthlyPrice - yearlyPrice) / monthlyPrice) * 100);
170
+ return discount;
171
+ };
172
+ return (React.createElement("div", { className: `designbase-figma-pricing-comparison ${className || ''}` },
173
+ React.createElement("div", { className: "designbase-figma-pricing-comparison__billing-toggle" },
174
+ React.createElement(SegmentControl, { value: isYearly ? 'yearly' : 'monthly', onChange: (value) => setIsYearly(value === 'yearly'), options: [
175
+ {
176
+ value: 'monthly',
177
+ label: t('monthlyBilling') || '월간 결제',
178
+ },
179
+ {
180
+ value: 'yearly',
181
+ label: (React.createElement("div", { style: { display: 'flex', alignItems: 'center', gap: '8px' } },
182
+ t('yearlyBilling') || '연간 결제',
183
+ React.createElement("span", { className: "designbase-figma-pricing-comparison__discount-badge" },
184
+ getDiscountPercentage(),
185
+ "% \uC808\uC57D"))),
186
+ },
187
+ ], size: "s" })),
188
+ React.createElement("table", { className: "designbase-figma-pricing-comparison__table" },
189
+ React.createElement("thead", null,
190
+ React.createElement("tr", null,
191
+ React.createElement("th", null, t('feature') || '기능'),
192
+ React.createElement("th", null,
193
+ React.createElement("div", { className: "designbase-figma-pricing-comparison__plan-name" }, t('free') || 'Free'),
194
+ React.createElement("div", null, t('freePrice') || '$0')),
195
+ React.createElement("th", { className: "designbase-figma-pricing-comparison__recommended" },
196
+ React.createElement("div", { className: "designbase-figma-pricing-comparison__plan-name" },
197
+ t('pro') || 'Pro',
198
+ React.createElement("span", { className: "designbase-figma-pricing-comparison__recommended-badge" }, t('recommended') || '추천')),
199
+ React.createElement("div", null, getPrice())))),
200
+ React.createElement("tbody", null, features.map((feature, index) => (React.createElement("tr", { key: index },
201
+ React.createElement("td", null, feature.name),
202
+ React.createElement("td", null, typeof feature.free === 'boolean'
203
+ ? (feature.free ? '✓' : '✗')
204
+ : feature.free),
205
+ React.createElement("td", null, typeof feature.pro === 'boolean'
206
+ ? (feature.pro ? '✓' : '✗')
207
+ : feature.pro))))))));
208
+ };
209
+ PricingComparison.displayName = 'PricingComparison';
210
+
211
+ const PageLicense = ({ status: initialStatus, onClose, usageCount: initialUsageCount = 0, onLicenseSubmit, licenseKey: initialLicenseKey = '', setPaymentStatus, setUsageCount, setShowLicensePage, paymentPageUrl, t = (key) => key, className, }) => {
212
+ const [isSubmitting, setIsSubmitting] = useState(false);
213
+ const [isDeactivating, setIsDeactivating] = useState(false);
214
+ const [licenseKey, setLicenseKey] = useState(initialLicenseKey);
215
+ const [status, setStatus] = useState(initialStatus);
216
+ const [usageCount, setLocalUsageCount] = useState(initialUsageCount);
217
+ const [activationLimit, setActivationLimit] = useState(0);
218
+ const [activationUsage, setActivationUsage] = useState(0);
219
+ const [showDetails, setShowDetails] = useState(false);
220
+ useEffect(() => {
221
+ const handleMessage = (event) => {
222
+ const { type, paymentStatus, licenseKey, activationLimit, activationUsage, success, message, usageCount } = event.data.pluginMessage || {};
223
+ if (type === 'update-plugin-status' || type === 'license-verification-result') {
224
+ if (paymentStatus)
225
+ setStatus(paymentStatus);
226
+ if (licenseKey)
227
+ setLicenseKey(licenseKey);
228
+ if (activationLimit !== undefined)
229
+ setActivationLimit(activationLimit);
230
+ if (activationUsage !== undefined)
231
+ setActivationUsage(activationUsage);
232
+ if (paymentStatus)
233
+ setPaymentStatus(paymentStatus);
234
+ if (usageCount !== undefined) {
235
+ setLocalUsageCount(usageCount);
236
+ setUsageCount(usageCount);
237
+ }
238
+ }
239
+ if (type === 'license-verification-result') {
240
+ setIsSubmitting(false);
241
+ if (message)
242
+ console.log('License verification:', message);
243
+ if (success) {
244
+ setStatus('PAID');
245
+ setPaymentStatus('PAID');
246
+ }
247
+ }
248
+ if (type === 'license-deactivation-result') {
249
+ setIsDeactivating(false);
250
+ if (message)
251
+ console.log('License deactivation:', message);
252
+ if (success) {
253
+ setStatus('UNPAID');
254
+ setPaymentStatus('UNPAID');
255
+ setLicenseKey('');
256
+ setLocalUsageCount(20);
257
+ setActivationLimit(0);
258
+ setActivationUsage(0);
259
+ }
260
+ }
261
+ };
262
+ window.addEventListener('message', handleMessage);
263
+ if (typeof parent !== 'undefined') {
264
+ parent.postMessage({ pluginMessage: { type: 'initialize' } }, '*');
265
+ }
266
+ return () => window.removeEventListener('message', handleMessage);
267
+ }, [setPaymentStatus, setUsageCount]);
268
+ const handleLicenseSubmit = async (submittedLicenseKey) => {
269
+ setIsSubmitting(true);
270
+ if (typeof parent !== 'undefined') {
271
+ parent.postMessage({
272
+ pluginMessage: {
273
+ type: 'verify-license',
274
+ licenseKey: submittedLicenseKey
275
+ }
276
+ }, '*');
277
+ }
278
+ };
279
+ const handleDeactivateLicense = () => {
280
+ setIsDeactivating(true);
281
+ if (typeof parent !== 'undefined') {
282
+ parent.postMessage({
283
+ pluginMessage: { type: 'deactivate-license' }
284
+ }, '*');
285
+ }
286
+ };
287
+ const isPaid = status === 'PAID';
288
+ return (React.createElement("div", { className: `designbase-figma-page-license ${className || ''}` },
289
+ React.createElement("div", { className: "designbase-figma-page-license__content" },
290
+ React.createElement("div", { className: "designbase-figma-page-license__header" },
291
+ React.createElement("div", { className: "designbase-figma-page-license__title" },
292
+ isPaid ? React.createElement(StarIcon, { size: 24 }) : React.createElement(KeyIcon, { size: 24 }),
293
+ React.createElement("h2", null, isPaid ? t('proAccount') || '프로 계정' : t('upgradeToPro') || '프로로 업그레이드')),
294
+ React.createElement("p", { className: "designbase-figma-page-license__description" }, isPaid
295
+ ? t('allFeaturesAvailable') || '모든 기능을 사용할 수 있습니다.'
296
+ : t('purchaseForUnlimited') || '라이선스 구매 후 무제한 접근이 가능합니다.')),
297
+ React.createElement(PricingComparison, { t: t }),
298
+ isPaid && (React.createElement("div", { className: "designbase-figma-page-license__section" },
299
+ React.createElement("div", { className: "designbase-figma-page-license__section-header" },
300
+ React.createElement("h3", null, t('licenseActivated') || '라이선스 활성화됨'),
301
+ React.createElement(Button, { onClick: () => setShowDetails(!showDetails), variant: "tertiary", size: "s", iconOnly: true },
302
+ React.createElement(MoreHorizontalIcon, { size: 16 }))),
303
+ React.createElement(PaymentStatusSection, { status: status, usageCount: usageCount, activationLimit: activationLimit, activationUsage: activationUsage, licenseKey: licenseKey, onDeactivate: handleDeactivateLicense, isDeactivating: isDeactivating, showDetails: showDetails }))),
304
+ React.createElement("div", { className: "designbase-figma-page-license__section" },
305
+ React.createElement("div", { className: "designbase-figma-page-license__section-header" },
306
+ React.createElement("h3", null, t('enterLicenseKey') || '라이선스 키 입력'),
307
+ !isPaid && paymentPageUrl && (React.createElement(Button, { onClick: () => window.open(paymentPageUrl, '_blank'), variant: "primary", size: "s" },
308
+ React.createElement("span", null, t('purchaseLicense') || '라이선스 구매'),
309
+ React.createElement(ExternalLinkIcon, { size: 16 })))),
310
+ React.createElement(FormWithSubmit, { onLicenseSubmit: handleLicenseSubmit, disabled: false, isSubmitting: isSubmitting, value: licenseKey, onValueChange: setLicenseKey, label: t('licenseKey') || 'License Key', submitText: t('submit') || 'Submit', submittingText: t('verifying') || 'Verifying...' }),
311
+ React.createElement("p", { className: "designbase-figma-page-license__disclaimer" }, isPaid
312
+ ? t('licenseActivatedSuccess') || '라이선스가 성공적으로 활성화되었습니다.'
313
+ : t('enterLicenseFromEmail') || '구독 후 이메일로 받은 라이선스 키를 입력하세요.'))),
314
+ React.createElement(Button, { className: "designbase-figma-page-license__close", onPress: onClose, variant: "tertiary", size: "s", "aria-label": "Close" },
315
+ React.createElement(CloseIcon, { size: 20 }))));
316
+ };
317
+ PageLicense.displayName = 'PageLicense';
318
+
319
+ const UpgradeBanner = ({ onClick, isLoading = false, title, description, buttonText, t = (key) => key, className, }) => {
320
+ return (React.createElement("div", { className: `designbase-figma-upgrade-banner ${className || ''}` },
321
+ React.createElement("div", { className: "designbase-figma-upgrade-banner__content" },
322
+ React.createElement("div", { className: "designbase-figma-upgrade-banner__text-wrap" },
323
+ React.createElement("h3", { className: "designbase-figma-upgrade-banner__title" }, title || t('bannerTitle') || '프로로 업그레이드하세요'),
324
+ React.createElement("p", { className: "designbase-figma-upgrade-banner__text" }, description || t('bannerText') || '무제한 기능을 사용하고 더 많은 혜택을 누리세요.')),
325
+ React.createElement(Button, { onClick: onClick, variant: "primary", size: "m", disabled: isLoading }, buttonText || t('upgradeNow') || '지금 업그레이드'))));
326
+ };
327
+ UpgradeBanner.displayName = 'UpgradeBanner';
328
+
329
+ const ResizablePlugin = ({ children, minWidth = 360, maxWidth = 460, minHeight = 440, maxHeight = 700, icon, className, }) => {
330
+ const [isResizing, setIsResizing] = useState(false);
331
+ const resizeHandleRef = useRef(null);
332
+ useEffect(() => {
333
+ const handleMouseMove = (e) => {
334
+ if (!isResizing)
335
+ return;
336
+ const newWidth = Math.max(minWidth, Math.min(maxWidth, e.clientX + 10));
337
+ const newHeight = Math.max(minHeight, Math.min(maxHeight, e.clientY + 10));
338
+ if (typeof parent !== 'undefined') {
339
+ parent.postMessage({
340
+ pluginMessage: {
341
+ type: 'resize',
342
+ width: newWidth,
343
+ height: newHeight
344
+ }
345
+ }, '*');
346
+ }
347
+ };
348
+ const handleMouseUp = () => {
349
+ setIsResizing(false);
350
+ };
351
+ if (isResizing) {
352
+ window.addEventListener('mousemove', handleMouseMove);
353
+ window.addEventListener('mouseup', handleMouseUp);
354
+ }
355
+ return () => {
356
+ window.removeEventListener('mousemove', handleMouseMove);
357
+ window.removeEventListener('mouseup', handleMouseUp);
358
+ };
359
+ }, [isResizing, minWidth, maxWidth, minHeight, maxHeight]);
360
+ return (React.createElement("div", { className: `designbase-figma-resizable-plugin ${className || ''}` },
361
+ children,
362
+ React.createElement("div", { ref: resizeHandleRef, className: `designbase-figma-resizable-plugin__handle ${isResizing ? 'resizing' : ''}`, onMouseDown: (e) => {
363
+ e.preventDefault();
364
+ setIsResizing(true);
365
+ }, role: "button", tabIndex: 0, "aria-label": "Resize plugin" }, icon || React.createElement(ExpandIcon, { size: 12 }))));
366
+ };
367
+ ResizablePlugin.displayName = 'ResizablePlugin';
368
+
369
+ const InteractionFeedback = ({ status = 'default', message, statusMessage, leftContent, rightContent, visible = true, className, fixed = true, }) => {
370
+ const wrapperClasses = clsx('designbase-figma-interaction-wrapper', {
371
+ 'designbase-figma-interaction-wrapper--fixed': fixed,
372
+ });
373
+ const feedbackClasses = clsx('designbase-figma-interaction', `designbase-figma-interaction--${status}`, {
374
+ 'designbase-figma-interaction--visible': visible,
375
+ }, className);
376
+ if (!visible)
377
+ return null;
378
+ return (React.createElement("div", { className: wrapperClasses },
379
+ React.createElement("div", { className: feedbackClasses },
380
+ React.createElement("div", { className: "designbase-figma-interaction__content" },
381
+ React.createElement("div", { className: "designbase-figma-interaction__left" }, leftContent || (React.createElement("div", { className: "designbase-figma-interaction__message" },
382
+ message && (React.createElement("span", { className: "designbase-figma-interaction__text" }, message)),
383
+ statusMessage && (React.createElement("span", { className: "designbase-figma-interaction__status-text" }, statusMessage))))),
384
+ rightContent && (React.createElement("div", { className: "designbase-figma-interaction__right" }, rightContent))))));
385
+ };
386
+ InteractionFeedback.displayName = 'InteractionFeedback';
387
+
388
+ const SettingsModal = ({ isOpen, onClose, categories: initialCategories, categoryGroups, onSave, onReset, title = '목록 변경', description = '드래그하여 순서를 변경하거나, 토글하여 카테고리를 숨길 수 있습니다.', className, }) => {
389
+ const [tempCategories, setTempCategories] = useState(initialCategories);
390
+ const [draggedIndex, setDraggedIndex] = useState(null);
391
+ useEffect(() => {
392
+ setTempCategories(initialCategories);
393
+ }, [initialCategories]);
394
+ const handleDragStart = (e, index) => {
395
+ setDraggedIndex(index);
396
+ e.dataTransfer.effectAllowed = 'move';
397
+ e.dataTransfer.setData('text/plain', index.toString());
398
+ };
399
+ const handleDragOver = (e) => {
400
+ e.preventDefault();
401
+ e.dataTransfer.dropEffect = 'move';
402
+ };
403
+ const handleDrop = (e, dropIndex) => {
404
+ e.preventDefault();
405
+ const dragIndex = Number(e.dataTransfer.getData('text/plain'));
406
+ if (dragIndex === dropIndex) {
407
+ setDraggedIndex(null);
408
+ return;
409
+ }
410
+ const newCategories = [...tempCategories];
411
+ const [draggedItem] = newCategories.splice(dragIndex, 1);
412
+ const enabledCategories = newCategories.filter(cat => cat.enabled);
413
+ const disabledCategories = newCategories.filter(cat => !cat.enabled);
414
+ if (draggedItem.enabled) {
415
+ if (dropIndex <= enabledCategories.length) {
416
+ const adjustedDropIndex = dragIndex < dropIndex ? dropIndex - 1 : dropIndex;
417
+ enabledCategories.splice(adjustedDropIndex, 0, draggedItem);
418
+ }
419
+ else {
420
+ enabledCategories.push(draggedItem);
421
+ }
422
+ }
423
+ else {
424
+ if (dropIndex > enabledCategories.length) {
425
+ const disabledDropIndex = dropIndex - enabledCategories.length;
426
+ const adjustedDropIndex = dragIndex < dropIndex ? disabledDropIndex - 1 : disabledDropIndex;
427
+ disabledCategories.splice(adjustedDropIndex, 0, draggedItem);
428
+ }
429
+ else {
430
+ disabledCategories.unshift(draggedItem);
431
+ }
432
+ }
433
+ const reorderedCategories = [
434
+ ...enabledCategories.map((cat, idx) => ({ ...cat, order: idx })),
435
+ ...disabledCategories.map((cat, idx) => ({ ...cat, order: enabledCategories.length + idx }))
436
+ ];
437
+ setTempCategories(reorderedCategories);
438
+ setDraggedIndex(null);
439
+ };
440
+ const handleDragEnd = () => {
441
+ setDraggedIndex(null);
442
+ };
443
+ const handleToggle = (id) => {
444
+ setTempCategories(prev => {
445
+ const enabledCategories = prev.filter(cat => cat.id !== id && cat.enabled);
446
+ const disabledCategories = prev.filter(cat => cat.id !== id && !cat.enabled);
447
+ const targetCategory = prev.find(cat => cat.id === id);
448
+ if (!targetCategory)
449
+ return prev;
450
+ const newTargetCategory = { ...targetCategory, enabled: !targetCategory.enabled };
451
+ if (newTargetCategory.enabled) {
452
+ return [
453
+ ...enabledCategories,
454
+ newTargetCategory,
455
+ ...disabledCategories
456
+ ].map((cat, idx) => ({ ...cat, order: idx }));
457
+ }
458
+ else {
459
+ return [
460
+ ...enabledCategories,
461
+ ...disabledCategories,
462
+ newTargetCategory
463
+ ].map((cat, idx) => ({ ...cat, order: idx }));
464
+ }
465
+ });
466
+ };
467
+ const handleSave = () => {
468
+ onSave(tempCategories);
469
+ onClose();
470
+ };
471
+ const handleCancel = () => {
472
+ setTempCategories(initialCategories);
473
+ onClose();
474
+ };
475
+ const handleReset = () => {
476
+ if (onReset) {
477
+ onReset();
478
+ }
479
+ };
480
+ const modalClasses = clsx('designbase-figma-settings-modal', className);
481
+ return (React.createElement(Modal, { isOpen: isOpen, onClose: onClose, title: title, size: "m", className: modalClasses },
482
+ React.createElement("div", { className: "designbase-figma-settings-modal__content" },
483
+ description && (React.createElement("p", { className: "designbase-figma-settings-modal__description" }, description)),
484
+ React.createElement("ul", { className: "designbase-figma-settings-modal__list" }, tempCategories.map((category, index) => (React.createElement("li", { key: category.id, draggable: true, onDragStart: (e) => handleDragStart(e, index), onDragOver: handleDragOver, onDrop: (e) => handleDrop(e, index), onDragEnd: handleDragEnd, className: clsx('designbase-figma-settings-modal__item', {
485
+ 'designbase-figma-settings-modal__item--disabled': !category.enabled,
486
+ 'designbase-figma-settings-modal__item--dragging': draggedIndex === index,
487
+ }) },
488
+ React.createElement("div", { className: "designbase-figma-settings-modal__item-info" },
489
+ React.createElement("span", { className: "designbase-figma-settings-modal__drag-handle" },
490
+ React.createElement(GripVerticalIcon, { size: 16 })),
491
+ category.icon && (React.createElement("span", { className: "designbase-figma-settings-modal__category-icon" }, category.icon)),
492
+ React.createElement("span", { className: "designbase-figma-settings-modal__category-title" }, category.title)),
493
+ React.createElement(Toggle, { checked: category.enabled, onChange: () => handleToggle(category.id), size: "s" })))))),
494
+ React.createElement("div", { className: "designbase-figma-settings-modal__footer" },
495
+ onReset && (React.createElement(Button, { onPress: handleReset, variant: "tertiary", size: "s" }, "\uCD08\uAE30\uD654")),
496
+ React.createElement("div", { className: "designbase-figma-settings-modal__footer-right" },
497
+ React.createElement(Button, { onPress: handleCancel, variant: "tertiary", size: "s" }, "\uCDE8\uC18C"),
498
+ React.createElement(Button, { onPress: handleSave, variant: "primary", size: "s" }, "\uC800\uC7A5")))));
499
+ };
500
+ SettingsModal.displayName = 'SettingsModal';
501
+
502
+ const ProgressModal = ({ isOpen, onClose, progress, title = '작업 중', completedMessage = '완료되었습니다', processingMessage = '처리 중...', stopButtonText = '중지', confirmButtonText = '확인', helpText, onStop, onComplete, loadingIcon, completeIcon, className, }) => {
503
+ const [isCompleted, setIsCompleted] = useState(false);
504
+ const { processedNodes, totalNodes } = progress;
505
+ const progressPercentage = totalNodes > 0 ? (processedNodes / totalNodes) * 100 : 0;
506
+ useEffect(() => {
507
+ if (processedNodes === totalNodes && totalNodes > 0) {
508
+ setIsCompleted(true);
509
+ if (onComplete)
510
+ onComplete();
511
+ }
512
+ }, [processedNodes, totalNodes, onComplete]);
513
+ useEffect(() => {
514
+ if (isOpen)
515
+ setIsCompleted(false);
516
+ }, [isOpen]);
517
+ const handleStop = () => {
518
+ if (onStop)
519
+ onStop();
520
+ onClose();
521
+ };
522
+ const modalClasses = clsx('designbase-figma-progress-modal', className);
523
+ return (React.createElement(Modal, { isOpen: isOpen, onClose: isCompleted ? onClose : undefined, title: isCompleted ? completedMessage : title, size: "s", className: modalClasses, showCloseButton: false },
524
+ React.createElement("div", { className: "designbase-figma-progress-modal__content" },
525
+ React.createElement("div", { className: "designbase-figma-progress-modal__icon", "aria-hidden": "true" }, isCompleted ? (completeIcon || (React.createElement("div", { className: "designbase-figma-progress-modal__icon-success" },
526
+ React.createElement(CircleCheckFilledIcon, null)))) : (loadingIcon || (React.createElement("div", { className: "designbase-figma-progress-modal__icon-loading" },
527
+ React.createElement(Spinner, { size: "m" }))))),
528
+ React.createElement("p", { className: "designbase-figma-progress-modal__message" }, isCompleted ? completedMessage : processingMessage),
529
+ React.createElement("div", { className: "designbase-figma-progress-modal__status" }, `${processedNodes} / ${totalNodes} (${progressPercentage.toFixed(1)}%)`),
530
+ React.createElement(Progressbar, { value: progressPercentage, size: "m", variant: isCompleted ? 'success' : 'primary', style: "animated", fullWidth: true }),
531
+ helpText && !isCompleted && (React.createElement("p", { className: "designbase-figma-progress-modal__help-text" }, helpText)),
532
+ React.createElement("div", { className: "designbase-figma-progress-modal__actions" }, isCompleted ? (React.createElement(Button, { onPress: onClose, variant: "primary", size: "m", fullWidth: true }, confirmButtonText)) : (React.createElement(Button, { onPress: handleStop, variant: "secondary", size: "m", fullWidth: true }, stopButtonText))))));
533
+ };
534
+ ProgressModal.displayName = 'ProgressModal';
535
+
536
+ export { DonationBadge, Footer, FormWithSubmit, InteractionFeedback, LanguageSelector, LogoDropdown, PageLicense, PaymentBadge, PaymentStatusSection, PricingComparison, ProgressModal, ResizablePlugin, SettingsModal, UpgradeBanner };
54
537
  //# sourceMappingURL=index.esm.js.map