@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.esm.js
CHANGED
|
@@ -1,11 +1,19 @@
|
|
|
1
|
-
import React, { useRef, useState, useLayoutEffect, useEffect, createContext, useContext,
|
|
2
|
-
import { Button, Dropdown, Badge, Toggle,
|
|
1
|
+
import React, { useRef, useState, useLayoutEffect, useEffect, useCallback, useId, createContext, useContext, useMemo } from 'react';
|
|
2
|
+
import { Button, Dropdown, Badge, Toggle, Modal, ModalBody, Logo, Toast, Input, Spinner, SegmentControl, Progressbar, RandomGradient } from '@designbasekorea/ui-wc/react';
|
|
3
3
|
export * from '@designbasekorea/ui-wc/react';
|
|
4
4
|
export { Accordion, ActivityHeatmap, AdBanner, AiChat, AiChatList, Alert, AnimationBackground, AnimationText, AppBadge, AudioPlayer, Avatar, Backdrop, Badge, Banner, BottomNavigation, BottomSheet, Breadcrumbs, Button, Calendar, Card, Carousel, ChatComposer, ChatList, ChatMessage, ChatOrb, ChatRoom, ChatSuggestions, ChatTyping, ChatWelcome, ChatWidget, Checkbox, Chip, CodeBlock, ColorPicker, ColorSwatch, Confirm, Container, ContextMenu, CookieBanner, Countdown, CursorFollower, DatePicker, Divider, DownloadProgress, Drawer, Dropdown, Dropzone, EmailVerify, EmptyState, FileUploader, FloatingActionButton, Footer, Form, Gantt, Gradient, Grid, GridBackground, GridItem, Image, ImageList, ImagePlaceholder, Indicator, Input, Kanban, Label, Lightbox, List, Logo, MarkdownEditor, Marquee, Masonry, MenuItem, Modal, ModalBody, ModalFooter, ModalHeader, Navbar, OnboardingModal, OtpInput, PageHeader, Pagination, PhoneVerify, Popover, Progress, ProgressStep, Progressbar, Radio, RandomGradient, RangeSlider, Rating, Reorder, ResizablePanels, ScrollArea, SearchBar, Section, SectionAppDownload, SectionContact, SectionCta, SectionFaq, SectionFeature, SectionFeatureGrid, SectionHero, SectionLogoCloud, SectionPricing, SectionStats, SectionTestimonials, SegmentControl, Select, Share, Sidebar, Signature, Skeleton, Spinner, SplitView, Stack, Stat, Stepper, Table, Tabs, Testimonial, Textarea, TimePicker, Timeline, Toast, Toggle, Toolbar, Tooltip, TopBanner, Tutorial, TwoFactor, VideoPlayer, YouTubePlayer, useContextMenu } from '@designbasekorea/ui-wc/react';
|
|
5
|
-
import { ChevronDownIcon, AppWindowIcon, YoutubeIcon, InstagramIcon, FigmaIcon, MailIcon, ExternalLinkIcon, MoreHorizontalIcon, CloseIcon, CircleCheckFilledIcon, ResizableIcon, GripVerticalIcon, ChevronRightIcon } from '@designbasekorea/icons';
|
|
5
|
+
import { ChevronDownIcon, AppWindowIcon, YoutubeIcon, InstagramIcon, FigmaIcon, MailIcon, ExternalLinkIcon, TranslateIcon, MoreHorizontalIcon, CloseIcon, CircleCheckFilledIcon, ResizableIcon, GripVerticalIcon, ChevronRightIcon } from '@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(Spinner, { size: "s" })) : (React.createElement(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] = useState(resolvedCurrentLanguage);
|
|
424
|
+
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 = 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(Modal, { isOpen: isOpen, onClose: onClose, title: title || translate('languageSettings', '언어 설정'), size: "s", className: clsx('designbase-figma-language-settings-modal', className) },
|
|
442
|
+
React.createElement(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(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] = useState(false);
|
|
480
|
-
const
|
|
505
|
+
const [isLanguageModalOpen, setIsLanguageModalOpen] = useState(false);
|
|
506
|
+
const [internalLanguage, setInternalLanguage] = useState();
|
|
507
|
+
const i18n = useTranslation$1 ? useTranslation$1().i18n : null;
|
|
508
|
+
const rootRef = useRef(null);
|
|
509
|
+
const triggerRef = useRef(null);
|
|
510
|
+
const menuId = `designbase-figma-logo-menu-${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
|
+
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(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(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(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(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] = useState(false);
|
|
588
|
+
const rootRef = useRef(null);
|
|
589
|
+
const triggerRef = useRef(null);
|
|
590
|
+
const popoverId = `designbase-figma-usage-popover-${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
|
+
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(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(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-${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(Button, { onPress: () => openExternalUrl(resolvedJoinUrl), variant: "primary", size: "m", fullWidth: true }, resolveText(t, { key: 'license.youtube_membership_join' }, '멤버십 가입'))),
|
|
918
|
+
guideUrl && (React.createElement(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(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] = 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(Button, { type: "button", onPress: () => setIsKeyRevealed((value) => !value), variant: "tertiary", size: "s" }, isKeyRevealed
|
|
977
|
+
allowRevealKey && (React.createElement(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(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 = 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 = 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(Button, { onPress: () => setShowDetails(!showDetails), variant: "tertiary", size: "s", iconOnly: true },
|
|
1072
1391
|
React.createElement(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(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(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(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(Button, { className: "designbase-figma-page-license__close", onPress: onClose, variant: "tertiary", size: "s", "aria-label": "Close" },
|
|
1113
|
-
React.createElement(CloseIcon, { size:
|
|
1435
|
+
React.createElement(Button, { className: "designbase-figma-page-license__close", onPress: onClose, variant: "tertiary", size: "s", iconOnly: true, "aria-label": "Close" },
|
|
1436
|
+
React.createElement(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(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(Spinner, { size: "s" })) : (React.createElement(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] = useState(false);
|
|
@@ -1181,7 +1494,7 @@ const ProgressModal = ({ isOpen, onClose, progress, title = { key: 'progress.wor
|
|
|
1181
1494
|
React.createElement(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(Progressbar, { value: progressPercentage, size: "m", variant: isCompleted ? 'success' : 'primary',
|
|
1497
|
+
React.createElement(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(Button, { onPress: handleConfirm, variant: "primary", size: "m", fullWidth: true }, displayConfirmText)) : (React.createElement(Button, { onPress: handleStop, variant: "secondary", size: "m", fullWidth: true }, displayStopText))))));
|
|
1187
1500
|
};
|
|
@@ -1388,5 +1701,5 @@ const UpgradeBanner = ({ onClick, variant = 'button', isLoading = false, title =
|
|
|
1388
1701
|
};
|
|
1389
1702
|
UpgradeBanner.displayName = 'UpgradeBanner';
|
|
1390
1703
|
|
|
1391
|
-
export { DEFAULT_LICENSE_MESSAGE_TYPES, DonationBadge, FigmaContainer, FigmaFooter, FigmaHeader, FigmaPageHead, FigmaSection, FigmaSidebar, FigmaToast, FigmaToastProvider, FormWithSubmit, InteractionFeedback, LanguageSelector, LicenseOffer, LicenseStatusCard, LogoDropdown, PageLicense, PaymentBadge, PaymentStatusSection, PricingComparison, ProgressModal, ResizablePlugin, START_DESIGNBASE_AUTH_MESSAGE, SettingsModal, TitleDescription, UpgradeBanner, getActiveSection, resolveText, scrollToSection, useFigmaLicenseBridge, useFigmaToast, useToast };
|
|
1704
|
+
export { DEFAULT_LICENSE_MESSAGE_TYPES, DonationBadge, FigmaContainer, FigmaFooter, FigmaHeader, FigmaPageHead, FigmaSection, FigmaSidebar, FigmaToast, FigmaToastProvider, FormWithSubmit, InteractionFeedback, LanguageSelector, LanguageSettingsModal, LicenseOffer, LicenseStatusCard, LogoDropdown, PageLicense, PaymentBadge, PaymentStatusSection, PricingComparison, ProgressModal, ResizablePlugin, START_DESIGNBASE_AUTH_MESSAGE, SettingsModal, TitleDescription, UpgradeBanner, UsageStatusPopover, getActiveSection, interpolateText, resolvePaymentFeedbackVariant, resolveText, scrollToSection, useFigmaLicenseBridge, useFigmaToast, useToast };
|
|
1392
1705
|
//# sourceMappingURL=index.esm.js.map
|