@designbasekorea/figma-ui 0.6.6 → 0.6.9
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 +459 -146
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +459 -142
- 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,70 @@ 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 resolvedCurrentLanguage = currentLanguage || i18n?.language || languages[0]?.code;
|
|
423
|
+
const [pendingLanguage, setPendingLanguage] = React.useState(resolvedCurrentLanguage);
|
|
424
|
+
React.useEffect(() => {
|
|
425
|
+
if (isOpen) {
|
|
426
|
+
setPendingLanguage(resolvedCurrentLanguage);
|
|
427
|
+
}
|
|
428
|
+
}, [isOpen, resolvedCurrentLanguage]);
|
|
429
|
+
const translate = (key, fallback) => {
|
|
430
|
+
const translated = t(key);
|
|
431
|
+
return translated && translated !== key ? translated : fallback;
|
|
432
|
+
};
|
|
433
|
+
const handleSelect = React.useCallback((languageCode) => {
|
|
434
|
+
setPendingLanguage(languageCode);
|
|
417
435
|
if (i18n) {
|
|
418
436
|
i18n.changeLanguage(languageCode);
|
|
419
437
|
}
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
? detail.value
|
|
435
|
-
: undefined;
|
|
436
|
-
if (value) {
|
|
437
|
-
handleLanguageChange(value);
|
|
438
|
-
}
|
|
439
|
-
}, size: size, className: "designbase-figma-language-selector__segment" })));
|
|
438
|
+
onLanguageChange?.(languageCode);
|
|
439
|
+
onClose();
|
|
440
|
+
}, [i18n, onClose, onLanguageChange]);
|
|
441
|
+
return (React.createElement(react.Modal, { isOpen: isOpen, onClose: onClose, title: title || translate('languageSettings', '언어 설정'), size: "s", className: clsx('designbase-figma-language-settings-modal', className) },
|
|
442
|
+
React.createElement(react.ModalBody, { className: "designbase-figma-language-settings-modal__content" },
|
|
443
|
+
React.createElement("p", { className: "designbase-figma-language-settings-modal__description" }, description || translate('languageSettingsDescription', '플러그인에 표시할 언어를 선택하세요.')),
|
|
444
|
+
React.createElement("ul", { className: "designbase-figma-language-settings-modal__list", role: "listbox", "aria-label": translate('languageSettings', '언어 설정') }, languages.map((language) => {
|
|
445
|
+
const label = resolveText(t, language.label, language.code.toUpperCase());
|
|
446
|
+
const isActive = language.code === pendingLanguage;
|
|
447
|
+
return (React.createElement("li", { key: language.code, role: "presentation" },
|
|
448
|
+
React.createElement(react.Button, { type: "button", variant: "tertiary", size: "m", fullWidth: true, className: clsx('designbase-figma-language-settings-modal__option', { 'designbase-figma-language-settings-modal__option--active': isActive }), role: "option", "aria-selected": isActive, onPress: () => handleSelect(language.code) },
|
|
449
|
+
React.createElement("span", { className: "designbase-figma-language-settings-modal__option-label" }, label),
|
|
450
|
+
React.createElement("span", { className: "designbase-figma-language-settings-modal__option-indicator", "aria-hidden": "true" }))));
|
|
451
|
+
})))));
|
|
440
452
|
};
|
|
441
|
-
|
|
453
|
+
LanguageSettingsModal.displayName = 'LanguageSettingsModal';
|
|
442
454
|
|
|
455
|
+
let useTranslation$1 = null;
|
|
456
|
+
try {
|
|
457
|
+
const reactI18next = require('react-i18next');
|
|
458
|
+
useTranslation$1 = reactI18next.useTranslation;
|
|
459
|
+
}
|
|
460
|
+
catch (e) {
|
|
461
|
+
}
|
|
443
462
|
const defaultLinks = [
|
|
444
463
|
{
|
|
445
464
|
name: 'officialWebsite',
|
|
@@ -473,50 +492,223 @@ const defaultTranslations$1 = {
|
|
|
473
492
|
instagram: '인스타그램',
|
|
474
493
|
figmaCommunity: '피그마 커뮤니티',
|
|
475
494
|
contact: '문의하기',
|
|
495
|
+
languageSettings: '언어 설정',
|
|
476
496
|
};
|
|
477
497
|
const defaultT$1 = (key) => defaultTranslations$1[key] || key;
|
|
478
|
-
const
|
|
498
|
+
const defaultLanguages = [
|
|
499
|
+
{ code: 'ko', label: '한국어' },
|
|
500
|
+
{ code: 'en', label: 'English' },
|
|
501
|
+
{ code: 'ja', label: '日本語' },
|
|
502
|
+
];
|
|
503
|
+
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
504
|
const [isOpen, setIsOpen] = React.useState(false);
|
|
480
|
-
const
|
|
505
|
+
const [isLanguageModalOpen, setIsLanguageModalOpen] = React.useState(false);
|
|
506
|
+
const [internalLanguage, setInternalLanguage] = React.useState();
|
|
507
|
+
const i18n = useTranslation$1 ? useTranslation$1().i18n : null;
|
|
508
|
+
const rootRef = React.useRef(null);
|
|
509
|
+
const triggerRef = React.useRef(null);
|
|
510
|
+
const menuId = `designbase-figma-logo-menu-${React.useId().replace(/:/g, '')}`;
|
|
511
|
+
const activeLanguage = currentLanguage ?? internalLanguage ?? i18n?.language ?? languages[0]?.code;
|
|
512
|
+
const translate = (key, fallback) => {
|
|
513
|
+
const translated = t(key);
|
|
514
|
+
return translated && translated !== key ? translated : fallback;
|
|
515
|
+
};
|
|
516
|
+
React.useEffect(() => {
|
|
517
|
+
if (!isOpen) {
|
|
518
|
+
return undefined;
|
|
519
|
+
}
|
|
520
|
+
const handlePointerDown = (event) => {
|
|
521
|
+
if (!rootRef.current?.contains(event.target)) {
|
|
522
|
+
setIsOpen(false);
|
|
523
|
+
}
|
|
524
|
+
};
|
|
525
|
+
const handleKeyDown = (event) => {
|
|
526
|
+
if (event.key === 'Escape') {
|
|
527
|
+
setIsOpen(false);
|
|
528
|
+
triggerRef.current?.focus();
|
|
529
|
+
}
|
|
530
|
+
};
|
|
531
|
+
document.addEventListener('mousedown', handlePointerDown);
|
|
532
|
+
document.addEventListener('keydown', handleKeyDown);
|
|
533
|
+
return () => {
|
|
534
|
+
document.removeEventListener('mousedown', handlePointerDown);
|
|
535
|
+
document.removeEventListener('keydown', handleKeyDown);
|
|
536
|
+
};
|
|
537
|
+
}, [isOpen]);
|
|
538
|
+
const toggleDropdown = () => setIsOpen((value) => !value);
|
|
481
539
|
const handleLinkClick = (url) => {
|
|
540
|
+
setIsOpen(false);
|
|
482
541
|
if (url.startsWith('mailto:')) {
|
|
483
542
|
return;
|
|
484
543
|
}
|
|
485
544
|
window.open(url, '_blank', 'noopener,noreferrer');
|
|
486
545
|
};
|
|
546
|
+
const handleLanguageSettingsClick = () => {
|
|
547
|
+
setIsOpen(false);
|
|
548
|
+
setIsLanguageModalOpen(true);
|
|
549
|
+
};
|
|
550
|
+
const handleLanguageChange = (languageCode) => {
|
|
551
|
+
if (currentLanguage === undefined) {
|
|
552
|
+
setInternalLanguage(languageCode);
|
|
553
|
+
}
|
|
554
|
+
if (i18n) {
|
|
555
|
+
i18n.changeLanguage(languageCode);
|
|
556
|
+
}
|
|
557
|
+
onLanguageChange?.(languageCode);
|
|
558
|
+
};
|
|
487
559
|
const classes = clsx('designbase-figma-logo-dropdown', `designbase-figma-logo-dropdown--${position}`, {
|
|
488
560
|
'designbase-figma-logo-dropdown--open': isOpen,
|
|
489
561
|
}, className);
|
|
490
562
|
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" })));
|
|
563
|
+
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
564
|
};
|
|
497
|
-
return (React.createElement("div", { className: classes },
|
|
565
|
+
return (React.createElement("div", { ref: rootRef, className: classes },
|
|
498
566
|
renderLogo(),
|
|
499
|
-
isOpen && (React.createElement("ul", { className: "designbase-figma-logo-dropdown__menu"
|
|
500
|
-
React.createElement("
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
567
|
+
isOpen && (React.createElement("ul", { id: menuId, className: "designbase-figma-logo-dropdown__menu", role: "menu" },
|
|
568
|
+
links.map((link, index) => (React.createElement("li", { key: index, className: "designbase-figma-logo-dropdown__item", role: "none" },
|
|
569
|
+
React.createElement("button", { type: "button", className: "designbase-figma-logo-dropdown__link", role: "menuitem", onClick: () => handleLinkClick(link.url) },
|
|
570
|
+
link.icon && (React.createElement("span", { className: "designbase-figma-logo-dropdown__icon" }, link.icon)),
|
|
571
|
+
React.createElement("span", { className: "designbase-figma-logo-dropdown__text" }, t(link.name)),
|
|
572
|
+
React.createElement(icons.ExternalLinkIcon, { size: 16, className: "designbase-figma-logo-dropdown__arrow" }))))),
|
|
573
|
+
showLanguageSelector && (React.createElement("li", { className: "designbase-figma-logo-dropdown__item designbase-figma-logo-dropdown__language-item", role: "none" },
|
|
574
|
+
React.createElement("button", { type: "button", className: "designbase-figma-logo-dropdown__link", role: "menuitem", onClick: handleLanguageSettingsClick },
|
|
575
|
+
React.createElement("span", { className: "designbase-figma-logo-dropdown__icon" },
|
|
576
|
+
React.createElement(icons.TranslateIcon, { size: 20, "aria-hidden": "true" })),
|
|
577
|
+
React.createElement("span", { className: "designbase-figma-logo-dropdown__text" }, translate('languageSettings', '언어 설정'))))))),
|
|
578
|
+
showLanguageSelector && (React.createElement(LanguageSettingsModal, { isOpen: isLanguageModalOpen, onClose: () => setIsLanguageModalOpen(false), languages: languages, currentLanguage: activeLanguage, onLanguageChange: handleLanguageChange, t: t }))));
|
|
504
579
|
};
|
|
505
580
|
LogoDropdown.displayName = 'LogoDropdown';
|
|
506
581
|
|
|
582
|
+
const fallbackText = (t, key, fallback) => {
|
|
583
|
+
const translated = t?.(key);
|
|
584
|
+
return translated && translated !== key ? translated : fallback;
|
|
585
|
+
};
|
|
586
|
+
const UsageStatusPopover = ({ paymentStatus, usageCount = 0, maxDailyUsage = 20, showUsage = true, showPlan = true, isLoading = false, onLicensePageClick, t, className, }) => {
|
|
587
|
+
const [isOpen, setIsOpen] = React.useState(false);
|
|
588
|
+
const rootRef = React.useRef(null);
|
|
589
|
+
const triggerRef = React.useRef(null);
|
|
590
|
+
const popoverId = `designbase-figma-usage-popover-${React.useId().replace(/:/g, '')}`;
|
|
591
|
+
const isPaid = paymentStatus === 'PAID';
|
|
592
|
+
const isBeta = paymentStatus === 'BETA';
|
|
593
|
+
const hasDailyLimit = !isPaid && maxDailyUsage > 0;
|
|
594
|
+
const limit = Math.max(1, Math.round(maxDailyUsage));
|
|
595
|
+
const used = Math.min(limit, Math.max(0, Math.round(usageCount)));
|
|
596
|
+
const remaining = Math.max(0, limit - used);
|
|
597
|
+
const usedPercent = hasDailyLimit ? Math.round((used / limit) * 100) : 0;
|
|
598
|
+
const hasControl = showUsage || showPlan;
|
|
599
|
+
const planLabel = isPaid
|
|
600
|
+
? fallbackText(t, 'proUser', '프로')
|
|
601
|
+
: isBeta
|
|
602
|
+
? fallbackText(t, 'betaStatus', 'BETA')
|
|
603
|
+
: paymentStatus === 'TRIAL'
|
|
604
|
+
? fallbackText(t, 'trialPlan', '체험판')
|
|
605
|
+
: fallbackText(t, 'free', '무료');
|
|
606
|
+
const planTitle = isBeta
|
|
607
|
+
? planLabel
|
|
608
|
+
: `${planLabel} ${fallbackText(t, 'planSuffix', '플랜')}`;
|
|
609
|
+
const triggerLabel = isPaid
|
|
610
|
+
? `${planLabel}, ${fallbackText(t, 'unlimitedUsage', '무제한 사용 가능')}`
|
|
611
|
+
: isBeta
|
|
612
|
+
? planLabel
|
|
613
|
+
: hasDailyLimit
|
|
614
|
+
? `${remaining}/${limit} ${fallbackText(t, 'remainingShort', '남음')}`
|
|
615
|
+
: planLabel;
|
|
616
|
+
const upgradeLabel = isPaid
|
|
617
|
+
? fallbackText(t, 'managePlan', '플랜 관리')
|
|
618
|
+
: fallbackText(t, 'upgradePlan', '플랜 업그레이드');
|
|
619
|
+
React.useEffect(() => {
|
|
620
|
+
if (!isOpen)
|
|
621
|
+
return undefined;
|
|
622
|
+
const handlePointerDown = (event) => {
|
|
623
|
+
if (!rootRef.current?.contains(event.target)) {
|
|
624
|
+
setIsOpen(false);
|
|
625
|
+
}
|
|
626
|
+
};
|
|
627
|
+
const handleKeyDown = (event) => {
|
|
628
|
+
if (event.key === 'Escape') {
|
|
629
|
+
setIsOpen(false);
|
|
630
|
+
triggerRef.current?.focus();
|
|
631
|
+
}
|
|
632
|
+
};
|
|
633
|
+
document.addEventListener('mousedown', handlePointerDown);
|
|
634
|
+
document.addEventListener('keydown', handleKeyDown);
|
|
635
|
+
return () => {
|
|
636
|
+
document.removeEventListener('mousedown', handlePointerDown);
|
|
637
|
+
document.removeEventListener('keydown', handleKeyDown);
|
|
638
|
+
};
|
|
639
|
+
}, [isOpen]);
|
|
640
|
+
if (!hasControl)
|
|
641
|
+
return null;
|
|
642
|
+
const handleManageClick = () => {
|
|
643
|
+
setIsOpen(false);
|
|
644
|
+
onLicensePageClick?.();
|
|
645
|
+
};
|
|
646
|
+
const togglePopover = () => setIsOpen((value) => !value);
|
|
647
|
+
return (React.createElement("div", { ref: rootRef, className: clsx('designbase-figma-usage-status', {
|
|
648
|
+
'designbase-figma-usage-status--open': isOpen,
|
|
649
|
+
'designbase-figma-usage-status--unlimited': isPaid,
|
|
650
|
+
'designbase-figma-usage-status--loading': isLoading,
|
|
651
|
+
}, className) },
|
|
652
|
+
React.createElement("div", { className: "designbase-figma-usage-status__control" },
|
|
653
|
+
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,
|
|
654
|
+
React.createElement("span", { className: "designbase-figma-usage-status__mini-progress", "aria-hidden": "true" },
|
|
655
|
+
React.createElement("span", { style: { width: `${usedPercent}%` } })),
|
|
656
|
+
React.createElement("span", { className: "designbase-figma-usage-status__usage-value" },
|
|
657
|
+
React.createElement("strong", null, remaining),
|
|
658
|
+
React.createElement("span", null,
|
|
659
|
+
"/",
|
|
660
|
+
limit)))) : (React.createElement("span", { className: "designbase-figma-usage-status__plan-label" }, planLabel)))),
|
|
661
|
+
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))),
|
|
662
|
+
isOpen && (React.createElement("div", { id: popoverId, className: "designbase-figma-usage-status__popover", role: "dialog", "aria-label": fallbackText(t, 'usageDetails', '사용량 상세') },
|
|
663
|
+
React.createElement("div", { className: "designbase-figma-usage-status__popover-header" },
|
|
664
|
+
React.createElement("span", { className: "designbase-figma-usage-status__plan-title" }, planTitle),
|
|
665
|
+
!isBeta && (React.createElement(react.Button, { className: "designbase-figma-usage-status__upgrade", variant: isPaid ? 'secondary' : 'primary', size: "s", endIcon: "chevron-right", onPress: handleManageClick }, upgradeLabel))),
|
|
666
|
+
isPaid ? (React.createElement("div", { className: "designbase-figma-usage-status__unlimited-copy" },
|
|
667
|
+
React.createElement("span", { className: "designbase-figma-usage-status__unlimited-mark", "aria-hidden": "true" }, "\u221E"),
|
|
668
|
+
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" },
|
|
669
|
+
React.createElement("div", { className: "designbase-figma-usage-status__usage-summary" },
|
|
670
|
+
React.createElement("strong", { className: "designbase-figma-usage-status__usage-count" },
|
|
671
|
+
used,
|
|
672
|
+
React.createElement("span", { className: "designbase-figma-usage-status__usage-divider" }, "/"),
|
|
673
|
+
limit),
|
|
674
|
+
React.createElement("span", { className: "designbase-figma-usage-status__usage-caption" }, fallbackText(t, 'usageCountCaption', '오늘 사용'))),
|
|
675
|
+
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}` },
|
|
676
|
+
React.createElement("span", { style: { width: `${usedPercent}%` } })),
|
|
677
|
+
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', '사용량 제한이 없습니다')))))));
|
|
678
|
+
};
|
|
679
|
+
UsageStatusPopover.displayName = 'UsageStatusPopover';
|
|
680
|
+
|
|
507
681
|
const defaultTranslations = {
|
|
508
682
|
unlimitedUsage: '무제한 사용',
|
|
683
|
+
proUser: '프로',
|
|
684
|
+
free: '무료',
|
|
685
|
+
trialPlan: '체험판',
|
|
686
|
+
planSuffix: '플랜',
|
|
687
|
+
usageDetails: '사용량 상세',
|
|
688
|
+
dailyUsage: '오늘 사용량',
|
|
689
|
+
usedToday: '오늘 사용',
|
|
690
|
+
remainingToday: '오늘 남은 사용량',
|
|
691
|
+
remainingUsage: '오늘 남은 사용량',
|
|
692
|
+
managePlan: '플랜 관리',
|
|
693
|
+
upgradePlan: '플랜 업그레이드',
|
|
694
|
+
remainingShort: '남음',
|
|
695
|
+
usageCountCaption: '오늘 사용',
|
|
696
|
+
remainingCopy: '{{count}}회 남음 · 매일 리셋',
|
|
697
|
+
usageUnavailable: '사용량 제한이 없습니다',
|
|
698
|
+
betaUsageDescription: '베타 기간에는 사용량 제한이 적용되지 않습니다',
|
|
509
699
|
perDay: '일',
|
|
510
700
|
resetsDaily: '매일 리셋',
|
|
511
701
|
donationPrompt: '이 플러그인이 유용했나요?',
|
|
512
702
|
betaStatus: 'BETA',
|
|
513
703
|
};
|
|
514
|
-
const defaultT = (key) =>
|
|
515
|
-
const
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
704
|
+
const defaultT = (key, values) => {
|
|
705
|
+
const template = defaultTranslations[key] || key;
|
|
706
|
+
return interpolateText(template, values);
|
|
707
|
+
};
|
|
708
|
+
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, }) => {
|
|
709
|
+
const resolvedShowUsageInfo = showPaymentStatus && showUsageInfo;
|
|
519
710
|
const resolvedShowPaymentBadge = showPaymentStatus && showPaymentBadge;
|
|
711
|
+
const resolvedLicensePageClick = paymentStatus === 'BETA' ? undefined : onLicensePageClick;
|
|
520
712
|
const hasChildren = React.Children.count(children) > 0;
|
|
521
713
|
const classes = clsx('designbase-figma-footer', {
|
|
522
714
|
'designbase-figma-footer--has-children': hasChildren,
|
|
@@ -524,17 +716,9 @@ const FigmaFooter = ({ onLicensePageClick, paymentStatus = 'FREE', usageCount =
|
|
|
524
716
|
return (React.createElement("footer", { className: classes },
|
|
525
717
|
React.createElement("div", { className: "designbase-figma-footer__wrap" },
|
|
526
718
|
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" }))),
|
|
719
|
+
React.createElement(LogoDropdown, { logoSrc: logoSrc, logoAlt: logoAlt, logoType: logoType, logoSize: logoSize, links: logoLinks, languages: languages, currentLanguage: currentLanguage, showLanguageSelector: showLanguageSelector, onLanguageChange: onLanguageChange, t: t })),
|
|
529
720
|
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 })),
|
|
721
|
+
(resolvedShowUsageInfo || resolvedShowPaymentBadge) && (React.createElement(UsageStatusPopover, { paymentStatus: paymentStatus, usageCount: usageCount, maxDailyUsage: maxDailyUsage, showUsage: resolvedShowUsageInfo, showPlan: resolvedShowPaymentBadge, isLoading: isLoading, onLicensePageClick: resolvedLicensePageClick, t: t })),
|
|
538
722
|
showDonation && (React.createElement(DonationBadge, { donationUrl: donationUrl, text: donationText, iconType: "heart", size: "s", iconOnly: true, showPrompt: false, t: t })))),
|
|
539
723
|
children));
|
|
540
724
|
};
|
|
@@ -658,6 +842,47 @@ const InteractionFeedback = ({ status = 'default', message, statusMessage, leftC
|
|
|
658
842
|
};
|
|
659
843
|
InteractionFeedback.displayName = 'InteractionFeedback';
|
|
660
844
|
|
|
845
|
+
let useTranslation = null;
|
|
846
|
+
try {
|
|
847
|
+
const reactI18next = require('react-i18next');
|
|
848
|
+
useTranslation = reactI18next.useTranslation;
|
|
849
|
+
}
|
|
850
|
+
catch (e) {
|
|
851
|
+
}
|
|
852
|
+
const DEFAULT_LANGUAGES = [
|
|
853
|
+
{ code: 'ko', label: 'KO' },
|
|
854
|
+
{ code: 'en', label: 'EN' },
|
|
855
|
+
];
|
|
856
|
+
const LanguageSelector = ({ currentLanguage, languages = DEFAULT_LANGUAGES, onLanguageChange, size = 's', className, t }) => {
|
|
857
|
+
const i18n = useTranslation ? useTranslation().i18n : null;
|
|
858
|
+
const activeLanguage = currentLanguage || (i18n?.language) || 'ko';
|
|
859
|
+
const handleLanguageChange = (languageCode) => {
|
|
860
|
+
if (i18n) {
|
|
861
|
+
i18n.changeLanguage(languageCode);
|
|
862
|
+
}
|
|
863
|
+
if (onLanguageChange) {
|
|
864
|
+
onLanguageChange(languageCode);
|
|
865
|
+
}
|
|
866
|
+
};
|
|
867
|
+
const segmentOptions = languages.map((language) => ({
|
|
868
|
+
value: language.code,
|
|
869
|
+
label: resolveText(t, language.label),
|
|
870
|
+
disabled: false,
|
|
871
|
+
}));
|
|
872
|
+
return (React.createElement("div", { className: `designbase-figma-language-selector ${className || ''}` },
|
|
873
|
+
React.createElement(react.SegmentControl, { options: segmentOptions, value: activeLanguage, onChange: (event) => {
|
|
874
|
+
const detail = event.detail;
|
|
875
|
+
const value = typeof detail === 'object' && detail !== null && 'value' in detail &&
|
|
876
|
+
typeof detail.value === 'string'
|
|
877
|
+
? detail.value
|
|
878
|
+
: undefined;
|
|
879
|
+
if (value) {
|
|
880
|
+
handleLanguageChange(value);
|
|
881
|
+
}
|
|
882
|
+
}, size: size, className: "designbase-figma-language-selector__segment" })));
|
|
883
|
+
};
|
|
884
|
+
LanguageSelector.displayName = 'LanguageSelector';
|
|
885
|
+
|
|
661
886
|
function postPluginMessage(message) {
|
|
662
887
|
if (typeof parent === 'undefined') {
|
|
663
888
|
return;
|
|
@@ -673,17 +898,27 @@ function openExternalUrl(url) {
|
|
|
673
898
|
}
|
|
674
899
|
}
|
|
675
900
|
|
|
676
|
-
const LicenseOffer = ({ membershipUrl, t, className }) =>
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
React.createElement("
|
|
686
|
-
React.createElement(
|
|
901
|
+
const LicenseOffer = ({ joinUrl, guideUrl, membershipUrl, t, className, }) => {
|
|
902
|
+
const titleId = `designbase-license-offer-title-${React.useId().replace(/:/g, '')}`;
|
|
903
|
+
const resolvedJoinUrl = joinUrl || membershipUrl;
|
|
904
|
+
const steps = [
|
|
905
|
+
resolveText(t, { key: 'license.youtube_membership_step_1' }, 'Designbase YouTube 채널 멤버십에 가입합니다.'),
|
|
906
|
+
resolveText(t, { key: 'license.youtube_membership_step_2' }, '멤버 전용 커뮤니티에서 이번 달 키를 복사합니다.'),
|
|
907
|
+
resolveText(t, { key: 'license.youtube_membership_step_3' }, '아래 입력창에 키를 붙여 넣고 활성화합니다.'),
|
|
908
|
+
];
|
|
909
|
+
return (React.createElement("section", { className: `designbase-figma-license-offer ${className || ''}`, "aria-labelledby": titleId },
|
|
910
|
+
React.createElement("h3", { id: titleId, className: "designbase-figma-license-offer__title" }, resolveText(t, { key: 'license.youtube_membership_howto' }, '이용 방법')),
|
|
911
|
+
React.createElement("ol", { className: "designbase-figma-license-offer__steps" }, steps.map((step, index) => (React.createElement("li", { key: index },
|
|
912
|
+
React.createElement("span", { className: "designbase-figma-license-offer__step-index", "aria-hidden": "true" },
|
|
913
|
+
index + 1,
|
|
914
|
+
"."),
|
|
915
|
+
React.createElement("span", null, step))))),
|
|
916
|
+
(resolvedJoinUrl || guideUrl) && (React.createElement("div", { className: "designbase-figma-license-offer__actions" },
|
|
917
|
+
resolvedJoinUrl && (React.createElement(react.Button, { onPress: () => openExternalUrl(resolvedJoinUrl), variant: "primary", size: "m", fullWidth: true }, resolveText(t, { key: 'license.youtube_membership_join' }, '멤버십 가입'))),
|
|
918
|
+
guideUrl && (React.createElement(react.Button, { onPress: () => openExternalUrl(guideUrl), variant: "secondary", size: "m", fullWidth: true },
|
|
919
|
+
React.createElement("span", null, resolveText(t, { key: 'license.youtube_membership_guide' }, '안내보기')),
|
|
920
|
+
React.createElement(icons.ExternalLinkIcon, { size: 16, "aria-hidden": "true" })))))));
|
|
921
|
+
};
|
|
687
922
|
LicenseOffer.displayName = 'LicenseOffer';
|
|
688
923
|
|
|
689
924
|
function maskLicenseKey(value) {
|
|
@@ -699,16 +934,30 @@ function formatDate(value) {
|
|
|
699
934
|
return value;
|
|
700
935
|
return new Intl.DateTimeFormat(undefined, { dateStyle: 'medium' }).format(date);
|
|
701
936
|
}
|
|
702
|
-
function formatScope(value) {
|
|
937
|
+
function formatScope(value, t) {
|
|
938
|
+
if (!value)
|
|
939
|
+
return undefined;
|
|
940
|
+
if (value === 'all-products' || value === 'all_products') {
|
|
941
|
+
return resolveText(t, { key: 'license.all_plugins' }, '모든 플러그인');
|
|
942
|
+
}
|
|
943
|
+
return value;
|
|
944
|
+
}
|
|
945
|
+
function formatSource(value, t) {
|
|
703
946
|
if (!value)
|
|
704
947
|
return undefined;
|
|
705
|
-
if (value === '
|
|
706
|
-
return '
|
|
948
|
+
if (value === 'youtube_membership' || value === 'membership') {
|
|
949
|
+
return resolveText(t, { key: 'license.source_youtube_membership' }, 'YouTube 멤버십');
|
|
950
|
+
}
|
|
951
|
+
if (value === 'lemonsqueezy') {
|
|
952
|
+
return resolveText(t, { key: 'license.source_purchase' }, '구매 라이선스');
|
|
953
|
+
}
|
|
707
954
|
return value;
|
|
708
955
|
}
|
|
709
|
-
const LicenseStatusCard = ({ status, licenseKey = '', licenseStatus, licenseSource, accessScope, expiresAt, lastVerifiedAt, validationMessage, allowRevealKey =
|
|
956
|
+
const LicenseStatusCard = ({ status, licenseKey = '', licenseStatus, licenseSource, accessScope, expiresAt, lastVerifiedAt, validationMessage, allowRevealKey = false, t, className, }) => {
|
|
710
957
|
const [isKeyRevealed, setIsKeyRevealed] = React.useState(false);
|
|
711
|
-
const
|
|
958
|
+
const normalizedLicenseStatus = licenseStatus?.toLowerCase();
|
|
959
|
+
const isInactiveStatus = ['expired', 'revoked', 'invalid', 'inactive'].includes(normalizedLicenseStatus || '');
|
|
960
|
+
const isActive = status === 'PAID' && !isInactiveStatus;
|
|
712
961
|
const statusText = isActive
|
|
713
962
|
? resolveText(t, { key: 'license.status_active' }, 'Active')
|
|
714
963
|
: resolveText(t, { key: 'license.status_inactive' }, 'License check failed');
|
|
@@ -716,30 +965,82 @@ const LicenseStatusCard = ({ status, licenseKey = '', licenseStatus, licenseSour
|
|
|
716
965
|
return (React.createElement("section", { className: `designbase-figma-license-status-card designbase-figma-license-status-card--${statusClass} ${className || ''}`, "aria-label": statusText },
|
|
717
966
|
React.createElement("div", { className: "designbase-figma-license-status-card__header" },
|
|
718
967
|
React.createElement("div", null,
|
|
719
|
-
React.createElement("div", { className: "designbase-figma-license-status-card__eyebrow" }, isActive
|
|
968
|
+
React.createElement("div", { className: "designbase-figma-license-status-card__eyebrow" }, isActive
|
|
969
|
+
? resolveText(t, { key: 'license.activated' }, '라이선스 활성화됨')
|
|
970
|
+
: resolveText(t, { key: 'license.status_label' }, '라이선스 상태')),
|
|
720
971
|
React.createElement("h3", null, statusText)),
|
|
721
972
|
React.createElement("span", { className: "designbase-figma-license-status-card__badge" }, licenseStatus || (isActive ? 'active' : 'inactive'))),
|
|
722
973
|
validationMessage && !isActive && (React.createElement("p", { className: "designbase-figma-license-status-card__message" }, validationMessage)),
|
|
723
974
|
licenseKey && (React.createElement("div", { className: "designbase-figma-license-status-card__key-row" },
|
|
724
975
|
React.createElement("span", null, resolveText(t, { key: 'license.key_label' }, '라이선스 키')),
|
|
725
976
|
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
|
|
977
|
+
allowRevealKey && (React.createElement(react.Button, { type: "button", onPress: () => setIsKeyRevealed((value) => !value), variant: "tertiary", size: "s" }, isKeyRevealed
|
|
978
|
+
? resolveText(t, { key: 'license.hide_key' }, '숨기기')
|
|
979
|
+
: resolveText(t, { key: 'license.reveal_key' }, '보기'))))),
|
|
727
980
|
(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))),
|
|
981
|
+
formatSource(licenseSource, t) && React.createElement(React.Fragment, null,
|
|
982
|
+
React.createElement("dt", null, resolveText(t, { key: 'license.source' }, '출처')),
|
|
983
|
+
React.createElement("dd", null, formatSource(licenseSource, t))),
|
|
984
|
+
formatScope(accessScope, t) && React.createElement(React.Fragment, null,
|
|
985
|
+
React.createElement("dt", null, resolveText(t, { key: 'license.access' }, '접근 범위')),
|
|
986
|
+
React.createElement("dd", null, formatScope(accessScope, t))),
|
|
734
987
|
expiresAt && React.createElement(React.Fragment, null,
|
|
735
|
-
React.createElement("dt", null,
|
|
988
|
+
React.createElement("dt", null, resolveText(t, { key: 'license.expires' }, '만료일')),
|
|
736
989
|
React.createElement("dd", null, formatDate(expiresAt) || expiresAt)),
|
|
737
990
|
lastVerifiedAt && React.createElement(React.Fragment, null,
|
|
738
|
-
React.createElement("dt", null,
|
|
991
|
+
React.createElement("dt", null, resolveText(t, { key: 'license.last_checked' }, '최근 확인')),
|
|
739
992
|
React.createElement("dd", null, formatDate(lastVerifiedAt) || lastVerifiedAt))))));
|
|
740
993
|
};
|
|
741
994
|
LicenseStatusCard.displayName = 'LicenseStatusCard';
|
|
742
995
|
|
|
996
|
+
function resolvePaymentFeedbackVariant(errorCode, explicitVariant) {
|
|
997
|
+
if (explicitVariant)
|
|
998
|
+
return explicitVariant;
|
|
999
|
+
if (!errorCode)
|
|
1000
|
+
return 'success';
|
|
1001
|
+
const normalized = errorCode.toLowerCase();
|
|
1002
|
+
if (normalized.includes('network')
|
|
1003
|
+
|| normalized.includes('server')
|
|
1004
|
+
|| normalized.includes('rate')
|
|
1005
|
+
|| normalized.includes('timeout')) {
|
|
1006
|
+
return 'warning';
|
|
1007
|
+
}
|
|
1008
|
+
if (normalized.includes('info')) {
|
|
1009
|
+
return 'info';
|
|
1010
|
+
}
|
|
1011
|
+
return 'error';
|
|
1012
|
+
}
|
|
1013
|
+
const PaymentStatusSection = ({ activationLimit, activationUsage, licenseKey, onDeactivate, isDeactivating = false, showDetails = false, actionMessage, actionMessageVariant, errorCode, className, t, }) => {
|
|
1014
|
+
const remainingActivations = Math.max(0, activationLimit - activationUsage);
|
|
1015
|
+
const resolvedActionMessage = actionMessage ? resolveText(t, actionMessage) : undefined;
|
|
1016
|
+
const feedbackVariant = resolvedActionMessage
|
|
1017
|
+
? resolvePaymentFeedbackVariant(errorCode, actionMessageVariant)
|
|
1018
|
+
: undefined;
|
|
1019
|
+
return (React.createElement("div", { className: clsx('designbase-figma-payment-status', className) },
|
|
1020
|
+
React.createElement("div", { className: "designbase-figma-payment-status__license-key" },
|
|
1021
|
+
resolveText(t, { key: 'payment.license_key_label' }, '라이선스 키'),
|
|
1022
|
+
": ",
|
|
1023
|
+
React.createElement("span", null, licenseKey)),
|
|
1024
|
+
showDetails && (React.createElement("div", { className: "designbase-figma-payment-status__details" },
|
|
1025
|
+
React.createElement("div", { className: "designbase-figma-payment-status__activation-info" },
|
|
1026
|
+
React.createElement("div", { className: "designbase-figma-payment-status__remaining" },
|
|
1027
|
+
remainingActivations,
|
|
1028
|
+
' ',
|
|
1029
|
+
resolveText(t, { key: 'payment.activations_remaining' }, '활성'),
|
|
1030
|
+
' ',
|
|
1031
|
+
resolveText(t, { key: 'payment.spots_remaining' }, '자리 남음')),
|
|
1032
|
+
React.createElement("div", { className: "designbase-figma-payment-status__usage" },
|
|
1033
|
+
activationUsage,
|
|
1034
|
+
"/",
|
|
1035
|
+
activationLimit)),
|
|
1036
|
+
React.createElement("div", { className: "designbase-figma-payment-status__action" },
|
|
1037
|
+
React.createElement(react.Button, { onPress: onDeactivate, variant: "secondary", size: "s", fullWidth: true, disabled: isDeactivating }, isDeactivating
|
|
1038
|
+
? resolveText(t, { key: 'payment.deactivating' }, '비활성화중...')
|
|
1039
|
+
: resolveText(t, { key: 'payment.deactivate_license' }, '라이선스 비활성화')),
|
|
1040
|
+
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)))))));
|
|
1041
|
+
};
|
|
1042
|
+
PaymentStatusSection.displayName = 'PaymentStatusSection';
|
|
1043
|
+
|
|
743
1044
|
const defaultFeatures = [
|
|
744
1045
|
{ name: '사용 제한', free: '일일 제한', pro: '무제한' },
|
|
745
1046
|
{ name: '모든 주제 사용', free: false, pro: true },
|
|
@@ -934,15 +1235,27 @@ function useFigmaLicenseBridge(options = {}) {
|
|
|
934
1235
|
if (type === messageTypes.verifyResult) {
|
|
935
1236
|
setIsVerifying(false);
|
|
936
1237
|
setIsStartingAuth(false);
|
|
937
|
-
applyPatch(
|
|
1238
|
+
applyPatch({
|
|
1239
|
+
...parseLicenseMessage(payload),
|
|
1240
|
+
deactivateMessage: '',
|
|
1241
|
+
deactivateErrorCode: '',
|
|
1242
|
+
});
|
|
938
1243
|
if (payload.success === true) {
|
|
939
1244
|
applyPatch({ paymentStatus: 'PAID', isLicenseRegistered: true });
|
|
940
1245
|
}
|
|
941
1246
|
}
|
|
942
1247
|
if (type === messageTypes.deactivateResult) {
|
|
943
1248
|
setIsDeactivating(false);
|
|
944
|
-
|
|
945
|
-
|
|
1249
|
+
const parsed = parseLicenseMessage(payload);
|
|
1250
|
+
const isSuccess = payload.success === true;
|
|
1251
|
+
applyPatch({
|
|
1252
|
+
...parsed,
|
|
1253
|
+
deactivateMessage: parsed.lastMessage,
|
|
1254
|
+
deactivateErrorCode: isSuccess ? undefined : parsed.lastErrorCode,
|
|
1255
|
+
lastMessage: isSuccess ? undefined : parsed.lastMessage,
|
|
1256
|
+
lastErrorCode: isSuccess ? undefined : parsed.lastErrorCode,
|
|
1257
|
+
});
|
|
1258
|
+
if (isSuccess) {
|
|
946
1259
|
applyPatch({
|
|
947
1260
|
paymentStatus: 'UNPAID',
|
|
948
1261
|
licenseKey: '',
|
|
@@ -950,6 +1263,8 @@ function useFigmaLicenseBridge(options = {}) {
|
|
|
950
1263
|
isLicenseRegistered: false,
|
|
951
1264
|
activationLimit: 0,
|
|
952
1265
|
activationUsage: 0,
|
|
1266
|
+
deactivateMessage: parsed.lastMessage || '라이선스가 비활성화되었습니다.',
|
|
1267
|
+
deactivateErrorCode: undefined,
|
|
953
1268
|
});
|
|
954
1269
|
}
|
|
955
1270
|
}
|
|
@@ -976,8 +1291,12 @@ function useFigmaLicenseBridge(options = {}) {
|
|
|
976
1291
|
}, [messageTypes.verifyLicense]);
|
|
977
1292
|
const deactivateLicense = React.useCallback(() => {
|
|
978
1293
|
setIsDeactivating(true);
|
|
1294
|
+
applyPatch({
|
|
1295
|
+
deactivateMessage: '',
|
|
1296
|
+
deactivateErrorCode: '',
|
|
1297
|
+
});
|
|
979
1298
|
postPluginMessage({ type: messageTypes.deactivateLicense });
|
|
980
|
-
}, [messageTypes.deactivateLicense]);
|
|
1299
|
+
}, [applyPatch, messageTypes.deactivateLicense]);
|
|
981
1300
|
const startDesignbaseAuth = React.useCallback(() => {
|
|
982
1301
|
setIsStartingAuth(true);
|
|
983
1302
|
postPluginMessage({ type: START_DESIGNBASE_AUTH_MESSAGE });
|
|
@@ -1047,7 +1366,7 @@ const PageLicense = ({ status: initialStatus, onClose, licenseKey: initialLicens
|
|
|
1047
1366
|
setLicenseKeyDraft(value);
|
|
1048
1367
|
setLicenseKeyInput(value);
|
|
1049
1368
|
};
|
|
1050
|
-
return (React.createElement("div", { className: `designbase-figma-page-license ${className || ''}
|
|
1369
|
+
return (React.createElement("div", { className: `designbase-figma-page-license designbase-figma-page-license--${mode} ${className || ''}`, "data-license-mode": mode },
|
|
1051
1370
|
React.createElement("div", { className: "designbase-figma-page-license__content" },
|
|
1052
1371
|
React.createElement("div", { className: "designbase-figma-page-license__header" },
|
|
1053
1372
|
React.createElement("div", { className: "designbase-figma-page-license__title" },
|
|
@@ -1062,15 +1381,15 @@ const PageLicense = ({ status: initialStatus, onClose, licenseKey: initialLicens
|
|
|
1062
1381
|
: isMembershipMode
|
|
1063
1382
|
? resolveText(t, { key: 'license.youtube_membership_description' }, 'YouTube 멤버십에 가입하면 Designbase 플러그인을 무제한으로 사용할 수 있습니다.')
|
|
1064
1383
|
: resolveText(t, { key: 'license.purchase_for_unlimited' }, '디자인베이스 멤버십 또는 라이선스로 무제한 이용이 가능합니다.')),
|
|
1065
|
-
!isPaid && (React.createElement("p", { className: "designbase-figma-page-license__usage" }, usageLabel))),
|
|
1384
|
+
!isPaid && !isMembershipMode && (React.createElement("p", { className: "designbase-figma-page-license__usage" }, usageLabel))),
|
|
1066
1385
|
!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' : ''}
|
|
1386
|
+
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
1387
|
isPaid && (React.createElement("div", { className: "designbase-figma-page-license__section" },
|
|
1069
1388
|
React.createElement("div", { className: "designbase-figma-page-license__section-header" },
|
|
1070
1389
|
React.createElement("h3", null, resolveText(t, { key: 'license.activated' }, '라이선스 활성화됨')),
|
|
1071
1390
|
React.createElement(react.Button, { onPress: () => setShowDetails(!showDetails), variant: "tertiary", size: "s", iconOnly: true },
|
|
1072
1391
|
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 }),
|
|
1392
|
+
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
1393
|
React.createElement("p", { className: "designbase-figma-page-license__usage" }, usageLabel),
|
|
1075
1394
|
showDetails && (React.createElement("div", { className: "designbase-figma-page-license__details" },
|
|
1076
1395
|
React.createElement("div", { className: "designbase-figma-page-license__activation-info" },
|
|
@@ -1084,11 +1403,13 @@ const PageLicense = ({ status: initialStatus, onClose, licenseKey: initialLicens
|
|
|
1084
1403
|
state.activationUsage,
|
|
1085
1404
|
"/",
|
|
1086
1405
|
state.activationLimit)),
|
|
1087
|
-
React.createElement(
|
|
1088
|
-
|
|
1089
|
-
|
|
1406
|
+
React.createElement("div", { className: "designbase-figma-page-license__action" },
|
|
1407
|
+
React.createElement(react.Button, { onPress: deactivateLicense, variant: "secondary", size: "s", fullWidth: true, disabled: isDeactivating }, isDeactivating
|
|
1408
|
+
? resolveText(t, { key: 'license.deactivating' }, '비활성화 중...')
|
|
1409
|
+
: resolveText(t, { key: 'license.deactivate' }, '라이선스 비활성화')),
|
|
1410
|
+
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
1411
|
!isPaid && (React.createElement(React.Fragment, null,
|
|
1091
|
-
isMembershipMode && (React.createElement(LicenseOffer, {
|
|
1412
|
+
isMembershipMode && (React.createElement(LicenseOffer, { joinUrl: links.membershipUrl || links.purchaseUrl, guideUrl: links.helpUrl || links.purchaseUrl, t: t })),
|
|
1092
1413
|
!isMembershipMode && (enableAccountAuth || links.purchaseUrl || links.accountUrl) && (React.createElement("div", { className: "designbase-figma-page-license__section" },
|
|
1093
1414
|
React.createElement("div", { className: "designbase-figma-page-license__section-header" },
|
|
1094
1415
|
React.createElement("h3", null, resolveText(t, { key: 'license.designbase_account' }, '디자인베이스 계정'))),
|
|
@@ -1103,41 +1424,33 @@ const PageLicense = ({ status: initialStatus, onClose, licenseKey: initialLicens
|
|
|
1103
1424
|
links.accountUrl && (React.createElement(react.Button, { onPress: () => openExternalUrl(links.accountUrl), variant: "tertiary", size: "s", fullWidth: true },
|
|
1104
1425
|
React.createElement("span", null, resolveText(t, { key: 'license.manage_account' }, '계정·멤버십 관리')),
|
|
1105
1426
|
React.createElement(icons.ExternalLinkIcon, { size: 16 })))))),
|
|
1106
|
-
showLicenseKeyInput && (React.createElement("div", { className:
|
|
1427
|
+
showLicenseKeyInput && (React.createElement("div", { className: isMembershipMode
|
|
1428
|
+
? 'designbase-figma-page-license__key-form'
|
|
1429
|
+
: 'designbase-figma-page-license__section' },
|
|
1107
1430
|
React.createElement("div", { className: "designbase-figma-page-license__section-header" },
|
|
1108
1431
|
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" },
|
|
1432
|
+
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 }))))),
|
|
1433
|
+
links.helpUrl && !isMembershipMode && (React.createElement("p", { className: "designbase-figma-page-license__disclaimer" },
|
|
1111
1434
|
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:
|
|
1435
|
+
React.createElement(react.Button, { className: "designbase-figma-page-license__close", onPress: onClose, variant: "tertiary", size: "s", iconOnly: true, "aria-label": "Close" },
|
|
1436
|
+
React.createElement(icons.CloseIcon, { size: 16 }))));
|
|
1114
1437
|
};
|
|
1115
1438
|
PageLicense.displayName = 'PageLicense';
|
|
1116
1439
|
|
|
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' }, '라이선스 비활성화'))))));
|
|
1440
|
+
const PaymentBadge = ({ isActive, onClick, isLoading = false, text, t = (key) => key, className, }) => {
|
|
1441
|
+
const getBadgeText = () => {
|
|
1442
|
+
if (text)
|
|
1443
|
+
return text;
|
|
1444
|
+
return isActive
|
|
1445
|
+
? (t('proUser') || '프로 계정')
|
|
1446
|
+
: (t('usingFree') || '무료 사용');
|
|
1447
|
+
};
|
|
1448
|
+
const getBadgeVariant = () => {
|
|
1449
|
+
return isActive ? 'success' : 'secondary';
|
|
1450
|
+
};
|
|
1451
|
+
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
1452
|
};
|
|
1140
|
-
|
|
1453
|
+
PaymentBadge.displayName = 'PaymentBadge';
|
|
1141
1454
|
|
|
1142
1455
|
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
1456
|
const [isCompleted, setIsCompleted] = React.useState(false);
|
|
@@ -1181,7 +1494,7 @@ const ProgressModal = ({ isOpen, onClose, progress, title = { key: 'progress.wor
|
|
|
1181
1494
|
React.createElement(react.Spinner, { size: "m" }))))),
|
|
1182
1495
|
React.createElement("p", { className: "designbase-figma-progress-modal__message" }, displayMessage),
|
|
1183
1496
|
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',
|
|
1497
|
+
React.createElement(react.Progressbar, { value: progressPercentage, size: "m", variant: isCompleted ? 'success' : 'primary', barStyle: "animated", fullWidth: true }),
|
|
1185
1498
|
displayHelpText && !isCompleted && (React.createElement("p", { className: "designbase-figma-progress-modal__help-text" }, displayHelpText)),
|
|
1186
1499
|
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
1500
|
};
|
|
@@ -1933,6 +2246,7 @@ exports.FigmaToastProvider = FigmaToastProvider;
|
|
|
1933
2246
|
exports.FormWithSubmit = FormWithSubmit;
|
|
1934
2247
|
exports.InteractionFeedback = InteractionFeedback;
|
|
1935
2248
|
exports.LanguageSelector = LanguageSelector;
|
|
2249
|
+
exports.LanguageSettingsModal = LanguageSettingsModal;
|
|
1936
2250
|
exports.LicenseOffer = LicenseOffer;
|
|
1937
2251
|
exports.LicenseStatusCard = LicenseStatusCard;
|
|
1938
2252
|
exports.LogoDropdown = LogoDropdown;
|
|
@@ -1946,7 +2260,10 @@ exports.START_DESIGNBASE_AUTH_MESSAGE = START_DESIGNBASE_AUTH_MESSAGE;
|
|
|
1946
2260
|
exports.SettingsModal = SettingsModal;
|
|
1947
2261
|
exports.TitleDescription = TitleDescription;
|
|
1948
2262
|
exports.UpgradeBanner = UpgradeBanner;
|
|
2263
|
+
exports.UsageStatusPopover = UsageStatusPopover;
|
|
1949
2264
|
exports.getActiveSection = getActiveSection;
|
|
2265
|
+
exports.interpolateText = interpolateText;
|
|
2266
|
+
exports.resolvePaymentFeedbackVariant = resolvePaymentFeedbackVariant;
|
|
1950
2267
|
exports.resolveText = resolveText;
|
|
1951
2268
|
exports.scrollToSection = scrollToSection;
|
|
1952
2269
|
exports.useFigmaLicenseBridge = useFigmaLicenseBridge;
|