@designbasekorea/figma-ui 0.6.3 → 0.6.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/README.md +1 -1
- package/dist/index.css +1 -1
- package/dist/index.css.map +1 -1
- package/dist/index.d.ts +83 -14
- package/dist/index.esm.css +1 -1
- package/dist/index.esm.css.map +1 -1
- package/dist/index.esm.js +336 -127
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +356 -144
- package/dist/index.js.map +1 -1
- package/package.json +13 -6
package/dist/index.esm.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import React, { useRef, useState, useLayoutEffect, useEffect, createContext, useContext, useCallback } from 'react';
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import React, { useRef, useState, useLayoutEffect, useEffect, createContext, useContext, useCallback, useMemo } from 'react';
|
|
2
|
+
import { Button, Dropdown, Badge, Toggle, Spinner, SegmentControl, Logo, Toast, Input, Modal, Progressbar, RandomGradient } from '@designbasekorea/ui-wc/react';
|
|
3
|
+
export * from '@designbasekorea/ui-wc/react';
|
|
4
|
+
import { ChevronDownIcon, AppWindowIcon, YoutubeIcon, InstagramIcon, FigmaIcon, MailIcon, ExternalLinkIcon, MoreHorizontalIcon, CloseIcon, CircleCheckFilledIcon, ResizableIcon, GripVerticalIcon, ChevronRightIcon } from '@designbasekorea/icons';
|
|
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
|
|
|
@@ -11,10 +11,20 @@ function resolveText(t, input, fallback) {
|
|
|
11
11
|
if (typeof input === 'string') {
|
|
12
12
|
return input;
|
|
13
13
|
}
|
|
14
|
-
|
|
14
|
+
if (t) {
|
|
15
|
+
const translated = t(input.key, input.values);
|
|
16
|
+
if (translated && translated !== input.key) {
|
|
17
|
+
return translated;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return fallback ?? input.key;
|
|
15
21
|
}
|
|
16
22
|
|
|
17
|
-
const
|
|
23
|
+
const DONATION_ICON_NAMES = {
|
|
24
|
+
heart: 'star-filled',
|
|
25
|
+
coffee: 'cart',
|
|
26
|
+
};
|
|
27
|
+
const DonationBadge = ({ donationUrl = 'https://buymeacoffee.com/designbase', text = { key: 'donation.buy_me_a_coffee' }, iconType = 'heart', size = 's', variant = 'tertiary', fullWidth = false, iconOnly = false, disabled = false, showPrompt = false, promptText = { key: 'donation.prompt' }, className, onClick, t }) => {
|
|
18
28
|
const handleClick = () => {
|
|
19
29
|
if (onClick) {
|
|
20
30
|
onClick();
|
|
@@ -23,15 +33,12 @@ const DonationBadge = ({ donationUrl = 'https://buymeacoffee.com/designbase', te
|
|
|
23
33
|
window.open(donationUrl, '_blank', 'noopener,noreferrer');
|
|
24
34
|
}
|
|
25
35
|
};
|
|
26
|
-
const Icon = iconType === 'heart' ? HeartFilledIcon : CoffeeFilledIcon;
|
|
27
36
|
const displayText = resolveText(t, text, 'Buy me a coffee');
|
|
28
37
|
const displayPromptText = resolveText(t, promptText, '도움이 되었다면 커피 한 잔 사주세요!');
|
|
29
38
|
const buttonSize = size;
|
|
30
39
|
return (React.createElement("div", { className: clsx('designbase-figma-donation-badge-container', className) },
|
|
31
|
-
showPrompt && (React.createElement("div", { className: "designbase-figma-donation-badge__prompt" }, displayPromptText)),
|
|
32
|
-
React.createElement(Button, { variant: variant, size: buttonSize, fullWidth: fullWidth, disabled: disabled, onPress: handleClick, startIcon:
|
|
33
|
-
'--designbase-figma-donation-badge-icon-color': 'var(--db-brand-primary)'
|
|
34
|
-
} }, displayText)));
|
|
40
|
+
showPrompt && !iconOnly && (React.createElement("div", { className: "designbase-figma-donation-badge__prompt" }, displayPromptText)),
|
|
41
|
+
React.createElement(Button, { variant: variant, size: buttonSize, fullWidth: fullWidth, disabled: disabled, onPress: handleClick, iconOnly: iconOnly, startIcon: DONATION_ICON_NAMES[iconType], className: clsx('designbase-figma-donation-badge', `designbase-figma-donation-badge--${iconType}`), "aria-label": displayText }, iconOnly ? undefined : displayText)));
|
|
35
42
|
};
|
|
36
43
|
DonationBadge.displayName = 'DonationBadge';
|
|
37
44
|
|
|
@@ -69,10 +76,11 @@ const FigmaHeader = ({ children, actions = [], searchBar, sticky = true, classNa
|
|
|
69
76
|
'designbase-figma-header--with-search': !!searchBar,
|
|
70
77
|
'designbase-figma-header--with-actions': actions.length > 0,
|
|
71
78
|
}, className);
|
|
79
|
+
const hasToolbar = !!children || actions.length > 0;
|
|
72
80
|
return (React.createElement("header", { className: classes },
|
|
73
|
-
React.createElement("div", { className: "designbase-figma-header__container" },
|
|
81
|
+
hasToolbar && (React.createElement("div", { className: "designbase-figma-header__container" },
|
|
74
82
|
children && (React.createElement("div", { className: "designbase-figma-header__center" }, children)),
|
|
75
|
-
actions.length > 0 && (React.createElement("div", { className: "designbase-figma-header__right" }, actions.map((action, index) => (React.createElement(Button, { key: index
|
|
83
|
+
actions.length > 0 && (React.createElement("div", { className: "designbase-figma-header__right" }, actions.map((action, index) => (React.createElement(Button, { key: `${action.startIcon}-${index}`, variant: "tertiary", size: "s", iconOnly: true, startIcon: action.startIcon, "aria-label": action.ariaLabel, onPress: action.onPress }))))))),
|
|
76
84
|
searchBar && (React.createElement("div", { className: "designbase-figma-header__search-container" }, searchBar))));
|
|
77
85
|
};
|
|
78
86
|
FigmaHeader.displayName = 'FigmaHeader';
|
|
@@ -81,22 +89,24 @@ const FigmaPageHead = ({ onBack, title, description, actions = [], moreActions =
|
|
|
81
89
|
const classes = clsx('designbase-figma-page-head', className);
|
|
82
90
|
const displayTitle = resolveText(t, title);
|
|
83
91
|
const displayDescription = description ? resolveText(t, description) : undefined;
|
|
84
|
-
const dropdownItems = moreActions.map(action => ({
|
|
92
|
+
const dropdownItems = moreActions.map((action) => ({
|
|
85
93
|
id: action.id,
|
|
86
94
|
label: resolveText(t, action.label),
|
|
87
|
-
onClick: action.onClick
|
|
88
95
|
}));
|
|
96
|
+
const handleDropdownChange = (event) => {
|
|
97
|
+
const actionId = event.detail?.id ?? event.detail?.value;
|
|
98
|
+
const action = moreActions.find((item) => item.id === actionId);
|
|
99
|
+
action?.onClick();
|
|
100
|
+
};
|
|
89
101
|
return (React.createElement("div", { className: classes },
|
|
90
102
|
React.createElement("div", { className: "designbase-figma-page-head__left" },
|
|
91
|
-
React.createElement(Button, { variant: "tertiary", size: "s", iconOnly: true, onPress: onBack, "aria-label": "\uB4A4\uB85C\uAC00\uAE30" },
|
|
92
|
-
React.createElement(ChevronLeftIcon, { size: 14 })),
|
|
103
|
+
React.createElement(Button, { variant: "tertiary", size: "s", iconOnly: true, startIcon: "chevron-left", onPress: onBack, "aria-label": "\uB4A4\uB85C\uAC00\uAE30" }),
|
|
93
104
|
React.createElement("div", { className: "designbase-figma-page-head__titles" },
|
|
94
105
|
React.createElement("h2", null, displayTitle),
|
|
95
106
|
displayDescription && React.createElement("p", null, displayDescription))),
|
|
96
107
|
React.createElement("div", { className: "designbase-figma-page-head__actions" },
|
|
97
108
|
actions.map((action, index) => (React.createElement(React.Fragment, { key: index }, action))),
|
|
98
|
-
moreActions.length > 0 && (React.createElement(Dropdown, {
|
|
99
|
-
React.createElement(MoreHorizontalIcon, { size: 16 })), items: dropdownItems, placement: "bottom-right", size: "s" })))));
|
|
109
|
+
moreActions.length > 0 && (React.createElement(Dropdown, { items: dropdownItems, triggerIcon: "more-horizontal", iconOnly: true, triggerVariant: "tertiary", placement: "bottom-right", size: "s", onChange: handleDropdownChange })))));
|
|
100
110
|
};
|
|
101
111
|
FigmaPageHead.displayName = 'FigmaPageHead';
|
|
102
112
|
|
|
@@ -413,10 +423,15 @@ const LanguageSelector = ({ currentLanguage, languages = DEFAULT_LANGUAGES, onLa
|
|
|
413
423
|
const segmentOptions = languages.map((language) => ({
|
|
414
424
|
value: language.code,
|
|
415
425
|
label: resolveText(t, language.label),
|
|
416
|
-
disabled: false
|
|
426
|
+
disabled: false,
|
|
417
427
|
}));
|
|
418
428
|
return (React.createElement("div", { className: `designbase-figma-language-selector ${className || ''}` },
|
|
419
|
-
React.createElement(SegmentControl, { options: segmentOptions, value: activeLanguage, onChange:
|
|
429
|
+
React.createElement(SegmentControl, { options: segmentOptions, value: activeLanguage, onChange: (event) => {
|
|
430
|
+
const value = event.detail?.value;
|
|
431
|
+
if (value) {
|
|
432
|
+
handleLanguageChange(value);
|
|
433
|
+
}
|
|
434
|
+
}, size: size, className: "designbase-figma-language-selector__segment" })));
|
|
420
435
|
};
|
|
421
436
|
LanguageSelector.displayName = 'LanguageSelector';
|
|
422
437
|
|
|
@@ -506,22 +521,29 @@ const FigmaFooter = ({ onLicensePageClick, paymentStatus = 'FREE', usageCount =
|
|
|
506
521
|
React.createElement("div", { className: "designbase-figma-footer__left" },
|
|
507
522
|
React.createElement(LogoDropdown, { logoSrc: logoSrc, logoAlt: logoAlt, logoType: logoType, logoSize: logoSize, links: logoLinks, t: t }),
|
|
508
523
|
showLanguageSelector && (React.createElement(LanguageSelector, { currentLanguage: currentLanguage, languages: languages, onLanguageChange: onLanguageChange, size: "s" }))),
|
|
509
|
-
|
|
510
|
-
resolvedShowUsageInfo && (React.createElement("div", { className: "designbase-figma-footer__usage-info" }, isActive ? (React.createElement("span", { className: "designbase-figma-footer__unlimited-usage designbase-figma-footer__unlimited-usage--tooltip", title: t('unlimitedUsageTooltip') }, t('unlimitedUsage'))) : (React.createElement(
|
|
511
|
-
React.createElement("span", { className: "designbase-figma-footer__usage-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
showDonation && (React.createElement("div", { className: "designbase-figma-footer__donation" },
|
|
520
|
-
React.createElement(DonationBadge, { donationUrl: donationUrl, text: donationText, iconType: "heart", size: "s", showPrompt: showDonationText && !isActive, promptText: donationPromptText, t: t })))),
|
|
524
|
+
React.createElement("div", { className: "designbase-figma-footer__right" },
|
|
525
|
+
showPaymentStatus && resolvedShowUsageInfo && (React.createElement("div", { className: "designbase-figma-footer__usage-info" }, isActive ? (React.createElement("span", { className: "designbase-figma-footer__unlimited-usage designbase-figma-footer__unlimited-usage--tooltip", title: t('unlimitedUsageTooltip') }, t('unlimitedUsage'))) : (React.createElement("span", { className: "designbase-figma-footer__usage-info designbase-figma-footer__usage-info--tooltip", title: t('resetsDaily') },
|
|
526
|
+
React.createElement("span", { className: "designbase-figma-footer__usage-count" }, usageCount),
|
|
527
|
+
React.createElement("span", { className: "designbase-figma-footer__max-daily-usage" },
|
|
528
|
+
"/",
|
|
529
|
+
maxDailyUsage,
|
|
530
|
+
" ",
|
|
531
|
+
t('perDay')))))),
|
|
532
|
+
showPaymentStatus && resolvedShowPaymentBadge && (React.createElement(PaymentBadge, { isActive: isActive, onClick: isBeta ? undefined : onLicensePageClick, isLoading: isLoading, text: isBeta ? (t('betaStatus') || 'BETA') : undefined, t: t })),
|
|
533
|
+
showDonation && (React.createElement(DonationBadge, { donationUrl: donationUrl, text: donationText, iconType: "heart", size: "s", iconOnly: true, showPrompt: false, t: t })))),
|
|
521
534
|
children));
|
|
522
535
|
};
|
|
523
536
|
FigmaFooter.displayName = 'FigmaFooter';
|
|
524
537
|
|
|
538
|
+
const FigmaToastStack = ({ toasts, position, maxToasts, onRemoveToast, }) => {
|
|
539
|
+
const visibleToasts = [...toasts]
|
|
540
|
+
.sort((a, b) => b.createdAt - a.createdAt)
|
|
541
|
+
.slice(0, maxToasts);
|
|
542
|
+
if (visibleToasts.length === 0)
|
|
543
|
+
return null;
|
|
544
|
+
return (React.createElement("div", { className: `figma-toast-container figma-toast-container--${position}` }, visibleToasts.map((toast) => (React.createElement("div", { key: toast.id, className: "figma-toast-container__item" },
|
|
545
|
+
React.createElement(Toast, { toastId: toast.id, title: toast.title, description: toast.description, status: toast.status, duration: toast.duration, showProgress: toast.showProgress, showClose: toast.showCloseButton, onClose: () => onRemoveToast(toast.id) }))))));
|
|
546
|
+
};
|
|
525
547
|
const ToastContext = createContext(null);
|
|
526
548
|
const useToast = () => {
|
|
527
549
|
const context = useContext(ToastContext);
|
|
@@ -539,13 +561,13 @@ const FigmaToastProvider = ({ children, position = 'top-right', maxToasts = 5, }
|
|
|
539
561
|
id,
|
|
540
562
|
createdAt: Date.now(),
|
|
541
563
|
};
|
|
542
|
-
setToasts(prev => {
|
|
564
|
+
setToasts((prev) => {
|
|
543
565
|
const updated = [newToast, ...prev];
|
|
544
566
|
return updated.slice(0, maxToasts);
|
|
545
567
|
});
|
|
546
568
|
}, [maxToasts]);
|
|
547
569
|
const removeToast = useCallback((id) => {
|
|
548
|
-
setToasts(prev => prev.filter(toast => toast.id !== id));
|
|
570
|
+
setToasts((prev) => prev.filter((toast) => toast.id !== id));
|
|
549
571
|
}, []);
|
|
550
572
|
const showToast = useCallback((message, options = {}) => {
|
|
551
573
|
addToast({
|
|
@@ -564,17 +586,17 @@ const FigmaToastProvider = ({ children, position = 'top-right', maxToasts = 5, }
|
|
|
564
586
|
};
|
|
565
587
|
return (React.createElement(ToastContext.Provider, { value: contextValue },
|
|
566
588
|
children,
|
|
567
|
-
React.createElement(
|
|
589
|
+
React.createElement(FigmaToastStack, { toasts: toasts, onRemoveToast: removeToast, position: position, maxToasts: maxToasts })));
|
|
568
590
|
};
|
|
569
591
|
const FigmaToast = ({ message, isVisible, onClose, type = 'info', duration = 3000, className, }) => {
|
|
570
592
|
if (!isVisible)
|
|
571
593
|
return null;
|
|
572
594
|
return (React.createElement("div", { className: `figma-toast-fixed-wrap ${className || ''}` },
|
|
573
|
-
React.createElement(Toast, {
|
|
595
|
+
React.createElement(Toast, { toastId: "figma-toast-simple", title: message, status: type, duration: duration, showProgress: false, showClose: true, onClose: onClose })));
|
|
574
596
|
};
|
|
575
597
|
const useFigmaToast = () => {
|
|
576
598
|
const [toast, setToast] = useState({ message: '', isVisible: false });
|
|
577
|
-
const showToast = useCallback((message,
|
|
599
|
+
const showToast = useCallback((message, _type = 'info') => {
|
|
578
600
|
setToast({ message, isVisible: true });
|
|
579
601
|
setTimeout(() => setToast({ message: '', isVisible: false }), 5000);
|
|
580
602
|
}, []);
|
|
@@ -605,7 +627,12 @@ const FormWithSubmit = ({ onLicenseSubmit, disabled = false, isSubmitting = fals
|
|
|
605
627
|
return (React.createElement("form", { className: `designbase-figma-form-with-submit ${className || ''}`, onSubmit: handleSubmit },
|
|
606
628
|
React.createElement("div", { className: "designbase-figma-form-with-submit__field" },
|
|
607
629
|
React.createElement("label", { className: "designbase-figma-form-with-submit__label" }, displayLabel),
|
|
608
|
-
React.createElement(Input, { value: inputValue, onChange:
|
|
630
|
+
React.createElement(Input, { value: inputValue, onChange: (event) => {
|
|
631
|
+
const value = event.detail?.value;
|
|
632
|
+
if (value !== undefined) {
|
|
633
|
+
handleChange(value);
|
|
634
|
+
}
|
|
635
|
+
}, placeholder: displayPlaceholder, helperText: displayHelperText, disabled: disabled || isSubmitting, size: "m" })),
|
|
609
636
|
React.createElement(Button, { type: "submit", variant: "primary", size: "m", loading: isSubmitting, disabled: !inputValue.trim() || isSubmitting || disabled, fullWidth: true }, isSubmitting ? displaySubmittingText : displaySubmitText)));
|
|
610
637
|
};
|
|
611
638
|
FormWithSubmit.displayName = 'FormWithSubmit';
|
|
@@ -658,18 +685,17 @@ const PricingComparison = ({ features = defaultFeatures, pricing = defaultPricin
|
|
|
658
685
|
};
|
|
659
686
|
return (React.createElement("div", { className: `designbase-figma-pricing-comparison ${className || ''}` },
|
|
660
687
|
paymentType === 'subscription' && (React.createElement("div", { className: "designbase-figma-pricing-comparison__billing-toggle" },
|
|
661
|
-
React.createElement(SegmentControl, { value: isYearly ? 'yearly' : 'monthly', onChange: (
|
|
688
|
+
React.createElement(SegmentControl, { value: isYearly ? 'yearly' : 'monthly', onChange: (event) => {
|
|
689
|
+
const value = event.detail?.value;
|
|
690
|
+
setIsYearly(value === 'yearly');
|
|
691
|
+
}, options: [
|
|
662
692
|
{
|
|
663
693
|
value: 'monthly',
|
|
664
694
|
label: t('monthlyBilling') || '월간 결제',
|
|
665
695
|
},
|
|
666
696
|
{
|
|
667
697
|
value: 'yearly',
|
|
668
|
-
label: (
|
|
669
|
-
t('yearlyBilling') || '연간 결제',
|
|
670
|
-
React.createElement("span", { className: "designbase-figma-pricing-comparison__discount-badge" },
|
|
671
|
-
getDiscountPercentage(),
|
|
672
|
-
"% \uC808\uC57D"))),
|
|
698
|
+
label: `${t('yearlyBilling') || '연간 결제'} (${getDiscountPercentage()}% 절약)`,
|
|
673
699
|
},
|
|
674
700
|
], size: "s" }))),
|
|
675
701
|
paymentType === 'lifetime' && (React.createElement("div", { className: "designbase-figma-pricing-comparison__lifetime-info" },
|
|
@@ -697,123 +723,306 @@ const PricingComparison = ({ features = defaultFeatures, pricing = defaultPricin
|
|
|
697
723
|
};
|
|
698
724
|
PricingComparison.displayName = 'PricingComparison';
|
|
699
725
|
|
|
700
|
-
|
|
701
|
-
|
|
726
|
+
function postPluginMessage(message) {
|
|
727
|
+
if (typeof parent === 'undefined') {
|
|
728
|
+
return;
|
|
729
|
+
}
|
|
730
|
+
parent.postMessage({ pluginMessage: message }, '*');
|
|
731
|
+
}
|
|
732
|
+
function openExternalUrl(url) {
|
|
733
|
+
if (!url)
|
|
734
|
+
return;
|
|
735
|
+
postPluginMessage({ type: 'open-external-url', url });
|
|
736
|
+
if (typeof window !== 'undefined') {
|
|
737
|
+
window.open(url, '_blank', 'noopener,noreferrer');
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
const DEFAULT_LICENSE_MESSAGE_TYPES = {
|
|
742
|
+
initialize: 'initialize',
|
|
743
|
+
verifyLicense: 'verify-license',
|
|
744
|
+
deactivateLicense: 'deactivate-license',
|
|
745
|
+
updateStatus: 'update-plugin-status',
|
|
746
|
+
verifyResult: 'license-verification-result',
|
|
747
|
+
deactivateResult: 'license-deactivation-result',
|
|
748
|
+
};
|
|
749
|
+
const START_DESIGNBASE_AUTH_MESSAGE = 'start-designbase-auth';
|
|
750
|
+
const INITIAL_LICENSE_PAGE_STATE = {
|
|
751
|
+
paymentStatus: 'FREE',
|
|
752
|
+
licenseKey: '',
|
|
753
|
+
planTier: 'FREE',
|
|
754
|
+
hasLicenseKey: false,
|
|
755
|
+
isLicenseRegistered: false,
|
|
756
|
+
activationLimit: 0,
|
|
757
|
+
activationUsage: 0,
|
|
758
|
+
usageCount: 0,
|
|
759
|
+
remainingUsage: 0,
|
|
760
|
+
usageUsed: 0,
|
|
761
|
+
usageLimit: 0,
|
|
762
|
+
usageReset: 'none',
|
|
763
|
+
canUseFeature: true,
|
|
764
|
+
};
|
|
765
|
+
|
|
766
|
+
function readString(data, keys) {
|
|
767
|
+
for (const key of keys) {
|
|
768
|
+
const value = data[key];
|
|
769
|
+
if (typeof value === 'string') {
|
|
770
|
+
return value;
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
return '';
|
|
774
|
+
}
|
|
775
|
+
function readNumber(data, keys) {
|
|
776
|
+
for (const key of keys) {
|
|
777
|
+
const value = data[key];
|
|
778
|
+
if (typeof value === 'number' && Number.isFinite(value)) {
|
|
779
|
+
return value;
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
return undefined;
|
|
783
|
+
}
|
|
784
|
+
function readBoolean(data, keys) {
|
|
785
|
+
for (const key of keys) {
|
|
786
|
+
const value = data[key];
|
|
787
|
+
if (typeof value === 'boolean') {
|
|
788
|
+
return value;
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
return undefined;
|
|
792
|
+
}
|
|
793
|
+
function parseLicenseMessage(data) {
|
|
794
|
+
const paymentStatus = readString(data, ['paymentStatus', 'status']);
|
|
795
|
+
const usageLimitRaw = data.usageLimit;
|
|
796
|
+
return {
|
|
797
|
+
...(paymentStatus ? { paymentStatus } : {}),
|
|
798
|
+
licenseKey: readString(data, ['licenseKey']) || undefined,
|
|
799
|
+
planTier: readString(data, ['planTier']) || undefined,
|
|
800
|
+
hasLicenseKey: readBoolean(data, ['hasLicenseKey']),
|
|
801
|
+
isLicenseRegistered: readBoolean(data, ['isLicenseRegistered']),
|
|
802
|
+
activationLimit: readNumber(data, ['activationLimit']),
|
|
803
|
+
activationUsage: readNumber(data, ['activationUsage']),
|
|
804
|
+
usageCount: readNumber(data, ['usageCount']),
|
|
805
|
+
remainingUsage: readNumber(data, ['remainingUsage']),
|
|
806
|
+
usageUsed: readNumber(data, ['usageUsed']),
|
|
807
|
+
usageLimit: usageLimitRaw === 'unlimited' ? 'unlimited' : readNumber(data, ['usageLimit']),
|
|
808
|
+
usageReset: readString(data, ['usageReset']) || undefined,
|
|
809
|
+
canUseFeature: readBoolean(data, ['canUseFeature']),
|
|
810
|
+
usageReason: readString(data, ['usageReason']) || undefined,
|
|
811
|
+
lastMessage: readString(data, ['message']) || undefined,
|
|
812
|
+
lastErrorCode: readString(data, ['errorCode']) || undefined,
|
|
813
|
+
};
|
|
814
|
+
}
|
|
815
|
+
function useFigmaLicenseBridge(options = {}) {
|
|
816
|
+
const messageTypes = useMemo(() => ({ ...DEFAULT_LICENSE_MESSAGE_TYPES, ...options.messageTypes }), [options.messageTypes]);
|
|
817
|
+
const [state, setState] = useState({
|
|
818
|
+
...INITIAL_LICENSE_PAGE_STATE,
|
|
819
|
+
...options.initialState,
|
|
820
|
+
});
|
|
821
|
+
const [isVerifying, setIsVerifying] = useState(false);
|
|
702
822
|
const [isDeactivating, setIsDeactivating] = useState(false);
|
|
703
|
-
const [
|
|
704
|
-
const
|
|
705
|
-
|
|
706
|
-
const
|
|
707
|
-
|
|
708
|
-
|
|
823
|
+
const [isStartingAuth, setIsStartingAuth] = useState(false);
|
|
824
|
+
const optionsRef = useRef(options);
|
|
825
|
+
optionsRef.current = options;
|
|
826
|
+
const applyPatch = useCallback((patch) => {
|
|
827
|
+
setState((prev) => {
|
|
828
|
+
const next = {
|
|
829
|
+
...prev,
|
|
830
|
+
...Object.fromEntries(Object.entries(patch).filter(([, value]) => value !== undefined)),
|
|
831
|
+
};
|
|
832
|
+
optionsRef.current.onStateChange?.(next);
|
|
833
|
+
if (patch.paymentStatus) {
|
|
834
|
+
optionsRef.current.onPaymentStatusChange?.(patch.paymentStatus);
|
|
835
|
+
}
|
|
836
|
+
if (patch.usageCount !== undefined) {
|
|
837
|
+
optionsRef.current.onUsageCountChange?.(patch.usageCount);
|
|
838
|
+
}
|
|
839
|
+
return next;
|
|
840
|
+
});
|
|
841
|
+
}, []);
|
|
709
842
|
useEffect(() => {
|
|
710
843
|
const handleMessage = (event) => {
|
|
711
|
-
const
|
|
712
|
-
if (
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
setActivationLimit(activationLimit);
|
|
719
|
-
if (activationUsage !== undefined)
|
|
720
|
-
setActivationUsage(activationUsage);
|
|
721
|
-
if (paymentStatus)
|
|
722
|
-
setPaymentStatus(paymentStatus);
|
|
723
|
-
if (usageCount !== undefined) {
|
|
724
|
-
setLocalUsageCount(usageCount);
|
|
725
|
-
setUsageCount(usageCount);
|
|
726
|
-
}
|
|
844
|
+
const data = event.data?.pluginMessage;
|
|
845
|
+
if (!data || typeof data !== 'object')
|
|
846
|
+
return;
|
|
847
|
+
const payload = data;
|
|
848
|
+
const type = typeof payload.type === 'string' ? payload.type : '';
|
|
849
|
+
if (type === messageTypes.updateStatus) {
|
|
850
|
+
applyPatch(parseLicenseMessage(payload));
|
|
727
851
|
}
|
|
728
|
-
if (type ===
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
if (success) {
|
|
733
|
-
|
|
734
|
-
setPaymentStatus('PAID');
|
|
852
|
+
if (type === messageTypes.verifyResult) {
|
|
853
|
+
setIsVerifying(false);
|
|
854
|
+
setIsStartingAuth(false);
|
|
855
|
+
applyPatch(parseLicenseMessage(payload));
|
|
856
|
+
if (payload.success === true) {
|
|
857
|
+
applyPatch({ paymentStatus: 'PAID', isLicenseRegistered: true });
|
|
735
858
|
}
|
|
736
859
|
}
|
|
737
|
-
if (type ===
|
|
860
|
+
if (type === messageTypes.deactivateResult) {
|
|
738
861
|
setIsDeactivating(false);
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
862
|
+
applyPatch(parseLicenseMessage(payload));
|
|
863
|
+
if (payload.success === true) {
|
|
864
|
+
applyPatch({
|
|
865
|
+
paymentStatus: 'UNPAID',
|
|
866
|
+
licenseKey: '',
|
|
867
|
+
hasLicenseKey: false,
|
|
868
|
+
isLicenseRegistered: false,
|
|
869
|
+
activationLimit: 0,
|
|
870
|
+
activationUsage: 0,
|
|
871
|
+
});
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
if (type === 'designbase-auth-result') {
|
|
875
|
+
setIsStartingAuth(false);
|
|
876
|
+
if (payload.success === true) {
|
|
877
|
+
applyPatch(parseLicenseMessage(payload));
|
|
878
|
+
}
|
|
879
|
+
else if (typeof payload.message === 'string') {
|
|
880
|
+
applyPatch({ lastMessage: payload.message });
|
|
748
881
|
}
|
|
749
882
|
}
|
|
750
883
|
};
|
|
751
884
|
window.addEventListener('message', handleMessage);
|
|
752
|
-
|
|
753
|
-
parent.postMessage({ pluginMessage: { type: 'initialize' } }, '*');
|
|
754
|
-
}
|
|
885
|
+
postPluginMessage({ type: messageTypes.initialize });
|
|
755
886
|
return () => window.removeEventListener('message', handleMessage);
|
|
756
|
-
}, [
|
|
887
|
+
}, [applyPatch, messageTypes]);
|
|
888
|
+
const verifyLicense = useCallback((licenseKey) => {
|
|
889
|
+
setIsVerifying(true);
|
|
890
|
+
postPluginMessage({
|
|
891
|
+
type: messageTypes.verifyLicense,
|
|
892
|
+
licenseKey,
|
|
893
|
+
});
|
|
894
|
+
}, [messageTypes.verifyLicense]);
|
|
895
|
+
const deactivateLicense = useCallback(() => {
|
|
896
|
+
setIsDeactivating(true);
|
|
897
|
+
postPluginMessage({ type: messageTypes.deactivateLicense });
|
|
898
|
+
}, [messageTypes.deactivateLicense]);
|
|
899
|
+
const startDesignbaseAuth = useCallback(() => {
|
|
900
|
+
setIsStartingAuth(true);
|
|
901
|
+
postPluginMessage({ type: START_DESIGNBASE_AUTH_MESSAGE });
|
|
902
|
+
}, []);
|
|
903
|
+
return {
|
|
904
|
+
state,
|
|
905
|
+
isVerifying,
|
|
906
|
+
isDeactivating,
|
|
907
|
+
isStartingAuth,
|
|
908
|
+
verifyLicense,
|
|
909
|
+
deactivateLicense,
|
|
910
|
+
startDesignbaseAuth,
|
|
911
|
+
setLicenseKeyInput: (licenseKey) => applyPatch({ licenseKey }),
|
|
912
|
+
};
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
function formatUsageLabel(state, t) {
|
|
916
|
+
const { remainingUsage, usageUsed, usageLimit, usageReset } = state;
|
|
917
|
+
if (usageLimit === 'unlimited') {
|
|
918
|
+
return resolveText(t, { key: 'license.usage_unlimited' }, '사용 횟수 제한 없음');
|
|
919
|
+
}
|
|
920
|
+
const limit = typeof usageLimit === 'number' ? usageLimit : remainingUsage + usageUsed;
|
|
921
|
+
const remaining = remainingUsage ?? Math.max(0, limit - usageUsed);
|
|
922
|
+
if (usageReset === 'daily') {
|
|
923
|
+
const fallback = `오늘 남은 횟수 ${remaining}/${limit}`;
|
|
924
|
+
const translated = resolveText(t, { key: 'license.usage_daily' }, fallback);
|
|
925
|
+
return translated.includes(String(remaining)) ? translated : fallback;
|
|
926
|
+
}
|
|
927
|
+
if (usageReset === 'monthly') {
|
|
928
|
+
const fallback = `이번 달 남은 횟수 ${remaining}/${limit}`;
|
|
929
|
+
const translated = resolveText(t, { key: 'license.usage_monthly' }, fallback);
|
|
930
|
+
return translated.includes(String(remaining)) ? translated : fallback;
|
|
931
|
+
}
|
|
932
|
+
return `남은 횟수 ${remaining}/${limit}`;
|
|
933
|
+
}
|
|
934
|
+
const PageLicense = ({ status: initialStatus, onClose, licenseKey: initialLicenseKey = '', wordpressLinks, paymentPageUrl, enableAccountAuth = true, showLicenseKeyInput = true, pricingFeatures, pricing, paymentType = 'subscription', messageTypes, onStateChange, setPaymentStatus, setUsageCount, onLicenseSubmit, usageCount: initialUsageCount = 0, t, className, }) => {
|
|
935
|
+
const links = useMemo(() => ({
|
|
936
|
+
purchaseUrl: wordpressLinks?.purchaseUrl ?? paymentPageUrl,
|
|
937
|
+
accountUrl: wordpressLinks?.accountUrl,
|
|
938
|
+
helpUrl: wordpressLinks?.helpUrl,
|
|
939
|
+
}), [wordpressLinks, paymentPageUrl]);
|
|
940
|
+
const { state, isVerifying, isDeactivating, isStartingAuth, verifyLicense, deactivateLicense, startDesignbaseAuth, setLicenseKeyInput, } = useFigmaLicenseBridge({
|
|
941
|
+
messageTypes,
|
|
942
|
+
initialState: {
|
|
943
|
+
paymentStatus: initialStatus,
|
|
944
|
+
licenseKey: initialLicenseKey,
|
|
945
|
+
usageCount: initialUsageCount,
|
|
946
|
+
},
|
|
947
|
+
onStateChange,
|
|
948
|
+
onPaymentStatusChange: setPaymentStatus,
|
|
949
|
+
onUsageCountChange: setUsageCount,
|
|
950
|
+
});
|
|
951
|
+
const [showDetails, setShowDetails] = useState(false);
|
|
952
|
+
const [licenseKeyDraft, setLicenseKeyDraft] = useState(initialLicenseKey);
|
|
953
|
+
const isPaid = state.isLicenseRegistered || state.paymentStatus === 'PAID';
|
|
954
|
+
const usageLabel = formatUsageLabel(state, t);
|
|
757
955
|
const handleLicenseSubmit = async (submittedLicenseKey) => {
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
pluginMessage: {
|
|
762
|
-
type: 'verify-license',
|
|
763
|
-
licenseKey: submittedLicenseKey
|
|
764
|
-
}
|
|
765
|
-
}, '*');
|
|
956
|
+
if (onLicenseSubmit) {
|
|
957
|
+
await onLicenseSubmit(submittedLicenseKey);
|
|
958
|
+
return;
|
|
766
959
|
}
|
|
960
|
+
verifyLicense(submittedLicenseKey);
|
|
767
961
|
};
|
|
768
|
-
const
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
parent.postMessage({
|
|
772
|
-
pluginMessage: { type: 'deactivate-license' }
|
|
773
|
-
}, '*');
|
|
774
|
-
}
|
|
962
|
+
const handleLicenseKeyChange = (value) => {
|
|
963
|
+
setLicenseKeyDraft(value);
|
|
964
|
+
setLicenseKeyInput(value);
|
|
775
965
|
};
|
|
776
|
-
const isPaid = status === 'PAID';
|
|
777
966
|
return (React.createElement("div", { className: `designbase-figma-page-license ${className || ''}` },
|
|
778
967
|
React.createElement("div", { className: "designbase-figma-page-license__content" },
|
|
779
968
|
React.createElement("div", { className: "designbase-figma-page-license__header" },
|
|
780
969
|
React.createElement("div", { className: "designbase-figma-page-license__title" },
|
|
781
|
-
React.createElement("h2", null, isPaid
|
|
970
|
+
React.createElement("h2", null, isPaid
|
|
971
|
+
? resolveText(t, { key: 'license.pro_account' }, '프로 계정')
|
|
972
|
+
: resolveText(t, { key: 'license.upgrade_to_pro' }, '프로로 업그레이드')),
|
|
973
|
+
state.planTier && state.planTier !== 'FREE' && (React.createElement("span", { className: "designbase-figma-page-license__tier" }, state.planTier))),
|
|
782
974
|
React.createElement("p", { className: "designbase-figma-page-license__description" }, isPaid
|
|
783
975
|
? resolveText(t, { key: 'license.all_features_available' }, '모든 기능을 사용할 수 있습니다.')
|
|
784
|
-
: resolveText(t, { key: 'license.purchase_for_unlimited' }, '
|
|
976
|
+
: resolveText(t, { key: 'license.purchase_for_unlimited' }, '디자인베이스 멤버십 또는 라이선스로 무제한 이용이 가능합니다.')),
|
|
977
|
+
!isPaid && (React.createElement("p", { className: "designbase-figma-page-license__usage" }, usageLabel))),
|
|
785
978
|
React.createElement(PricingComparison, { features: pricingFeatures, pricing: pricing, paymentType: paymentType, t: t }),
|
|
979
|
+
state.lastMessage && (React.createElement("p", { className: `designbase-figma-page-license__feedback ${state.lastErrorCode ? 'designbase-figma-page-license__feedback--error' : ''}` }, state.lastMessage)),
|
|
786
980
|
isPaid && (React.createElement("div", { className: "designbase-figma-page-license__section" },
|
|
787
981
|
React.createElement("div", { className: "designbase-figma-page-license__section-header" },
|
|
788
982
|
React.createElement("h3", null, resolveText(t, { key: 'license.activated' }, '라이선스 활성화됨')),
|
|
789
983
|
React.createElement(Button, { onClick: () => setShowDetails(!showDetails), variant: "tertiary", size: "s", iconOnly: true },
|
|
790
984
|
React.createElement(MoreHorizontalIcon, { size: 16 }))),
|
|
791
|
-
React.createElement("div", { className: "designbase-figma-page-license__license-key" },
|
|
985
|
+
state.licenseKey && (React.createElement("div", { className: "designbase-figma-page-license__license-key" },
|
|
792
986
|
resolveText(t, { key: 'license.key_label' }, '라이선스 키'),
|
|
793
987
|
": ",
|
|
794
|
-
React.createElement("span", null, licenseKey)),
|
|
988
|
+
React.createElement("span", null, state.licenseKey))),
|
|
989
|
+
React.createElement("p", { className: "designbase-figma-page-license__usage" }, usageLabel),
|
|
795
990
|
showDetails && (React.createElement("div", { className: "designbase-figma-page-license__details" },
|
|
796
991
|
React.createElement("div", { className: "designbase-figma-page-license__activation-info" },
|
|
797
992
|
React.createElement("div", { className: "designbase-figma-page-license__remaining" },
|
|
798
|
-
activationLimit - activationUsage,
|
|
799
|
-
|
|
993
|
+
Math.max(0, state.activationLimit - state.activationUsage),
|
|
994
|
+
' ',
|
|
800
995
|
resolveText(t, { key: 'license.activations_remaining' }, '활성'),
|
|
801
|
-
|
|
996
|
+
' ',
|
|
802
997
|
resolveText(t, { key: 'license.spots_remaining' }, '자리 남음')),
|
|
803
|
-
React.createElement("div", { className: "designbase-figma-page-license__usage" },
|
|
804
|
-
activationUsage,
|
|
998
|
+
React.createElement("div", { className: "designbase-figma-page-license__usage-count" },
|
|
999
|
+
state.activationUsage,
|
|
805
1000
|
"/",
|
|
806
|
-
activationLimit)),
|
|
807
|
-
React.createElement(Button, { onClick:
|
|
808
|
-
? resolveText(t, { key: 'license.deactivating' }, '
|
|
1001
|
+
state.activationLimit)),
|
|
1002
|
+
React.createElement(Button, { onClick: deactivateLicense, variant: "tertiary", size: "s", disabled: isDeactivating }, isDeactivating
|
|
1003
|
+
? resolveText(t, { key: 'license.deactivating' }, '비활성화 중...')
|
|
809
1004
|
: resolveText(t, { key: 'license.deactivate' }, '라이선스 비활성화')))))),
|
|
810
|
-
!isPaid && (React.createElement(
|
|
811
|
-
React.createElement("div", { className: "designbase-figma-page-license__section
|
|
812
|
-
React.createElement("
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
1005
|
+
!isPaid && (React.createElement(React.Fragment, null,
|
|
1006
|
+
(enableAccountAuth || links.purchaseUrl || links.accountUrl) && (React.createElement("div", { className: "designbase-figma-page-license__section" },
|
|
1007
|
+
React.createElement("div", { className: "designbase-figma-page-license__section-header" },
|
|
1008
|
+
React.createElement("h3", null, resolveText(t, { key: 'license.designbase_account' }, '디자인베이스 계정'))),
|
|
1009
|
+
React.createElement("p", { className: "designbase-figma-page-license__hint" }, resolveText(t, { key: 'license.designbase_account_hint' }, 'WordPress 계정으로 로그인하거나 멤버십을 구매하면 플러그인이 자동으로 활성화됩니다.')),
|
|
1010
|
+
React.createElement("div", { className: "designbase-figma-page-license__actions" },
|
|
1011
|
+
enableAccountAuth && (React.createElement(Button, { onClick: startDesignbaseAuth, variant: "primary", size: "m", disabled: isStartingAuth || isVerifying, fullWidth: true }, isStartingAuth
|
|
1012
|
+
? resolveText(t, { key: 'license.authenticating' }, '인증 중...')
|
|
1013
|
+
: resolveText(t, { key: 'license.login_with_designbase' }, '디자인베이스로 로그인'))),
|
|
1014
|
+
links.purchaseUrl && (React.createElement(Button, { onClick: () => openExternalUrl(links.purchaseUrl), variant: "secondary", size: "m", fullWidth: true },
|
|
1015
|
+
React.createElement("span", null, resolveText(t, { key: 'license.purchase_membership' }, '멤버십 구매')),
|
|
1016
|
+
React.createElement(ExternalLinkIcon, { size: 16 }))),
|
|
1017
|
+
links.accountUrl && (React.createElement(Button, { onClick: () => openExternalUrl(links.accountUrl), variant: "tertiary", size: "s", fullWidth: true },
|
|
1018
|
+
React.createElement("span", null, resolveText(t, { key: 'license.manage_account' }, '계정·멤버십 관리')),
|
|
1019
|
+
React.createElement(ExternalLinkIcon, { size: 16 })))))),
|
|
1020
|
+
showLicenseKeyInput && (React.createElement("div", { className: "designbase-figma-page-license__section" },
|
|
1021
|
+
React.createElement("div", { className: "designbase-figma-page-license__section-header" },
|
|
1022
|
+
React.createElement("h3", null, resolveText(t, { key: 'license.enter_key' }, '라이선스 키 입력'))),
|
|
1023
|
+
React.createElement(FormWithSubmit, { onLicenseSubmit: handleLicenseSubmit, disabled: false, isSubmitting: isVerifying, value: licenseKeyDraft, onValueChange: handleLicenseKeyChange, label: { key: 'license.key' }, submitText: { key: 'license.submit' }, submittingText: { key: 'license.verifying' }, helperText: { key: 'license.enter_from_email' }, t: t }))))),
|
|
1024
|
+
links.helpUrl && (React.createElement("p", { className: "designbase-figma-page-license__disclaimer" },
|
|
1025
|
+
React.createElement("button", { type: "button", className: "designbase-figma-page-license__link", onClick: () => openExternalUrl(links.helpUrl) }, resolveText(t, { key: 'license.help_link' }, '라이선스·결제 도움말'))))),
|
|
817
1026
|
React.createElement(Button, { className: "designbase-figma-page-license__close", onPress: onClose, variant: "tertiary", size: "s", "aria-label": "Close" },
|
|
818
1027
|
React.createElement(CloseIcon, { size: 20 }))));
|
|
819
1028
|
};
|
|
@@ -1087,11 +1296,11 @@ const UpgradeBanner = ({ onClick, variant = 'button', isLoading = false, title =
|
|
|
1087
1296
|
React.createElement("div", { className: "designbase-figma-upgrade-banner__text-wrap" },
|
|
1088
1297
|
React.createElement("h3", { className: "designbase-figma-upgrade-banner__title" }, displayTitle),
|
|
1089
1298
|
React.createElement("p", { className: "designbase-figma-upgrade-banner__text" }, displayDescription)),
|
|
1090
|
-
variant === 'button' ? (React.createElement(Button, {
|
|
1299
|
+
variant === 'button' ? (React.createElement(Button, { onPress: onClick, variant: "primary", size: "m", disabled: isLoading }, displayButtonText)) : (React.createElement("div", { className: "designbase-figma-upgrade-banner__icon" },
|
|
1091
1300
|
React.createElement(ChevronRightIcon, { size: 20 })))));
|
|
1092
1301
|
return (React.createElement("div", { className: `designbase-figma-upgrade-banner designbase-figma-upgrade-banner--${variant} ${className || ''}` }, useGradient ? (React.createElement(RandomGradient, { scheme: gradientScheme, tone: gradientTone, animated: gradientAnimated, height: "100%", width: "100%", className: "designbase-figma-upgrade-banner__gradient-bg" }, bannerContent)) : (bannerContent)));
|
|
1093
1302
|
};
|
|
1094
1303
|
UpgradeBanner.displayName = 'UpgradeBanner';
|
|
1095
1304
|
|
|
1096
|
-
export { DonationBadge, FigmaContainer, FigmaFooter, FigmaHeader, FigmaPageHead, FigmaSection, FigmaSidebar, FigmaToast, FigmaToastProvider, FormWithSubmit, InteractionFeedback, LanguageSelector, LogoDropdown, PageLicense, PaymentBadge, PaymentStatusSection, PricingComparison, ProgressModal, ResizablePlugin, SettingsModal, TitleDescription, UpgradeBanner, getActiveSection, resolveText, scrollToSection, useFigmaToast, useToast };
|
|
1305
|
+
export { DEFAULT_LICENSE_MESSAGE_TYPES, DonationBadge, FigmaContainer, FigmaFooter, FigmaHeader, FigmaPageHead, FigmaSection, FigmaSidebar, FigmaToast, FigmaToastProvider, FormWithSubmit, InteractionFeedback, LanguageSelector, LogoDropdown, PageLicense, PaymentBadge, PaymentStatusSection, PricingComparison, ProgressModal, ResizablePlugin, START_DESIGNBASE_AUTH_MESSAGE, SettingsModal, TitleDescription, UpgradeBanner, getActiveSection, resolveText, scrollToSection, useFigmaLicenseBridge, useFigmaToast, useToast };
|
|
1097
1306
|
//# sourceMappingURL=index.esm.js.map
|