@designbasekorea/figma-ui 0.6.6 → 0.6.8
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.css +1 -1
- package/dist/index.css.map +1 -1
- package/dist/index.d.ts +44 -3
- package/dist/index.esm.css +1 -1
- package/dist/index.esm.css.map +1 -1
- package/dist/index.esm.js +447 -145
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +447 -141
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -6,6 +6,14 @@ var icons = require('@designbasekorea/icons');
|
|
|
6
6
|
|
|
7
7
|
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}
|
|
8
8
|
|
|
9
|
+
function interpolateText(template, values) {
|
|
10
|
+
if (!values)
|
|
11
|
+
return template;
|
|
12
|
+
return Object.entries(values).reduce((result, [key, value]) => {
|
|
13
|
+
const replacement = value == null ? '' : String(value);
|
|
14
|
+
return result.replace(new RegExp(`\\{\\{${key}\\}\\}`, 'g'), replacement);
|
|
15
|
+
}, template);
|
|
16
|
+
}
|
|
9
17
|
function resolveText(t, input, fallback) {
|
|
10
18
|
if (input == null)
|
|
11
19
|
return fallback;
|
|
@@ -15,10 +23,13 @@ function resolveText(t, input, fallback) {
|
|
|
15
23
|
if (t) {
|
|
16
24
|
const translated = t(input.key, input.values);
|
|
17
25
|
if (translated && translated !== input.key) {
|
|
18
|
-
return translated;
|
|
26
|
+
return interpolateText(translated, input.values);
|
|
19
27
|
}
|
|
20
28
|
}
|
|
21
|
-
|
|
29
|
+
if (fallback) {
|
|
30
|
+
return interpolateText(fallback, input.values);
|
|
31
|
+
}
|
|
32
|
+
return input.key;
|
|
22
33
|
}
|
|
23
34
|
|
|
24
35
|
const DONATION_ICON_NAMES = {
|
|
@@ -384,62 +395,63 @@ const FigmaSidebar = ({ categories, categoryGroups = {}, categoryLabels = {}, ac
|
|
|
384
395
|
};
|
|
385
396
|
FigmaSidebar.displayName = 'FigmaSidebar';
|
|
386
397
|
|
|
387
|
-
|
|
388
|
-
const getBadgeText = () => {
|
|
389
|
-
if (text)
|
|
390
|
-
return text;
|
|
391
|
-
return isActive
|
|
392
|
-
? (t('proUser') || '프로 계정')
|
|
393
|
-
: (t('usingFree') || '무료 사용');
|
|
394
|
-
};
|
|
395
|
-
const getBadgeVariant = () => {
|
|
396
|
-
return isActive ? 'success' : 'secondary';
|
|
397
|
-
};
|
|
398
|
-
return (React.createElement("div", { className: `designbase-figma-payment-badge ${className || ''} ${isActive ? 'active' : 'free'}`, onClick: onClick, style: { cursor: onClick ? 'pointer' : 'default' } }, isLoading ? (React.createElement(react.Spinner, { size: "s" })) : (React.createElement(react.Badge, { variant: getBadgeVariant(), size: "s", className: "designbase-figma-payment-badge__badge" }, getBadgeText()))));
|
|
399
|
-
};
|
|
400
|
-
PaymentBadge.displayName = 'PaymentBadge';
|
|
401
|
-
|
|
402
|
-
let useTranslation = null;
|
|
398
|
+
let useTranslation$2 = null;
|
|
403
399
|
try {
|
|
404
400
|
const reactI18next = require('react-i18next');
|
|
405
|
-
useTranslation = reactI18next.useTranslation;
|
|
401
|
+
useTranslation$2 = reactI18next.useTranslation;
|
|
406
402
|
}
|
|
407
403
|
catch (e) {
|
|
408
404
|
}
|
|
409
|
-
const DEFAULT_LANGUAGES = [
|
|
410
|
-
{ code: 'ko', label: '
|
|
411
|
-
{ code: 'en', label: '
|
|
405
|
+
const DEFAULT_LANGUAGES$1 = [
|
|
406
|
+
{ code: 'ko', label: '한국어' },
|
|
407
|
+
{ code: 'en', label: 'English' },
|
|
408
|
+
{ code: 'ja', label: '日本語' },
|
|
412
409
|
];
|
|
413
|
-
const
|
|
414
|
-
const
|
|
415
|
-
|
|
416
|
-
|
|
410
|
+
const defaultT$2 = (key) => {
|
|
411
|
+
const translations = {
|
|
412
|
+
languageSettings: '언어 설정',
|
|
413
|
+
languageSettingsDescription: '플러그인에 표시할 언어를 선택하세요.',
|
|
414
|
+
'language.korean': '한국어',
|
|
415
|
+
'language.english': 'English',
|
|
416
|
+
'language.japanese': '日本語',
|
|
417
|
+
};
|
|
418
|
+
return translations[key] || key;
|
|
419
|
+
};
|
|
420
|
+
const LanguageSettingsModal = ({ isOpen, onClose, languages = DEFAULT_LANGUAGES$1, currentLanguage, onLanguageChange, title, description, className, t = defaultT$2, }) => {
|
|
421
|
+
const i18n = useTranslation$2 ? useTranslation$2().i18n : null;
|
|
422
|
+
const activeLanguage = currentLanguage || i18n?.language || languages[0]?.code;
|
|
423
|
+
const translate = (key, fallback) => {
|
|
424
|
+
const translated = t(key);
|
|
425
|
+
return translated && translated !== key ? translated : fallback;
|
|
426
|
+
};
|
|
427
|
+
const handleSelect = (languageCode) => {
|
|
417
428
|
if (i18n) {
|
|
418
429
|
i18n.changeLanguage(languageCode);
|
|
419
430
|
}
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
}
|
|
431
|
+
onLanguageChange?.(languageCode);
|
|
432
|
+
onClose();
|
|
423
433
|
};
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
: undefined;
|
|
436
|
-
if (value) {
|
|
437
|
-
handleLanguageChange(value);
|
|
438
|
-
}
|
|
439
|
-
}, size: size, className: "designbase-figma-language-selector__segment" })));
|
|
434
|
+
return (React.createElement(react.Modal, { isOpen: isOpen, onClose: onClose, title: title || translate('languageSettings', '언어 설정'), size: "s", className: clsx('designbase-figma-language-settings-modal', className) },
|
|
435
|
+
React.createElement("div", { className: "designbase-figma-language-settings-modal__content" },
|
|
436
|
+
React.createElement("p", { className: "designbase-figma-language-settings-modal__description" }, description || translate('languageSettingsDescription', '플러그인에 표시할 언어를 선택하세요.')),
|
|
437
|
+
React.createElement("ul", { className: "designbase-figma-language-settings-modal__list", role: "listbox", "aria-label": translate('languageSettings', '언어 설정') }, languages.map((language) => {
|
|
438
|
+
const label = resolveText(t, language.label, language.code.toUpperCase());
|
|
439
|
+
const isActive = language.code === activeLanguage;
|
|
440
|
+
return (React.createElement("li", { key: language.code, role: "presentation" },
|
|
441
|
+
React.createElement("button", { type: "button", className: clsx('designbase-figma-language-settings-modal__option', { 'designbase-figma-language-settings-modal__option--active': isActive }), role: "option", "aria-selected": isActive, onClick: () => handleSelect(language.code) },
|
|
442
|
+
React.createElement("span", { className: "designbase-figma-language-settings-modal__option-label" }, label),
|
|
443
|
+
React.createElement("span", { className: "designbase-figma-language-settings-modal__option-indicator", "aria-hidden": "true" }))));
|
|
444
|
+
})))));
|
|
440
445
|
};
|
|
441
|
-
|
|
446
|
+
LanguageSettingsModal.displayName = 'LanguageSettingsModal';
|
|
442
447
|
|
|
448
|
+
let useTranslation$1 = null;
|
|
449
|
+
try {
|
|
450
|
+
const reactI18next = require('react-i18next');
|
|
451
|
+
useTranslation$1 = reactI18next.useTranslation;
|
|
452
|
+
}
|
|
453
|
+
catch (e) {
|
|
454
|
+
}
|
|
443
455
|
const defaultLinks = [
|
|
444
456
|
{
|
|
445
457
|
name: 'officialWebsite',
|
|
@@ -473,50 +485,219 @@ const defaultTranslations$1 = {
|
|
|
473
485
|
instagram: '인스타그램',
|
|
474
486
|
figmaCommunity: '피그마 커뮤니티',
|
|
475
487
|
contact: '문의하기',
|
|
488
|
+
languageSettings: '언어 설정',
|
|
476
489
|
};
|
|
477
490
|
const defaultT$1 = (key) => defaultTranslations$1[key] || key;
|
|
478
|
-
const
|
|
491
|
+
const defaultLanguages = [
|
|
492
|
+
{ code: 'ko', label: '한국어' },
|
|
493
|
+
{ code: 'en', label: 'English' },
|
|
494
|
+
{ code: 'ja', label: '日本語' },
|
|
495
|
+
];
|
|
496
|
+
const LogoDropdown = ({ logoSrc, logoAlt = 'DesignBase', logoType = 'designbase', logoSize = 'xs', links = defaultLinks, position = 'top-left', className, t = defaultT$1, showLanguageSelector = true, languages = defaultLanguages, currentLanguage, onLanguageChange, }) => {
|
|
479
497
|
const [isOpen, setIsOpen] = React.useState(false);
|
|
480
|
-
const
|
|
498
|
+
const [isLanguageModalOpen, setIsLanguageModalOpen] = React.useState(false);
|
|
499
|
+
const i18n = useTranslation$1 ? useTranslation$1().i18n : null;
|
|
500
|
+
const rootRef = React.useRef(null);
|
|
501
|
+
const triggerRef = React.useRef(null);
|
|
502
|
+
const menuId = `designbase-figma-logo-menu-${React.useId().replace(/:/g, '')}`;
|
|
503
|
+
const activeLanguage = currentLanguage || i18n?.language || languages[0]?.code;
|
|
504
|
+
const translate = (key, fallback) => {
|
|
505
|
+
const translated = t(key);
|
|
506
|
+
return translated && translated !== key ? translated : fallback;
|
|
507
|
+
};
|
|
508
|
+
React.useEffect(() => {
|
|
509
|
+
if (!isOpen) {
|
|
510
|
+
return undefined;
|
|
511
|
+
}
|
|
512
|
+
const handlePointerDown = (event) => {
|
|
513
|
+
if (!rootRef.current?.contains(event.target)) {
|
|
514
|
+
setIsOpen(false);
|
|
515
|
+
}
|
|
516
|
+
};
|
|
517
|
+
const handleKeyDown = (event) => {
|
|
518
|
+
if (event.key === 'Escape') {
|
|
519
|
+
setIsOpen(false);
|
|
520
|
+
triggerRef.current?.focus();
|
|
521
|
+
}
|
|
522
|
+
};
|
|
523
|
+
document.addEventListener('mousedown', handlePointerDown);
|
|
524
|
+
document.addEventListener('keydown', handleKeyDown);
|
|
525
|
+
return () => {
|
|
526
|
+
document.removeEventListener('mousedown', handlePointerDown);
|
|
527
|
+
document.removeEventListener('keydown', handleKeyDown);
|
|
528
|
+
};
|
|
529
|
+
}, [isOpen]);
|
|
530
|
+
const toggleDropdown = () => setIsOpen((value) => !value);
|
|
481
531
|
const handleLinkClick = (url) => {
|
|
532
|
+
setIsOpen(false);
|
|
482
533
|
if (url.startsWith('mailto:')) {
|
|
483
534
|
return;
|
|
484
535
|
}
|
|
485
536
|
window.open(url, '_blank', 'noopener,noreferrer');
|
|
486
537
|
};
|
|
538
|
+
const handleLanguageSettingsClick = () => {
|
|
539
|
+
setIsOpen(false);
|
|
540
|
+
setIsLanguageModalOpen(true);
|
|
541
|
+
};
|
|
542
|
+
const handleLanguageChange = (languageCode) => {
|
|
543
|
+
if (i18n) {
|
|
544
|
+
i18n.changeLanguage(languageCode);
|
|
545
|
+
}
|
|
546
|
+
onLanguageChange?.(languageCode);
|
|
547
|
+
};
|
|
487
548
|
const classes = clsx('designbase-figma-logo-dropdown', `designbase-figma-logo-dropdown--${position}`, {
|
|
488
549
|
'designbase-figma-logo-dropdown--open': isOpen,
|
|
489
550
|
}, className);
|
|
490
551
|
const renderLogo = () => {
|
|
491
|
-
|
|
492
|
-
return (React.createElement("img", { className: "designbase-figma-logo-dropdown__logo", src: logoSrc, alt: logoAlt, onClick: toggleDropdown }));
|
|
493
|
-
}
|
|
494
|
-
return (React.createElement("div", { className: "designbase-figma-logo-dropdown__logo", onClick: toggleDropdown },
|
|
495
|
-
React.createElement(react.Logo, { type: logoType, size: logoSize, variant: "default" })));
|
|
552
|
+
return (React.createElement("button", { ref: triggerRef, type: "button", className: "designbase-figma-logo-dropdown__logo", "aria-label": logoAlt, "aria-expanded": isOpen, "aria-controls": menuId, "aria-haspopup": "menu", onClick: toggleDropdown }, logoSrc ? (React.createElement("img", { src: logoSrc, alt: "" })) : (React.createElement(react.Logo, { type: logoType, size: logoSize, variant: "default" }))));
|
|
496
553
|
};
|
|
497
|
-
return (React.createElement("div", { className: classes },
|
|
554
|
+
return (React.createElement("div", { ref: rootRef, className: classes },
|
|
498
555
|
renderLogo(),
|
|
499
|
-
isOpen && (React.createElement("ul", { className: "designbase-figma-logo-dropdown__menu"
|
|
500
|
-
React.createElement("
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
556
|
+
isOpen && (React.createElement("ul", { id: menuId, className: "designbase-figma-logo-dropdown__menu", role: "menu" },
|
|
557
|
+
links.map((link, index) => (React.createElement("li", { key: index, className: "designbase-figma-logo-dropdown__item", role: "none" },
|
|
558
|
+
React.createElement("button", { type: "button", className: "designbase-figma-logo-dropdown__link", role: "menuitem", onClick: () => handleLinkClick(link.url) },
|
|
559
|
+
link.icon && (React.createElement("span", { className: "designbase-figma-logo-dropdown__icon" }, link.icon)),
|
|
560
|
+
React.createElement("span", { className: "designbase-figma-logo-dropdown__text" }, t(link.name)),
|
|
561
|
+
React.createElement(icons.ExternalLinkIcon, { size: 16, className: "designbase-figma-logo-dropdown__arrow" }))))),
|
|
562
|
+
showLanguageSelector && (React.createElement("li", { className: "designbase-figma-logo-dropdown__item designbase-figma-logo-dropdown__language-item", role: "none" },
|
|
563
|
+
React.createElement("button", { type: "button", className: "designbase-figma-logo-dropdown__link", role: "menuitem", onClick: handleLanguageSettingsClick },
|
|
564
|
+
React.createElement("span", { className: "designbase-figma-logo-dropdown__icon" },
|
|
565
|
+
React.createElement(icons.TranslateIcon, { size: 20, "aria-hidden": "true" })),
|
|
566
|
+
React.createElement("span", { className: "designbase-figma-logo-dropdown__text" }, translate('languageSettings', '언어 설정'))))))),
|
|
567
|
+
showLanguageSelector && (React.createElement(LanguageSettingsModal, { isOpen: isLanguageModalOpen, onClose: () => setIsLanguageModalOpen(false), languages: languages, currentLanguage: activeLanguage, onLanguageChange: handleLanguageChange, t: t }))));
|
|
504
568
|
};
|
|
505
569
|
LogoDropdown.displayName = 'LogoDropdown';
|
|
506
570
|
|
|
571
|
+
const fallbackText = (t, key, fallback) => {
|
|
572
|
+
const translated = t?.(key);
|
|
573
|
+
return translated && translated !== key ? translated : fallback;
|
|
574
|
+
};
|
|
575
|
+
const UsageStatusPopover = ({ paymentStatus, usageCount = 0, maxDailyUsage = 20, showUsage = true, showPlan = true, isLoading = false, onLicensePageClick, t, className, }) => {
|
|
576
|
+
const [isOpen, setIsOpen] = React.useState(false);
|
|
577
|
+
const rootRef = React.useRef(null);
|
|
578
|
+
const triggerRef = React.useRef(null);
|
|
579
|
+
const popoverId = `designbase-figma-usage-popover-${React.useId().replace(/:/g, '')}`;
|
|
580
|
+
const isPaid = paymentStatus === 'PAID';
|
|
581
|
+
const isBeta = paymentStatus === 'BETA';
|
|
582
|
+
const hasDailyLimit = !isPaid && maxDailyUsage > 0;
|
|
583
|
+
const limit = Math.max(1, Math.round(maxDailyUsage));
|
|
584
|
+
const used = Math.min(limit, Math.max(0, Math.round(usageCount)));
|
|
585
|
+
const remaining = Math.max(0, limit - used);
|
|
586
|
+
const usedPercent = hasDailyLimit ? Math.round((used / limit) * 100) : 0;
|
|
587
|
+
const hasControl = showUsage || showPlan;
|
|
588
|
+
const planLabel = isPaid
|
|
589
|
+
? fallbackText(t, 'proUser', '프로')
|
|
590
|
+
: isBeta
|
|
591
|
+
? fallbackText(t, 'betaStatus', 'BETA')
|
|
592
|
+
: paymentStatus === 'TRIAL'
|
|
593
|
+
? fallbackText(t, 'trialPlan', '체험판')
|
|
594
|
+
: fallbackText(t, 'free', '무료');
|
|
595
|
+
const planTitle = isBeta
|
|
596
|
+
? planLabel
|
|
597
|
+
: `${planLabel} ${fallbackText(t, 'planSuffix', '플랜')}`;
|
|
598
|
+
const triggerLabel = isPaid
|
|
599
|
+
? `${planLabel}, ${fallbackText(t, 'unlimitedUsage', '무제한 사용 가능')}`
|
|
600
|
+
: isBeta
|
|
601
|
+
? planLabel
|
|
602
|
+
: hasDailyLimit
|
|
603
|
+
? `${remaining}/${limit} ${fallbackText(t, 'remainingShort', '남음')}`
|
|
604
|
+
: planLabel;
|
|
605
|
+
const upgradeLabel = isPaid
|
|
606
|
+
? fallbackText(t, 'managePlan', '플랜 관리')
|
|
607
|
+
: fallbackText(t, 'upgradePlan', '플랜 업그레이드');
|
|
608
|
+
React.useEffect(() => {
|
|
609
|
+
if (!isOpen)
|
|
610
|
+
return undefined;
|
|
611
|
+
const handlePointerDown = (event) => {
|
|
612
|
+
if (!rootRef.current?.contains(event.target)) {
|
|
613
|
+
setIsOpen(false);
|
|
614
|
+
}
|
|
615
|
+
};
|
|
616
|
+
const handleKeyDown = (event) => {
|
|
617
|
+
if (event.key === 'Escape') {
|
|
618
|
+
setIsOpen(false);
|
|
619
|
+
triggerRef.current?.focus();
|
|
620
|
+
}
|
|
621
|
+
};
|
|
622
|
+
document.addEventListener('mousedown', handlePointerDown);
|
|
623
|
+
document.addEventListener('keydown', handleKeyDown);
|
|
624
|
+
return () => {
|
|
625
|
+
document.removeEventListener('mousedown', handlePointerDown);
|
|
626
|
+
document.removeEventListener('keydown', handleKeyDown);
|
|
627
|
+
};
|
|
628
|
+
}, [isOpen]);
|
|
629
|
+
if (!hasControl)
|
|
630
|
+
return null;
|
|
631
|
+
const handleManageClick = () => {
|
|
632
|
+
setIsOpen(false);
|
|
633
|
+
onLicensePageClick?.();
|
|
634
|
+
};
|
|
635
|
+
const togglePopover = () => setIsOpen((value) => !value);
|
|
636
|
+
return (React.createElement("div", { ref: rootRef, className: clsx('designbase-figma-usage-status', {
|
|
637
|
+
'designbase-figma-usage-status--open': isOpen,
|
|
638
|
+
'designbase-figma-usage-status--unlimited': isPaid,
|
|
639
|
+
'designbase-figma-usage-status--loading': isLoading,
|
|
640
|
+
}, className) },
|
|
641
|
+
React.createElement("div", { className: "designbase-figma-usage-status__control" },
|
|
642
|
+
showUsage && (React.createElement("button", { ref: triggerRef, type: "button", className: "designbase-figma-usage-status__trigger", "aria-expanded": isOpen, "aria-controls": popoverId, "aria-haspopup": "dialog", "aria-label": isLoading ? fallbackText(t, 'loading', '불러오는 중') : triggerLabel, disabled: isLoading, onClick: togglePopover }, isLoading ? (React.createElement("span", { className: "designbase-figma-usage-status__skeleton", "aria-hidden": "true" })) : isPaid ? (React.createElement("span", { className: "designbase-figma-usage-status__unlimited-mark", "aria-hidden": "true" }, "\u221E")) : hasDailyLimit ? (React.createElement(React.Fragment, null,
|
|
643
|
+
React.createElement("span", { className: "designbase-figma-usage-status__mini-progress", "aria-hidden": "true" },
|
|
644
|
+
React.createElement("span", { style: { width: `${usedPercent}%` } })),
|
|
645
|
+
React.createElement("span", { className: "designbase-figma-usage-status__usage-value" },
|
|
646
|
+
React.createElement("strong", null, remaining),
|
|
647
|
+
React.createElement("span", null,
|
|
648
|
+
"/",
|
|
649
|
+
limit)))) : (React.createElement("span", { className: "designbase-figma-usage-status__plan-label" }, planLabel)))),
|
|
650
|
+
showPlan && !isLoading && (React.createElement("button", { type: "button", className: "designbase-figma-usage-status__plan-badge", "aria-expanded": isOpen, "aria-controls": popoverId, "aria-haspopup": "dialog", "aria-label": planTitle, onClick: togglePopover }, planLabel))),
|
|
651
|
+
isOpen && (React.createElement("div", { id: popoverId, className: "designbase-figma-usage-status__popover", role: "dialog", "aria-label": fallbackText(t, 'usageDetails', '사용량 상세') },
|
|
652
|
+
React.createElement("div", { className: "designbase-figma-usage-status__popover-header" },
|
|
653
|
+
React.createElement("span", { className: "designbase-figma-usage-status__plan-title" }, planTitle),
|
|
654
|
+
!isBeta && (React.createElement(react.Button, { className: "designbase-figma-usage-status__upgrade", variant: isPaid ? 'secondary' : 'primary', size: "s", endIcon: "chevron-right", onPress: handleManageClick }, upgradeLabel))),
|
|
655
|
+
isPaid ? (React.createElement("div", { className: "designbase-figma-usage-status__unlimited-copy" },
|
|
656
|
+
React.createElement("span", { className: "designbase-figma-usage-status__unlimited-mark", "aria-hidden": "true" }, "\u221E"),
|
|
657
|
+
React.createElement("span", null, fallbackText(t, 'unlimitedUsageTooltip', '모든 기능을 제한 없이 사용할 수 있습니다')))) : isBeta ? (React.createElement("p", { className: "designbase-figma-usage-status__note" }, fallbackText(t, 'betaUsageDescription', '베타 기간에는 사용량 제한이 적용되지 않습니다'))) : hasDailyLimit ? (React.createElement("div", { className: "designbase-figma-usage-status__usage-detail" },
|
|
658
|
+
React.createElement("div", { className: "designbase-figma-usage-status__usage-summary" },
|
|
659
|
+
React.createElement("strong", { className: "designbase-figma-usage-status__usage-count" },
|
|
660
|
+
used,
|
|
661
|
+
React.createElement("span", { className: "designbase-figma-usage-status__usage-divider" }, "/"),
|
|
662
|
+
limit),
|
|
663
|
+
React.createElement("span", { className: "designbase-figma-usage-status__usage-caption" }, fallbackText(t, 'usageCountCaption', '오늘 사용'))),
|
|
664
|
+
React.createElement("div", { className: "designbase-figma-usage-status__progress", role: "progressbar", "aria-label": fallbackText(t, 'usageCountCaption', '오늘 사용'), "aria-valuemin": 0, "aria-valuemax": limit, "aria-valuenow": used, "aria-valuetext": `${used} / ${limit}` },
|
|
665
|
+
React.createElement("span", { style: { width: `${usedPercent}%` } })),
|
|
666
|
+
React.createElement("p", { className: "designbase-figma-usage-status__remaining-copy" }, resolveText(t, { key: 'remainingCopy', values: { count: remaining } }, `${remaining}회 남음 · 매일 리셋`)))) : (React.createElement("p", { className: "designbase-figma-usage-status__note" }, fallbackText(t, 'usageUnavailable', '사용량 제한이 없습니다')))))));
|
|
667
|
+
};
|
|
668
|
+
UsageStatusPopover.displayName = 'UsageStatusPopover';
|
|
669
|
+
|
|
507
670
|
const defaultTranslations = {
|
|
508
671
|
unlimitedUsage: '무제한 사용',
|
|
672
|
+
proUser: '프로',
|
|
673
|
+
free: '무료',
|
|
674
|
+
trialPlan: '체험판',
|
|
675
|
+
planSuffix: '플랜',
|
|
676
|
+
usageDetails: '사용량 상세',
|
|
677
|
+
dailyUsage: '오늘 사용량',
|
|
678
|
+
usedToday: '오늘 사용',
|
|
679
|
+
remainingToday: '오늘 남은 사용량',
|
|
680
|
+
remainingUsage: '오늘 남은 사용량',
|
|
681
|
+
managePlan: '플랜 관리',
|
|
682
|
+
upgradePlan: '플랜 업그레이드',
|
|
683
|
+
remainingShort: '남음',
|
|
684
|
+
usageCountCaption: '오늘 사용',
|
|
685
|
+
remainingCopy: '{{count}}회 남음 · 매일 리셋',
|
|
686
|
+
usageUnavailable: '사용량 제한이 없습니다',
|
|
687
|
+
betaUsageDescription: '베타 기간에는 사용량 제한이 적용되지 않습니다',
|
|
509
688
|
perDay: '일',
|
|
510
689
|
resetsDaily: '매일 리셋',
|
|
511
690
|
donationPrompt: '이 플러그인이 유용했나요?',
|
|
512
691
|
betaStatus: 'BETA',
|
|
513
692
|
};
|
|
514
|
-
const defaultT = (key) =>
|
|
515
|
-
const
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
693
|
+
const defaultT = (key, values) => {
|
|
694
|
+
const template = defaultTranslations[key] || key;
|
|
695
|
+
return interpolateText(template, values);
|
|
696
|
+
};
|
|
697
|
+
const FigmaFooter = ({ onLicensePageClick, paymentStatus = 'FREE', usageCount = 0, isLoading = false, showPaymentStatus = true, showUsageInfo = true, maxDailyUsage = 20, showPaymentBadge = true, showDonation = true, donationUrl = 'https://buymeacoffee.com/designbase', donationText = 'Buy me a coffee', showDonationText = true, donationPromptText, showLanguageSelector = true, currentLanguage = 'ko', languages, onLanguageChange, logoSrc, logoAlt = 'DesignBase', logoType = 'designbase', logoSize = 'xs', logoLinks, className, t = defaultT, children, }) => {
|
|
698
|
+
const resolvedShowUsageInfo = showPaymentStatus && showUsageInfo;
|
|
519
699
|
const resolvedShowPaymentBadge = showPaymentStatus && showPaymentBadge;
|
|
700
|
+
const resolvedLicensePageClick = paymentStatus === 'BETA' ? undefined : onLicensePageClick;
|
|
520
701
|
const hasChildren = React.Children.count(children) > 0;
|
|
521
702
|
const classes = clsx('designbase-figma-footer', {
|
|
522
703
|
'designbase-figma-footer--has-children': hasChildren,
|
|
@@ -524,17 +705,9 @@ const FigmaFooter = ({ onLicensePageClick, paymentStatus = 'FREE', usageCount =
|
|
|
524
705
|
return (React.createElement("footer", { className: classes },
|
|
525
706
|
React.createElement("div", { className: "designbase-figma-footer__wrap" },
|
|
526
707
|
React.createElement("div", { className: "designbase-figma-footer__left" },
|
|
527
|
-
React.createElement(LogoDropdown, { logoSrc: logoSrc, logoAlt: logoAlt, logoType: logoType, logoSize: logoSize, links: logoLinks, t: t }),
|
|
528
|
-
showLanguageSelector && (React.createElement(LanguageSelector, { currentLanguage: currentLanguage, languages: languages, onLanguageChange: onLanguageChange, size: "s" }))),
|
|
708
|
+
React.createElement(LogoDropdown, { logoSrc: logoSrc, logoAlt: logoAlt, logoType: logoType, logoSize: logoSize, links: logoLinks, languages: languages, currentLanguage: currentLanguage, showLanguageSelector: showLanguageSelector, onLanguageChange: onLanguageChange, t: t })),
|
|
529
709
|
React.createElement("div", { className: "designbase-figma-footer__right" },
|
|
530
|
-
|
|
531
|
-
React.createElement("span", { className: "designbase-figma-footer__usage-count" }, usageCount),
|
|
532
|
-
React.createElement("span", { className: "designbase-figma-footer__max-daily-usage" },
|
|
533
|
-
"/",
|
|
534
|
-
maxDailyUsage,
|
|
535
|
-
" ",
|
|
536
|
-
t('perDay')))))),
|
|
537
|
-
showPaymentStatus && resolvedShowPaymentBadge && (React.createElement(PaymentBadge, { isActive: isActive, onClick: isBeta ? undefined : onLicensePageClick, isLoading: isLoading, text: isBeta ? (t('betaStatus') || 'BETA') : undefined, t: t })),
|
|
710
|
+
(resolvedShowUsageInfo || resolvedShowPaymentBadge) && (React.createElement(UsageStatusPopover, { paymentStatus: paymentStatus, usageCount: usageCount, maxDailyUsage: maxDailyUsage, showUsage: resolvedShowUsageInfo, showPlan: resolvedShowPaymentBadge, isLoading: isLoading, onLicensePageClick: resolvedLicensePageClick, t: t })),
|
|
538
711
|
showDonation && (React.createElement(DonationBadge, { donationUrl: donationUrl, text: donationText, iconType: "heart", size: "s", iconOnly: true, showPrompt: false, t: t })))),
|
|
539
712
|
children));
|
|
540
713
|
};
|
|
@@ -658,6 +831,47 @@ const InteractionFeedback = ({ status = 'default', message, statusMessage, leftC
|
|
|
658
831
|
};
|
|
659
832
|
InteractionFeedback.displayName = 'InteractionFeedback';
|
|
660
833
|
|
|
834
|
+
let useTranslation = null;
|
|
835
|
+
try {
|
|
836
|
+
const reactI18next = require('react-i18next');
|
|
837
|
+
useTranslation = reactI18next.useTranslation;
|
|
838
|
+
}
|
|
839
|
+
catch (e) {
|
|
840
|
+
}
|
|
841
|
+
const DEFAULT_LANGUAGES = [
|
|
842
|
+
{ code: 'ko', label: 'KO' },
|
|
843
|
+
{ code: 'en', label: 'EN' },
|
|
844
|
+
];
|
|
845
|
+
const LanguageSelector = ({ currentLanguage, languages = DEFAULT_LANGUAGES, onLanguageChange, size = 's', className, t }) => {
|
|
846
|
+
const i18n = useTranslation ? useTranslation().i18n : null;
|
|
847
|
+
const activeLanguage = currentLanguage || (i18n?.language) || 'ko';
|
|
848
|
+
const handleLanguageChange = (languageCode) => {
|
|
849
|
+
if (i18n) {
|
|
850
|
+
i18n.changeLanguage(languageCode);
|
|
851
|
+
}
|
|
852
|
+
if (onLanguageChange) {
|
|
853
|
+
onLanguageChange(languageCode);
|
|
854
|
+
}
|
|
855
|
+
};
|
|
856
|
+
const segmentOptions = languages.map((language) => ({
|
|
857
|
+
value: language.code,
|
|
858
|
+
label: resolveText(t, language.label),
|
|
859
|
+
disabled: false,
|
|
860
|
+
}));
|
|
861
|
+
return (React.createElement("div", { className: `designbase-figma-language-selector ${className || ''}` },
|
|
862
|
+
React.createElement(react.SegmentControl, { options: segmentOptions, value: activeLanguage, onChange: (event) => {
|
|
863
|
+
const detail = event.detail;
|
|
864
|
+
const value = typeof detail === 'object' && detail !== null && 'value' in detail &&
|
|
865
|
+
typeof detail.value === 'string'
|
|
866
|
+
? detail.value
|
|
867
|
+
: undefined;
|
|
868
|
+
if (value) {
|
|
869
|
+
handleLanguageChange(value);
|
|
870
|
+
}
|
|
871
|
+
}, size: size, className: "designbase-figma-language-selector__segment" })));
|
|
872
|
+
};
|
|
873
|
+
LanguageSelector.displayName = 'LanguageSelector';
|
|
874
|
+
|
|
661
875
|
function postPluginMessage(message) {
|
|
662
876
|
if (typeof parent === 'undefined') {
|
|
663
877
|
return;
|
|
@@ -673,17 +887,27 @@ function openExternalUrl(url) {
|
|
|
673
887
|
}
|
|
674
888
|
}
|
|
675
889
|
|
|
676
|
-
const LicenseOffer = ({ membershipUrl, t, className }) =>
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
React.createElement("
|
|
686
|
-
React.createElement(
|
|
890
|
+
const LicenseOffer = ({ joinUrl, guideUrl, membershipUrl, t, className, }) => {
|
|
891
|
+
const titleId = `designbase-license-offer-title-${React.useId().replace(/:/g, '')}`;
|
|
892
|
+
const resolvedJoinUrl = joinUrl || membershipUrl;
|
|
893
|
+
const steps = [
|
|
894
|
+
resolveText(t, { key: 'license.youtube_membership_step_1' }, 'Designbase YouTube 채널 멤버십에 가입합니다.'),
|
|
895
|
+
resolveText(t, { key: 'license.youtube_membership_step_2' }, '멤버 전용 커뮤니티에서 이번 달 키를 복사합니다.'),
|
|
896
|
+
resolveText(t, { key: 'license.youtube_membership_step_3' }, '아래 입력창에 키를 붙여 넣고 활성화합니다.'),
|
|
897
|
+
];
|
|
898
|
+
return (React.createElement("section", { className: `designbase-figma-license-offer ${className || ''}`, "aria-labelledby": titleId },
|
|
899
|
+
React.createElement("h3", { id: titleId, className: "designbase-figma-license-offer__title" }, resolveText(t, { key: 'license.youtube_membership_howto' }, '이용 방법')),
|
|
900
|
+
React.createElement("ol", { className: "designbase-figma-license-offer__steps" }, steps.map((step, index) => (React.createElement("li", { key: index },
|
|
901
|
+
React.createElement("span", { className: "designbase-figma-license-offer__step-index", "aria-hidden": "true" },
|
|
902
|
+
index + 1,
|
|
903
|
+
"."),
|
|
904
|
+
React.createElement("span", null, step))))),
|
|
905
|
+
(resolvedJoinUrl || guideUrl) && (React.createElement("div", { className: "designbase-figma-license-offer__actions" },
|
|
906
|
+
resolvedJoinUrl && (React.createElement(react.Button, { onPress: () => openExternalUrl(resolvedJoinUrl), variant: "primary", size: "m", fullWidth: true }, resolveText(t, { key: 'license.youtube_membership_join' }, '멤버십 가입'))),
|
|
907
|
+
guideUrl && (React.createElement(react.Button, { onPress: () => openExternalUrl(guideUrl), variant: "secondary", size: "m", fullWidth: true },
|
|
908
|
+
React.createElement("span", null, resolveText(t, { key: 'license.youtube_membership_guide' }, '안내보기')),
|
|
909
|
+
React.createElement(icons.ExternalLinkIcon, { size: 16, "aria-hidden": "true" })))))));
|
|
910
|
+
};
|
|
687
911
|
LicenseOffer.displayName = 'LicenseOffer';
|
|
688
912
|
|
|
689
913
|
function maskLicenseKey(value) {
|
|
@@ -699,16 +923,30 @@ function formatDate(value) {
|
|
|
699
923
|
return value;
|
|
700
924
|
return new Intl.DateTimeFormat(undefined, { dateStyle: 'medium' }).format(date);
|
|
701
925
|
}
|
|
702
|
-
function formatScope(value) {
|
|
926
|
+
function formatScope(value, t) {
|
|
703
927
|
if (!value)
|
|
704
928
|
return undefined;
|
|
705
|
-
if (value === 'all-products' || value === 'all_products')
|
|
706
|
-
return '
|
|
929
|
+
if (value === 'all-products' || value === 'all_products') {
|
|
930
|
+
return resolveText(t, { key: 'license.all_plugins' }, '모든 플러그인');
|
|
931
|
+
}
|
|
932
|
+
return value;
|
|
933
|
+
}
|
|
934
|
+
function formatSource(value, t) {
|
|
935
|
+
if (!value)
|
|
936
|
+
return undefined;
|
|
937
|
+
if (value === 'youtube_membership' || value === 'membership') {
|
|
938
|
+
return resolveText(t, { key: 'license.source_youtube_membership' }, 'YouTube 멤버십');
|
|
939
|
+
}
|
|
940
|
+
if (value === 'lemonsqueezy') {
|
|
941
|
+
return resolveText(t, { key: 'license.source_purchase' }, '구매 라이선스');
|
|
942
|
+
}
|
|
707
943
|
return value;
|
|
708
944
|
}
|
|
709
|
-
const LicenseStatusCard = ({ status, licenseKey = '', licenseStatus, licenseSource, accessScope, expiresAt, lastVerifiedAt, validationMessage, allowRevealKey =
|
|
945
|
+
const LicenseStatusCard = ({ status, licenseKey = '', licenseStatus, licenseSource, accessScope, expiresAt, lastVerifiedAt, validationMessage, allowRevealKey = false, t, className, }) => {
|
|
710
946
|
const [isKeyRevealed, setIsKeyRevealed] = React.useState(false);
|
|
711
|
-
const
|
|
947
|
+
const normalizedLicenseStatus = licenseStatus?.toLowerCase();
|
|
948
|
+
const isInactiveStatus = ['expired', 'revoked', 'invalid', 'inactive'].includes(normalizedLicenseStatus || '');
|
|
949
|
+
const isActive = status === 'PAID' && !isInactiveStatus;
|
|
712
950
|
const statusText = isActive
|
|
713
951
|
? resolveText(t, { key: 'license.status_active' }, 'Active')
|
|
714
952
|
: resolveText(t, { key: 'license.status_inactive' }, 'License check failed');
|
|
@@ -716,30 +954,82 @@ const LicenseStatusCard = ({ status, licenseKey = '', licenseStatus, licenseSour
|
|
|
716
954
|
return (React.createElement("section", { className: `designbase-figma-license-status-card designbase-figma-license-status-card--${statusClass} ${className || ''}`, "aria-label": statusText },
|
|
717
955
|
React.createElement("div", { className: "designbase-figma-license-status-card__header" },
|
|
718
956
|
React.createElement("div", null,
|
|
719
|
-
React.createElement("div", { className: "designbase-figma-license-status-card__eyebrow" }, isActive
|
|
957
|
+
React.createElement("div", { className: "designbase-figma-license-status-card__eyebrow" }, isActive
|
|
958
|
+
? resolveText(t, { key: 'license.activated' }, '라이선스 활성화됨')
|
|
959
|
+
: resolveText(t, { key: 'license.status_label' }, '라이선스 상태')),
|
|
720
960
|
React.createElement("h3", null, statusText)),
|
|
721
961
|
React.createElement("span", { className: "designbase-figma-license-status-card__badge" }, licenseStatus || (isActive ? 'active' : 'inactive'))),
|
|
722
962
|
validationMessage && !isActive && (React.createElement("p", { className: "designbase-figma-license-status-card__message" }, validationMessage)),
|
|
723
963
|
licenseKey && (React.createElement("div", { className: "designbase-figma-license-status-card__key-row" },
|
|
724
964
|
React.createElement("span", null, resolveText(t, { key: 'license.key_label' }, '라이선스 키')),
|
|
725
965
|
React.createElement("code", null, allowRevealKey && isKeyRevealed ? licenseKey : maskLicenseKey(licenseKey)),
|
|
726
|
-
allowRevealKey && (React.createElement(react.Button, { type: "button", onPress: () => setIsKeyRevealed((value) => !value), variant: "tertiary", size: "s" }, isKeyRevealed
|
|
966
|
+
allowRevealKey && (React.createElement(react.Button, { type: "button", onPress: () => setIsKeyRevealed((value) => !value), variant: "tertiary", size: "s" }, isKeyRevealed
|
|
967
|
+
? resolveText(t, { key: 'license.hide_key' }, '숨기기')
|
|
968
|
+
: resolveText(t, { key: 'license.reveal_key' }, '보기'))))),
|
|
727
969
|
(licenseSource || accessScope || expiresAt || lastVerifiedAt) && (React.createElement("dl", { className: "designbase-figma-license-status-card__meta" },
|
|
728
|
-
licenseSource && React.createElement(React.Fragment, null,
|
|
729
|
-
React.createElement("dt", null,
|
|
730
|
-
React.createElement("dd", null, licenseSource)),
|
|
731
|
-
formatScope(accessScope) && React.createElement(React.Fragment, null,
|
|
732
|
-
React.createElement("dt", null,
|
|
733
|
-
React.createElement("dd", null, formatScope(accessScope))),
|
|
970
|
+
formatSource(licenseSource, t) && React.createElement(React.Fragment, null,
|
|
971
|
+
React.createElement("dt", null, resolveText(t, { key: 'license.source' }, '출처')),
|
|
972
|
+
React.createElement("dd", null, formatSource(licenseSource, t))),
|
|
973
|
+
formatScope(accessScope, t) && React.createElement(React.Fragment, null,
|
|
974
|
+
React.createElement("dt", null, resolveText(t, { key: 'license.access' }, '접근 범위')),
|
|
975
|
+
React.createElement("dd", null, formatScope(accessScope, t))),
|
|
734
976
|
expiresAt && React.createElement(React.Fragment, null,
|
|
735
|
-
React.createElement("dt", null,
|
|
977
|
+
React.createElement("dt", null, resolveText(t, { key: 'license.expires' }, '만료일')),
|
|
736
978
|
React.createElement("dd", null, formatDate(expiresAt) || expiresAt)),
|
|
737
979
|
lastVerifiedAt && React.createElement(React.Fragment, null,
|
|
738
|
-
React.createElement("dt", null,
|
|
980
|
+
React.createElement("dt", null, resolveText(t, { key: 'license.last_checked' }, '최근 확인')),
|
|
739
981
|
React.createElement("dd", null, formatDate(lastVerifiedAt) || lastVerifiedAt))))));
|
|
740
982
|
};
|
|
741
983
|
LicenseStatusCard.displayName = 'LicenseStatusCard';
|
|
742
984
|
|
|
985
|
+
function resolvePaymentFeedbackVariant(errorCode, explicitVariant) {
|
|
986
|
+
if (explicitVariant)
|
|
987
|
+
return explicitVariant;
|
|
988
|
+
if (!errorCode)
|
|
989
|
+
return 'success';
|
|
990
|
+
const normalized = errorCode.toLowerCase();
|
|
991
|
+
if (normalized.includes('network')
|
|
992
|
+
|| normalized.includes('server')
|
|
993
|
+
|| normalized.includes('rate')
|
|
994
|
+
|| normalized.includes('timeout')) {
|
|
995
|
+
return 'warning';
|
|
996
|
+
}
|
|
997
|
+
if (normalized.includes('info')) {
|
|
998
|
+
return 'info';
|
|
999
|
+
}
|
|
1000
|
+
return 'error';
|
|
1001
|
+
}
|
|
1002
|
+
const PaymentStatusSection = ({ activationLimit, activationUsage, licenseKey, onDeactivate, isDeactivating = false, showDetails = false, actionMessage, actionMessageVariant, errorCode, className, t, }) => {
|
|
1003
|
+
const remainingActivations = Math.max(0, activationLimit - activationUsage);
|
|
1004
|
+
const resolvedActionMessage = actionMessage ? resolveText(t, actionMessage) : undefined;
|
|
1005
|
+
const feedbackVariant = resolvedActionMessage
|
|
1006
|
+
? resolvePaymentFeedbackVariant(errorCode, actionMessageVariant)
|
|
1007
|
+
: undefined;
|
|
1008
|
+
return (React.createElement("div", { className: clsx('designbase-figma-payment-status', className) },
|
|
1009
|
+
React.createElement("div", { className: "designbase-figma-payment-status__license-key" },
|
|
1010
|
+
resolveText(t, { key: 'payment.license_key_label' }, '라이선스 키'),
|
|
1011
|
+
": ",
|
|
1012
|
+
React.createElement("span", null, licenseKey)),
|
|
1013
|
+
showDetails && (React.createElement("div", { className: "designbase-figma-payment-status__details" },
|
|
1014
|
+
React.createElement("div", { className: "designbase-figma-payment-status__activation-info" },
|
|
1015
|
+
React.createElement("div", { className: "designbase-figma-payment-status__remaining" },
|
|
1016
|
+
remainingActivations,
|
|
1017
|
+
' ',
|
|
1018
|
+
resolveText(t, { key: 'payment.activations_remaining' }, '활성'),
|
|
1019
|
+
' ',
|
|
1020
|
+
resolveText(t, { key: 'payment.spots_remaining' }, '자리 남음')),
|
|
1021
|
+
React.createElement("div", { className: "designbase-figma-payment-status__usage" },
|
|
1022
|
+
activationUsage,
|
|
1023
|
+
"/",
|
|
1024
|
+
activationLimit)),
|
|
1025
|
+
React.createElement("div", { className: "designbase-figma-payment-status__action" },
|
|
1026
|
+
React.createElement(react.Button, { onPress: onDeactivate, variant: "secondary", size: "s", fullWidth: true, disabled: isDeactivating }, isDeactivating
|
|
1027
|
+
? resolveText(t, { key: 'payment.deactivating' }, '비활성화중...')
|
|
1028
|
+
: resolveText(t, { key: 'payment.deactivate_license' }, '라이선스 비활성화')),
|
|
1029
|
+
resolvedActionMessage && feedbackVariant && (React.createElement("p", { className: clsx('designbase-figma-payment-status__feedback', `designbase-figma-payment-status__feedback--${feedbackVariant}`), role: feedbackVariant === 'error' ? 'alert' : 'status', "aria-live": "polite" }, resolvedActionMessage)))))));
|
|
1030
|
+
};
|
|
1031
|
+
PaymentStatusSection.displayName = 'PaymentStatusSection';
|
|
1032
|
+
|
|
743
1033
|
const defaultFeatures = [
|
|
744
1034
|
{ name: '사용 제한', free: '일일 제한', pro: '무제한' },
|
|
745
1035
|
{ name: '모든 주제 사용', free: false, pro: true },
|
|
@@ -934,15 +1224,27 @@ function useFigmaLicenseBridge(options = {}) {
|
|
|
934
1224
|
if (type === messageTypes.verifyResult) {
|
|
935
1225
|
setIsVerifying(false);
|
|
936
1226
|
setIsStartingAuth(false);
|
|
937
|
-
applyPatch(
|
|
1227
|
+
applyPatch({
|
|
1228
|
+
...parseLicenseMessage(payload),
|
|
1229
|
+
deactivateMessage: '',
|
|
1230
|
+
deactivateErrorCode: '',
|
|
1231
|
+
});
|
|
938
1232
|
if (payload.success === true) {
|
|
939
1233
|
applyPatch({ paymentStatus: 'PAID', isLicenseRegistered: true });
|
|
940
1234
|
}
|
|
941
1235
|
}
|
|
942
1236
|
if (type === messageTypes.deactivateResult) {
|
|
943
1237
|
setIsDeactivating(false);
|
|
944
|
-
|
|
945
|
-
|
|
1238
|
+
const parsed = parseLicenseMessage(payload);
|
|
1239
|
+
const isSuccess = payload.success === true;
|
|
1240
|
+
applyPatch({
|
|
1241
|
+
...parsed,
|
|
1242
|
+
deactivateMessage: parsed.lastMessage,
|
|
1243
|
+
deactivateErrorCode: isSuccess ? undefined : parsed.lastErrorCode,
|
|
1244
|
+
lastMessage: isSuccess ? undefined : parsed.lastMessage,
|
|
1245
|
+
lastErrorCode: isSuccess ? undefined : parsed.lastErrorCode,
|
|
1246
|
+
});
|
|
1247
|
+
if (isSuccess) {
|
|
946
1248
|
applyPatch({
|
|
947
1249
|
paymentStatus: 'UNPAID',
|
|
948
1250
|
licenseKey: '',
|
|
@@ -950,6 +1252,8 @@ function useFigmaLicenseBridge(options = {}) {
|
|
|
950
1252
|
isLicenseRegistered: false,
|
|
951
1253
|
activationLimit: 0,
|
|
952
1254
|
activationUsage: 0,
|
|
1255
|
+
deactivateMessage: parsed.lastMessage || '라이선스가 비활성화되었습니다.',
|
|
1256
|
+
deactivateErrorCode: undefined,
|
|
953
1257
|
});
|
|
954
1258
|
}
|
|
955
1259
|
}
|
|
@@ -976,8 +1280,12 @@ function useFigmaLicenseBridge(options = {}) {
|
|
|
976
1280
|
}, [messageTypes.verifyLicense]);
|
|
977
1281
|
const deactivateLicense = React.useCallback(() => {
|
|
978
1282
|
setIsDeactivating(true);
|
|
1283
|
+
applyPatch({
|
|
1284
|
+
deactivateMessage: '',
|
|
1285
|
+
deactivateErrorCode: '',
|
|
1286
|
+
});
|
|
979
1287
|
postPluginMessage({ type: messageTypes.deactivateLicense });
|
|
980
|
-
}, [messageTypes.deactivateLicense]);
|
|
1288
|
+
}, [applyPatch, messageTypes.deactivateLicense]);
|
|
981
1289
|
const startDesignbaseAuth = React.useCallback(() => {
|
|
982
1290
|
setIsStartingAuth(true);
|
|
983
1291
|
postPluginMessage({ type: START_DESIGNBASE_AUTH_MESSAGE });
|
|
@@ -1047,7 +1355,7 @@ const PageLicense = ({ status: initialStatus, onClose, licenseKey: initialLicens
|
|
|
1047
1355
|
setLicenseKeyDraft(value);
|
|
1048
1356
|
setLicenseKeyInput(value);
|
|
1049
1357
|
};
|
|
1050
|
-
return (React.createElement("div", { className: `designbase-figma-page-license ${className || ''}
|
|
1358
|
+
return (React.createElement("div", { className: `designbase-figma-page-license designbase-figma-page-license--${mode} ${className || ''}`, "data-license-mode": mode },
|
|
1051
1359
|
React.createElement("div", { className: "designbase-figma-page-license__content" },
|
|
1052
1360
|
React.createElement("div", { className: "designbase-figma-page-license__header" },
|
|
1053
1361
|
React.createElement("div", { className: "designbase-figma-page-license__title" },
|
|
@@ -1062,15 +1370,15 @@ const PageLicense = ({ status: initialStatus, onClose, licenseKey: initialLicens
|
|
|
1062
1370
|
: isMembershipMode
|
|
1063
1371
|
? resolveText(t, { key: 'license.youtube_membership_description' }, 'YouTube 멤버십에 가입하면 Designbase 플러그인을 무제한으로 사용할 수 있습니다.')
|
|
1064
1372
|
: resolveText(t, { key: 'license.purchase_for_unlimited' }, '디자인베이스 멤버십 또는 라이선스로 무제한 이용이 가능합니다.')),
|
|
1065
|
-
!isPaid && (React.createElement("p", { className: "designbase-figma-page-license__usage" }, usageLabel))),
|
|
1373
|
+
!isPaid && !isMembershipMode && (React.createElement("p", { className: "designbase-figma-page-license__usage" }, usageLabel))),
|
|
1066
1374
|
!isMembershipMode && (React.createElement(PricingComparison, { features: pricingFeatures, pricing: pricing, paymentType: paymentType, t: t })),
|
|
1067
|
-
state.lastMessage && (React.createElement("p", { className: `designbase-figma-page-license__feedback ${state.lastErrorCode ? 'designbase-figma-page-license__feedback--error' : ''}
|
|
1375
|
+
state.lastMessage && !state.deactivateMessage && (React.createElement("p", { className: `designbase-figma-page-license__feedback ${state.lastErrorCode ? 'designbase-figma-page-license__feedback--error' : ''}`, "aria-live": "polite" }, state.lastMessage)),
|
|
1068
1376
|
isPaid && (React.createElement("div", { className: "designbase-figma-page-license__section" },
|
|
1069
1377
|
React.createElement("div", { className: "designbase-figma-page-license__section-header" },
|
|
1070
1378
|
React.createElement("h3", null, resolveText(t, { key: 'license.activated' }, '라이선스 활성화됨')),
|
|
1071
1379
|
React.createElement(react.Button, { onPress: () => setShowDetails(!showDetails), variant: "tertiary", size: "s", iconOnly: true },
|
|
1072
1380
|
React.createElement(icons.MoreHorizontalIcon, { size: 16 }))),
|
|
1073
|
-
React.createElement(LicenseStatusCard, { status: state.paymentStatus, licenseKey: state.licenseKey, licenseStatus: state.licenseStatus, licenseSource: state.licenseSource, accessScope: state.accessScope, expiresAt: state.expiresAt, lastVerifiedAt: state.lastVerifiedAt, validationMessage: state.validationMessage, t: t }),
|
|
1381
|
+
React.createElement(LicenseStatusCard, { status: state.paymentStatus, licenseKey: state.licenseKey, licenseStatus: state.licenseStatus, licenseSource: state.licenseSource, accessScope: state.accessScope, expiresAt: state.expiresAt, lastVerifiedAt: state.lastVerifiedAt, validationMessage: state.validationMessage, allowRevealKey: false, t: t }),
|
|
1074
1382
|
React.createElement("p", { className: "designbase-figma-page-license__usage" }, usageLabel),
|
|
1075
1383
|
showDetails && (React.createElement("div", { className: "designbase-figma-page-license__details" },
|
|
1076
1384
|
React.createElement("div", { className: "designbase-figma-page-license__activation-info" },
|
|
@@ -1084,11 +1392,13 @@ const PageLicense = ({ status: initialStatus, onClose, licenseKey: initialLicens
|
|
|
1084
1392
|
state.activationUsage,
|
|
1085
1393
|
"/",
|
|
1086
1394
|
state.activationLimit)),
|
|
1087
|
-
React.createElement(
|
|
1088
|
-
|
|
1089
|
-
|
|
1395
|
+
React.createElement("div", { className: "designbase-figma-page-license__action" },
|
|
1396
|
+
React.createElement(react.Button, { onPress: deactivateLicense, variant: "secondary", size: "s", fullWidth: true, disabled: isDeactivating }, isDeactivating
|
|
1397
|
+
? resolveText(t, { key: 'license.deactivating' }, '비활성화 중...')
|
|
1398
|
+
: resolveText(t, { key: 'license.deactivate' }, '라이선스 비활성화')),
|
|
1399
|
+
state.deactivateMessage && (React.createElement("p", { className: `designbase-figma-page-license__action-feedback designbase-figma-page-license__action-feedback--${resolvePaymentFeedbackVariant(state.deactivateErrorCode)}`, role: state.deactivateErrorCode ? 'alert' : 'status', "aria-live": "polite" }, state.deactivateMessage))))))),
|
|
1090
1400
|
!isPaid && (React.createElement(React.Fragment, null,
|
|
1091
|
-
isMembershipMode && (React.createElement(LicenseOffer, {
|
|
1401
|
+
isMembershipMode && (React.createElement(LicenseOffer, { joinUrl: links.membershipUrl || links.purchaseUrl, guideUrl: links.helpUrl || links.purchaseUrl, t: t })),
|
|
1092
1402
|
!isMembershipMode && (enableAccountAuth || links.purchaseUrl || links.accountUrl) && (React.createElement("div", { className: "designbase-figma-page-license__section" },
|
|
1093
1403
|
React.createElement("div", { className: "designbase-figma-page-license__section-header" },
|
|
1094
1404
|
React.createElement("h3", null, resolveText(t, { key: 'license.designbase_account' }, '디자인베이스 계정'))),
|
|
@@ -1103,41 +1413,33 @@ const PageLicense = ({ status: initialStatus, onClose, licenseKey: initialLicens
|
|
|
1103
1413
|
links.accountUrl && (React.createElement(react.Button, { onPress: () => openExternalUrl(links.accountUrl), variant: "tertiary", size: "s", fullWidth: true },
|
|
1104
1414
|
React.createElement("span", null, resolveText(t, { key: 'license.manage_account' }, '계정·멤버십 관리')),
|
|
1105
1415
|
React.createElement(icons.ExternalLinkIcon, { size: 16 })))))),
|
|
1106
|
-
showLicenseKeyInput && (React.createElement("div", { className:
|
|
1416
|
+
showLicenseKeyInput && (React.createElement("div", { className: isMembershipMode
|
|
1417
|
+
? 'designbase-figma-page-license__key-form'
|
|
1418
|
+
: 'designbase-figma-page-license__section' },
|
|
1107
1419
|
React.createElement("div", { className: "designbase-figma-page-license__section-header" },
|
|
1108
1420
|
React.createElement("h3", null, resolveText(t, { key: 'license.enter_key' }, '라이선스 키 입력'))),
|
|
1109
|
-
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 }))))),
|
|
1110
|
-
links.helpUrl && (React.createElement("p", { className: "designbase-figma-page-license__disclaimer" },
|
|
1421
|
+
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: isMembershipMode ? 'license.enter_from_youtube' : 'license.enter_from_email' }, t: t }))))),
|
|
1422
|
+
links.helpUrl && !isMembershipMode && (React.createElement("p", { className: "designbase-figma-page-license__disclaimer" },
|
|
1111
1423
|
React.createElement("button", { type: "button", className: "designbase-figma-page-license__link", onClick: () => openExternalUrl(links.helpUrl) }, resolveText(t, { key: 'license.help_link' }, '라이선스·결제 도움말'))))),
|
|
1112
|
-
React.createElement(react.Button, { className: "designbase-figma-page-license__close", onPress: onClose, variant: "tertiary", size: "s", "aria-label": "Close" },
|
|
1113
|
-
React.createElement(icons.CloseIcon, { size:
|
|
1424
|
+
React.createElement(react.Button, { className: "designbase-figma-page-license__close", onPress: onClose, variant: "tertiary", size: "s", iconOnly: true, "aria-label": "Close" },
|
|
1425
|
+
React.createElement(icons.CloseIcon, { size: 16 }))));
|
|
1114
1426
|
};
|
|
1115
1427
|
PageLicense.displayName = 'PageLicense';
|
|
1116
1428
|
|
|
1117
|
-
const
|
|
1118
|
-
const
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
resolveText(t, { key: 'payment.activations_remaining' }, '활성'),
|
|
1130
|
-
" ",
|
|
1131
|
-
resolveText(t, { key: 'payment.spots_remaining' }, '자리 남음')),
|
|
1132
|
-
React.createElement("div", { className: "designbase-figma-payment-status__usage" },
|
|
1133
|
-
activationUsage,
|
|
1134
|
-
"/",
|
|
1135
|
-
activationLimit)),
|
|
1136
|
-
React.createElement(react.Button, { onPress: onDeactivate, variant: "tertiary", size: "s", disabled: isDeactivating }, isDeactivating
|
|
1137
|
-
? resolveText(t, { key: 'payment.deactivating' }, '비활성화중...')
|
|
1138
|
-
: resolveText(t, { key: 'payment.deactivate_license' }, '라이선스 비활성화'))))));
|
|
1429
|
+
const PaymentBadge = ({ isActive, onClick, isLoading = false, text, t = (key) => key, className, }) => {
|
|
1430
|
+
const getBadgeText = () => {
|
|
1431
|
+
if (text)
|
|
1432
|
+
return text;
|
|
1433
|
+
return isActive
|
|
1434
|
+
? (t('proUser') || '프로 계정')
|
|
1435
|
+
: (t('usingFree') || '무료 사용');
|
|
1436
|
+
};
|
|
1437
|
+
const getBadgeVariant = () => {
|
|
1438
|
+
return isActive ? 'success' : 'secondary';
|
|
1439
|
+
};
|
|
1440
|
+
return (React.createElement("div", { className: `designbase-figma-payment-badge ${className || ''} ${isActive ? 'active' : 'free'}`, onClick: onClick, style: { cursor: onClick ? 'pointer' : 'default' } }, isLoading ? (React.createElement(react.Spinner, { size: "s" })) : (React.createElement(react.Badge, { variant: getBadgeVariant(), size: "s", className: "designbase-figma-payment-badge__badge" }, getBadgeText()))));
|
|
1139
1441
|
};
|
|
1140
|
-
|
|
1442
|
+
PaymentBadge.displayName = 'PaymentBadge';
|
|
1141
1443
|
|
|
1142
1444
|
const ProgressModal = ({ isOpen, onClose, progress, title = { key: 'progress.working' }, completedMessage = { key: 'progress.completed' }, processingMessage = { key: 'progress.processing' }, stopButtonText = { key: 'progress.stop' }, confirmButtonText = { key: 'progress.confirm' }, helpText, onStop, onComplete, loadingIcon, completeIcon, className, t, autoComplete = true, autoCloseOnComplete = false }) => {
|
|
1143
1445
|
const [isCompleted, setIsCompleted] = React.useState(false);
|
|
@@ -1181,7 +1483,7 @@ const ProgressModal = ({ isOpen, onClose, progress, title = { key: 'progress.wor
|
|
|
1181
1483
|
React.createElement(react.Spinner, { size: "m" }))))),
|
|
1182
1484
|
React.createElement("p", { className: "designbase-figma-progress-modal__message" }, displayMessage),
|
|
1183
1485
|
React.createElement("div", { className: "designbase-figma-progress-modal__status" }, `${processedNodes} / ${totalNodes} (${progressPercentage.toFixed(1)}%)`),
|
|
1184
|
-
React.createElement(react.Progressbar, { value: progressPercentage, size: "m", variant: isCompleted ? 'success' : 'primary',
|
|
1486
|
+
React.createElement(react.Progressbar, { value: progressPercentage, size: "m", variant: isCompleted ? 'success' : 'primary', barStyle: "animated", fullWidth: true }),
|
|
1185
1487
|
displayHelpText && !isCompleted && (React.createElement("p", { className: "designbase-figma-progress-modal__help-text" }, displayHelpText)),
|
|
1186
1488
|
React.createElement("div", { className: "designbase-figma-progress-modal__actions" }, isCompleted ? (React.createElement(react.Button, { onPress: handleConfirm, variant: "primary", size: "m", fullWidth: true }, displayConfirmText)) : (React.createElement(react.Button, { onPress: handleStop, variant: "secondary", size: "m", fullWidth: true }, displayStopText))))));
|
|
1187
1489
|
};
|
|
@@ -1933,6 +2235,7 @@ exports.FigmaToastProvider = FigmaToastProvider;
|
|
|
1933
2235
|
exports.FormWithSubmit = FormWithSubmit;
|
|
1934
2236
|
exports.InteractionFeedback = InteractionFeedback;
|
|
1935
2237
|
exports.LanguageSelector = LanguageSelector;
|
|
2238
|
+
exports.LanguageSettingsModal = LanguageSettingsModal;
|
|
1936
2239
|
exports.LicenseOffer = LicenseOffer;
|
|
1937
2240
|
exports.LicenseStatusCard = LicenseStatusCard;
|
|
1938
2241
|
exports.LogoDropdown = LogoDropdown;
|
|
@@ -1946,7 +2249,10 @@ exports.START_DESIGNBASE_AUTH_MESSAGE = START_DESIGNBASE_AUTH_MESSAGE;
|
|
|
1946
2249
|
exports.SettingsModal = SettingsModal;
|
|
1947
2250
|
exports.TitleDescription = TitleDescription;
|
|
1948
2251
|
exports.UpgradeBanner = UpgradeBanner;
|
|
2252
|
+
exports.UsageStatusPopover = UsageStatusPopover;
|
|
1949
2253
|
exports.getActiveSection = getActiveSection;
|
|
2254
|
+
exports.interpolateText = interpolateText;
|
|
2255
|
+
exports.resolvePaymentFeedbackVariant = resolvePaymentFeedbackVariant;
|
|
1950
2256
|
exports.resolveText = resolveText;
|
|
1951
2257
|
exports.scrollToSection = scrollToSection;
|
|
1952
2258
|
exports.useFigmaLicenseBridge = useFigmaLicenseBridge;
|