@designbasekorea/figma-ui 0.6.10 → 0.7.1
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 +26 -1
- package/dist/index.esm.css +1 -1
- package/dist/index.esm.css.map +1 -1
- package/dist/index.esm.js +299 -66
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +298 -65
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.esm.js
CHANGED
|
@@ -2,7 +2,7 @@ import React, { createContext, useMemo, useContext, useRef, useState, useLayoutE
|
|
|
2
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, TranslateIcon,
|
|
5
|
+
import { ChevronDownIcon, AppWindowIcon, YoutubeIcon, InstagramIcon, FigmaIcon, MailIcon, ExternalLinkIcon, TranslateIcon, CloseIcon, CircleCheckFilledIcon, ResizableIcon, GripVerticalIcon, ChevronRightIcon } from '@designbasekorea/icons';
|
|
6
6
|
import { DEFAULT_LICENSE_MESSAGE_TYPES } from '@designbasekorea/figma-license-core';
|
|
7
7
|
export { DEFAULT_LICENSE_MESSAGE_TYPES } from '@designbasekorea/figma-license-core';
|
|
8
8
|
|
|
@@ -38,6 +38,7 @@ const FIGMA_UI_TRANSLATIONS = {
|
|
|
38
38
|
remainingShort: '남음',
|
|
39
39
|
usageCountCaption: '오늘 사용',
|
|
40
40
|
remainingCopy: '{{count}}회 남음 · 매일 리셋',
|
|
41
|
+
lowUsageWarning: '무료 사용량이 {{count}}회({{percent}}%) 남았습니다. 멤버십으로 무제한 이용해보세요.',
|
|
41
42
|
usageUnavailable: '사용량 제한이 없습니다',
|
|
42
43
|
unlimitedUsageTooltip: '모든 기능을 제한 없이 사용할 수 있습니다',
|
|
43
44
|
betaUsageDescription: '베타 기간에는 사용량 제한이 적용되지 않습니다',
|
|
@@ -52,12 +53,16 @@ const FIGMA_UI_TRANSLATIONS = {
|
|
|
52
53
|
'form.submit': '제출',
|
|
53
54
|
'form.verifying': '확인 중...',
|
|
54
55
|
'license.youtube_membership': 'YouTube 멤버십',
|
|
56
|
+
'license.current_plan': '현재 플랜',
|
|
57
|
+
'license.pro_plan': '프로 플랜',
|
|
58
|
+
'license.membership': '멤버십',
|
|
55
59
|
'license.pro_account': '프로 계정',
|
|
56
60
|
'license.upgrade_to_pro': '프로로 업그레이드',
|
|
57
61
|
'license.all_features_available': '모든 기능을 사용할 수 있습니다.',
|
|
58
62
|
'license.youtube_membership_description': 'YouTube 멤버십에 가입하면 Designbase 플러그인을 무제한으로 사용할 수 있습니다.',
|
|
59
63
|
'license.purchase_for_unlimited': '디자인베이스 멤버십 또는 라이선스로 무제한 이용이 가능합니다.',
|
|
60
64
|
'license.usage_unlimited': '사용 횟수 제한 없음',
|
|
65
|
+
'license.active_until_expiry': '만료일까지 모든 기능을 제한 없이 사용할 수 있습니다.',
|
|
61
66
|
'license.usage_daily': '오늘 남은 횟수 {{remaining}}/{{limit}}',
|
|
62
67
|
'license.usage_weekly': '이번 주 남은 횟수 {{remaining}}/{{limit}}',
|
|
63
68
|
'license.usage_monthly': '이번 달 남은 횟수 {{remaining}}/{{limit}}',
|
|
@@ -67,6 +72,7 @@ const FIGMA_UI_TRANSLATIONS = {
|
|
|
67
72
|
'license.spots_remaining': '자리 남음',
|
|
68
73
|
'license.deactivating': '비활성화 중...',
|
|
69
74
|
'license.deactivate': '라이선스 비활성화',
|
|
75
|
+
'license.deactivate_hint': '이 기기에서 라이선스 연결을 해제합니다.',
|
|
70
76
|
'license.designbase_account': '디자인베이스 계정',
|
|
71
77
|
'license.designbase_account_hint': 'WordPress 계정으로 로그인하거나 멤버십을 구매하면 플러그인이 자동으로 활성화됩니다.',
|
|
72
78
|
'license.authenticating': '인증 중...',
|
|
@@ -170,6 +176,7 @@ const FIGMA_UI_TRANSLATIONS = {
|
|
|
170
176
|
remainingShort: 'left',
|
|
171
177
|
usageCountCaption: 'Used today',
|
|
172
178
|
remainingCopy: '{{count}} remaining · resets daily',
|
|
179
|
+
lowUsageWarning: 'Only {{count}} free uses ({{percent}}%) remain. Upgrade for unlimited access.',
|
|
173
180
|
usageUnavailable: 'No usage limit',
|
|
174
181
|
unlimitedUsageTooltip: 'Use every feature without limits',
|
|
175
182
|
betaUsageDescription: 'Usage limits are not applied during the beta period',
|
|
@@ -184,12 +191,16 @@ const FIGMA_UI_TRANSLATIONS = {
|
|
|
184
191
|
'form.submit': 'Activate',
|
|
185
192
|
'form.verifying': 'Verifying...',
|
|
186
193
|
'license.youtube_membership': 'YouTube Membership',
|
|
194
|
+
'license.current_plan': 'Current plan',
|
|
195
|
+
'license.pro_plan': 'Pro plan',
|
|
196
|
+
'license.membership': 'Membership',
|
|
187
197
|
'license.pro_account': 'Pro account',
|
|
188
198
|
'license.upgrade_to_pro': 'Upgrade to Pro',
|
|
189
199
|
'license.all_features_available': 'All features are available.',
|
|
190
200
|
'license.youtube_membership_description': 'Join YouTube Membership to use Designbase plugins without limits.',
|
|
191
201
|
'license.purchase_for_unlimited': 'Get unlimited access with a Designbase membership or license.',
|
|
192
202
|
'license.usage_unlimited': 'Unlimited usage',
|
|
203
|
+
'license.active_until_expiry': 'All features remain available without limits until expiration.',
|
|
193
204
|
'license.usage_daily': '{{remaining}}/{{limit}} remaining today',
|
|
194
205
|
'license.usage_weekly': '{{remaining}}/{{limit}} remaining this week',
|
|
195
206
|
'license.usage_monthly': '{{remaining}}/{{limit}} remaining this month',
|
|
@@ -199,6 +210,7 @@ const FIGMA_UI_TRANSLATIONS = {
|
|
|
199
210
|
'license.spots_remaining': 'spots remaining',
|
|
200
211
|
'license.deactivating': 'Deactivating...',
|
|
201
212
|
'license.deactivate': 'Deactivate license',
|
|
213
|
+
'license.deactivate_hint': 'Disconnects this license from the current device.',
|
|
202
214
|
'license.designbase_account': 'Designbase account',
|
|
203
215
|
'license.designbase_account_hint': 'Sign in with your WordPress account or purchase a membership to activate the plugin automatically.',
|
|
204
216
|
'license.authenticating': 'Authenticating...',
|
|
@@ -302,6 +314,7 @@ const FIGMA_UI_TRANSLATIONS = {
|
|
|
302
314
|
remainingShort: '残り',
|
|
303
315
|
usageCountCaption: '今日の使用',
|
|
304
316
|
remainingCopy: '{{count}}回残り · 毎日リセット',
|
|
317
|
+
lowUsageWarning: '無料使用回数は残り{{count}}回({{percent}}%)です。無制限利用にアップグレードできます。',
|
|
305
318
|
usageUnavailable: '使用制限はありません',
|
|
306
319
|
unlimitedUsageTooltip: 'すべての機能を無制限で使用できます',
|
|
307
320
|
betaUsageDescription: 'ベータ期間中は使用制限がありません',
|
|
@@ -316,12 +329,16 @@ const FIGMA_UI_TRANSLATIONS = {
|
|
|
316
329
|
'form.submit': '有効化',
|
|
317
330
|
'form.verifying': '確認中...',
|
|
318
331
|
'license.youtube_membership': 'YouTubeメンバーシップ',
|
|
332
|
+
'license.current_plan': '現在のプラン',
|
|
333
|
+
'license.pro_plan': 'Proプラン',
|
|
334
|
+
'license.membership': 'メンバーシップ',
|
|
319
335
|
'license.pro_account': 'Proアカウント',
|
|
320
336
|
'license.upgrade_to_pro': 'Proにアップグレード',
|
|
321
337
|
'license.all_features_available': 'すべての機能を使用できます。',
|
|
322
338
|
'license.youtube_membership_description': 'YouTubeメンバーシップに参加すると、Designbaseプラグインを無制限で使用できます。',
|
|
323
339
|
'license.purchase_for_unlimited': 'Designbaseのメンバーシップまたはライセンスで無制限に利用できます。',
|
|
324
340
|
'license.usage_unlimited': '使用回数の制限なし',
|
|
341
|
+
'license.active_until_expiry': '有効期限まで、すべての機能を制限なく使用できます。',
|
|
325
342
|
'license.usage_daily': '今日の残り {{remaining}}/{{limit}}回',
|
|
326
343
|
'license.usage_weekly': '今週の残り {{remaining}}/{{limit}}回',
|
|
327
344
|
'license.usage_monthly': '今月の残り {{remaining}}/{{limit}}回',
|
|
@@ -331,6 +348,7 @@ const FIGMA_UI_TRANSLATIONS = {
|
|
|
331
348
|
'license.spots_remaining': '枠残り',
|
|
332
349
|
'license.deactivating': '無効化中...',
|
|
333
350
|
'license.deactivate': 'ライセンスを無効化',
|
|
351
|
+
'license.deactivate_hint': 'このデバイスからライセンスの接続を解除します。',
|
|
334
352
|
'license.designbase_account': 'Designbaseアカウント',
|
|
335
353
|
'license.designbase_account_hint': 'WordPressアカウントでサインインするか、メンバーシップを購入するとプラグインが自動的に有効化されます。',
|
|
336
354
|
'license.authenticating': '認証中...',
|
|
@@ -979,7 +997,7 @@ const fallbackText = (t, key, fallback) => {
|
|
|
979
997
|
const translated = t?.(key);
|
|
980
998
|
return translated && translated !== key ? translated : fallback;
|
|
981
999
|
};
|
|
982
|
-
const UsageStatusPopover = ({ paymentStatus, usageCount = 0, maxDailyUsage = 20, showUsage = true, showPlan = true, isLoading = false, onLicensePageClick, t, locale, className, }) => {
|
|
1000
|
+
const UsageStatusPopover = ({ paymentStatus, usageCount = 0, remainingUsage, maxDailyUsage = 20, showUsage = true, showPlan = true, isLoading = false, onLicensePageClick, licenseSource, accessScope, expiresAt, lastVerifiedAt, t, locale, className, }) => {
|
|
983
1001
|
const { t: translate } = useFigmaUiI18n(locale, t);
|
|
984
1002
|
const [isOpen, setIsOpen] = useState(false);
|
|
985
1003
|
const rootRef = useRef(null);
|
|
@@ -989,9 +1007,11 @@ const UsageStatusPopover = ({ paymentStatus, usageCount = 0, maxDailyUsage = 20,
|
|
|
989
1007
|
const isBeta = paymentStatus === 'BETA';
|
|
990
1008
|
const hasDailyLimit = !isPaid && maxDailyUsage > 0;
|
|
991
1009
|
const limit = Math.max(1, Math.round(maxDailyUsage));
|
|
992
|
-
const
|
|
993
|
-
const remaining =
|
|
994
|
-
|
|
1010
|
+
const hasRemainingUsage = typeof remainingUsage === 'number' && Number.isFinite(remainingUsage);
|
|
1011
|
+
const remaining = hasRemainingUsage
|
|
1012
|
+
? Math.min(limit, Math.max(0, Math.round(remainingUsage)))
|
|
1013
|
+
: Math.max(0, limit - Math.min(limit, Math.max(0, Math.round(usageCount))));
|
|
1014
|
+
const remainingPercent = hasDailyLimit ? Math.round((remaining / limit) * 100) : 0;
|
|
995
1015
|
const hasControl = showUsage || showPlan;
|
|
996
1016
|
const planLabel = isPaid
|
|
997
1017
|
? fallbackText(translate, 'proUser', '프로')
|
|
@@ -1000,9 +1020,12 @@ const UsageStatusPopover = ({ paymentStatus, usageCount = 0, maxDailyUsage = 20,
|
|
|
1000
1020
|
: paymentStatus === 'TRIAL'
|
|
1001
1021
|
? fallbackText(translate, 'trialPlan', '체험판')
|
|
1002
1022
|
: fallbackText(translate, 'free', '무료');
|
|
1003
|
-
const
|
|
1023
|
+
const planSuffix = fallbackText(translate, 'planSuffix', '플랜');
|
|
1024
|
+
const normalizedPlanLabel = planLabel.toLocaleLowerCase();
|
|
1025
|
+
const normalizedPlanSuffix = planSuffix.toLocaleLowerCase();
|
|
1026
|
+
const planTitle = isBeta || normalizedPlanLabel.includes(normalizedPlanSuffix)
|
|
1004
1027
|
? planLabel
|
|
1005
|
-
: `${planLabel} ${
|
|
1028
|
+
: `${planLabel} ${planSuffix}`;
|
|
1006
1029
|
const triggerLabel = isPaid
|
|
1007
1030
|
? `${planLabel}, ${fallbackText(translate, 'unlimitedUsage', '무제한 사용 가능')}`
|
|
1008
1031
|
: isBeta
|
|
@@ -1013,6 +1036,41 @@ const UsageStatusPopover = ({ paymentStatus, usageCount = 0, maxDailyUsage = 20,
|
|
|
1013
1036
|
const upgradeLabel = isPaid
|
|
1014
1037
|
? fallbackText(translate, 'managePlan', '플랜 관리')
|
|
1015
1038
|
: fallbackText(translate, 'upgradePlan', '플랜 업그레이드');
|
|
1039
|
+
const resolvedLocale = locale || 'ko';
|
|
1040
|
+
const formatDate = (value) => {
|
|
1041
|
+
if (!value)
|
|
1042
|
+
return undefined;
|
|
1043
|
+
const date = new Date(value);
|
|
1044
|
+
if (Number.isNaN(date.getTime()))
|
|
1045
|
+
return value;
|
|
1046
|
+
return new Intl.DateTimeFormat(resolvedLocale, { dateStyle: 'medium' }).format(date);
|
|
1047
|
+
};
|
|
1048
|
+
const formattedSource = licenseSource === 'youtube_membership' || licenseSource === 'membership'
|
|
1049
|
+
? fallbackText(translate, 'license.source_youtube_membership', 'YouTube 멤버십')
|
|
1050
|
+
: licenseSource === 'lemonsqueezy'
|
|
1051
|
+
? fallbackText(translate, 'license.source_purchase', '구매 라이선스')
|
|
1052
|
+
: licenseSource;
|
|
1053
|
+
const formattedScope = accessScope === 'all-products' || accessScope === 'all_products'
|
|
1054
|
+
? fallbackText(translate, 'license.all_plugins', '모든 플러그인')
|
|
1055
|
+
: accessScope;
|
|
1056
|
+
const paidMetadata = [
|
|
1057
|
+
formattedSource && {
|
|
1058
|
+
label: fallbackText(translate, 'license.membership', '멤버십'),
|
|
1059
|
+
value: formattedSource,
|
|
1060
|
+
},
|
|
1061
|
+
expiresAt && {
|
|
1062
|
+
label: fallbackText(translate, 'license.expires', '만료일'),
|
|
1063
|
+
value: formatDate(expiresAt),
|
|
1064
|
+
},
|
|
1065
|
+
formattedScope && {
|
|
1066
|
+
label: fallbackText(translate, 'license.access', '접근 범위'),
|
|
1067
|
+
value: formattedScope,
|
|
1068
|
+
},
|
|
1069
|
+
lastVerifiedAt && {
|
|
1070
|
+
label: fallbackText(translate, 'license.last_checked', '최근 확인'),
|
|
1071
|
+
value: formatDate(lastVerifiedAt),
|
|
1072
|
+
},
|
|
1073
|
+
].filter(Boolean);
|
|
1016
1074
|
useEffect(() => {
|
|
1017
1075
|
if (!isOpen)
|
|
1018
1076
|
return undefined;
|
|
@@ -1047,9 +1105,12 @@ const UsageStatusPopover = ({ paymentStatus, usageCount = 0, maxDailyUsage = 20,
|
|
|
1047
1105
|
'designbase-figma-usage-status--loading': isLoading,
|
|
1048
1106
|
}, className) },
|
|
1049
1107
|
React.createElement("div", { className: "designbase-figma-usage-status__control" },
|
|
1050
|
-
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(translate, 'loading', '불러오는 중') : triggerLabel, disabled: isLoading, onClick: togglePopover }, isLoading ? (React.createElement("span", { className: "designbase-figma-usage-status__skeleton", "aria-hidden": "true" })) : isPaid ? (React.createElement(
|
|
1108
|
+
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(translate, 'loading', '불러오는 중') : triggerLabel, disabled: isLoading, onClick: togglePopover }, isLoading ? (React.createElement("span", { className: "designbase-figma-usage-status__skeleton", "aria-hidden": "true" })) : isPaid ? (React.createElement(React.Fragment, null,
|
|
1109
|
+
React.createElement("span", { className: "designbase-figma-usage-status__mini-progress", "aria-hidden": "true" },
|
|
1110
|
+
React.createElement("span", { style: { width: '100%' } })),
|
|
1111
|
+
React.createElement("span", { className: "designbase-figma-usage-status__unlimited-mark", "aria-hidden": "true" }, "\u221E"))) : hasDailyLimit ? (React.createElement(React.Fragment, null,
|
|
1051
1112
|
React.createElement("span", { className: "designbase-figma-usage-status__mini-progress", "aria-hidden": "true" },
|
|
1052
|
-
React.createElement("span", { style: { width: `${
|
|
1113
|
+
React.createElement("span", { style: { width: `${remainingPercent}%` } })),
|
|
1053
1114
|
React.createElement("span", { className: "designbase-figma-usage-status__usage-value" },
|
|
1054
1115
|
React.createElement("strong", null, remaining),
|
|
1055
1116
|
React.createElement("span", null,
|
|
@@ -1058,43 +1119,34 @@ const UsageStatusPopover = ({ paymentStatus, usageCount = 0, maxDailyUsage = 20,
|
|
|
1058
1119
|
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))),
|
|
1059
1120
|
isOpen && (React.createElement("div", { id: popoverId, className: "designbase-figma-usage-status__popover", role: "dialog", "aria-label": fallbackText(translate, 'usageDetails', '사용량 상세') },
|
|
1060
1121
|
React.createElement("div", { className: "designbase-figma-usage-status__popover-header" },
|
|
1061
|
-
React.createElement("span", { className: "designbase-figma-usage-status__plan-
|
|
1122
|
+
React.createElement("span", { className: "designbase-figma-usage-status__plan-heading" },
|
|
1123
|
+
React.createElement("span", null, fallbackText(translate, 'license.current_plan', '현재 플랜')),
|
|
1124
|
+
React.createElement("strong", null, planTitle)),
|
|
1062
1125
|
!isBeta && (React.createElement(Button, { className: "designbase-figma-usage-status__upgrade", variant: isPaid ? 'secondary' : 'primary', size: "s", endIcon: "chevron-right", onPress: handleManageClick }, upgradeLabel))),
|
|
1063
|
-
isPaid ? (React.createElement(
|
|
1064
|
-
React.createElement("
|
|
1065
|
-
|
|
1126
|
+
isPaid ? (React.createElement(React.Fragment, null,
|
|
1127
|
+
React.createElement("div", { className: "designbase-figma-usage-status__unlimited-copy" },
|
|
1128
|
+
React.createElement("span", { className: "designbase-figma-usage-status__unlimited-mark", "aria-hidden": "true" }, "\u221E"),
|
|
1129
|
+
React.createElement("span", null,
|
|
1130
|
+
React.createElement("strong", null, fallbackText(translate, 'unlimitedUsage', '무제한 사용')),
|
|
1131
|
+
React.createElement("small", null, fallbackText(translate, 'unlimitedUsageTooltip', '모든 기능을 제한 없이 사용할 수 있습니다')))),
|
|
1132
|
+
paidMetadata.length > 0 && (React.createElement("dl", { className: "designbase-figma-usage-status__license-meta" }, paidMetadata.map((item) => (React.createElement("div", { key: item.label },
|
|
1133
|
+
React.createElement("dt", null, item.label),
|
|
1134
|
+
React.createElement("dd", null, item.value)))))))) : isBeta ? (React.createElement("p", { className: "designbase-figma-usage-status__note" }, fallbackText(translate, 'betaUsageDescription', '베타 기간에는 사용량 제한이 적용되지 않습니다'))) : hasDailyLimit ? (React.createElement("div", { className: "designbase-figma-usage-status__usage-detail" },
|
|
1066
1135
|
React.createElement("div", { className: "designbase-figma-usage-status__usage-summary" },
|
|
1067
1136
|
React.createElement("strong", { className: "designbase-figma-usage-status__usage-count" },
|
|
1068
|
-
|
|
1137
|
+
remaining,
|
|
1069
1138
|
React.createElement("span", { className: "designbase-figma-usage-status__usage-divider" }, "/"),
|
|
1070
1139
|
limit),
|
|
1071
|
-
React.createElement("span", { className: "designbase-figma-usage-status__usage-caption" }, fallbackText(translate, '
|
|
1072
|
-
|
|
1073
|
-
|
|
1140
|
+
React.createElement("span", { className: "designbase-figma-usage-status__usage-caption" }, fallbackText(translate, 'remainingToday', '오늘 남은 사용량')),
|
|
1141
|
+
React.createElement("strong", { className: "designbase-figma-usage-status__usage-percent" },
|
|
1142
|
+
remainingPercent,
|
|
1143
|
+
"%")),
|
|
1144
|
+
React.createElement("div", { className: "designbase-figma-usage-status__progress", role: "progressbar", "aria-label": fallbackText(translate, 'remainingToday', '오늘 남은 사용량'), "aria-valuemin": 0, "aria-valuemax": limit, "aria-valuenow": remaining, "aria-valuetext": `${remaining} / ${limit}, ${remainingPercent}%` },
|
|
1145
|
+
React.createElement("span", { style: { width: `${remainingPercent}%` } })),
|
|
1074
1146
|
React.createElement("p", { className: "designbase-figma-usage-status__remaining-copy" }, resolveText(translate, { key: 'remainingCopy', values: { count: remaining } }, `${remaining}회 남음 · 매일 리셋`)))) : (React.createElement("p", { className: "designbase-figma-usage-status__note" }, fallbackText(translate, 'usageUnavailable', '사용량 제한이 없습니다')))))));
|
|
1075
1147
|
};
|
|
1076
1148
|
UsageStatusPopover.displayName = 'UsageStatusPopover';
|
|
1077
1149
|
|
|
1078
|
-
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 = { key: 'donation.buy_me_a_coffee' }, showDonationText = true, donationPromptText, showLanguageSelector = true, currentLanguage = 'ko', languages, onLanguageChange, logoSrc, logoAlt = 'DesignBase', logoType = 'designbase', logoSize = 'xs', logoLinks, className, t, children, }) => {
|
|
1079
|
-
const { t: translate } = useFigmaUiI18n(currentLanguage, t);
|
|
1080
|
-
const resolvedShowUsageInfo = showPaymentStatus && showUsageInfo;
|
|
1081
|
-
const resolvedShowPaymentBadge = showPaymentStatus && showPaymentBadge;
|
|
1082
|
-
const resolvedLicensePageClick = paymentStatus === 'BETA' ? undefined : onLicensePageClick;
|
|
1083
|
-
const hasChildren = React.Children.count(children) > 0;
|
|
1084
|
-
const classes = clsx('designbase-figma-footer', {
|
|
1085
|
-
'designbase-figma-footer--has-children': hasChildren,
|
|
1086
|
-
}, className);
|
|
1087
|
-
return (React.createElement("footer", { className: classes },
|
|
1088
|
-
React.createElement("div", { className: "designbase-figma-footer__wrap" },
|
|
1089
|
-
React.createElement("div", { className: "designbase-figma-footer__left" },
|
|
1090
|
-
React.createElement(LogoDropdown, { logoSrc: logoSrc, logoAlt: logoAlt, logoType: logoType, logoSize: logoSize, links: logoLinks, languages: languages, currentLanguage: currentLanguage, showLanguageSelector: showLanguageSelector, onLanguageChange: onLanguageChange, t: translate })),
|
|
1091
|
-
React.createElement("div", { className: "designbase-figma-footer__right" },
|
|
1092
|
-
(resolvedShowUsageInfo || resolvedShowPaymentBadge) && (React.createElement(UsageStatusPopover, { paymentStatus: paymentStatus, usageCount: usageCount, maxDailyUsage: maxDailyUsage, showUsage: resolvedShowUsageInfo, showPlan: resolvedShowPaymentBadge, isLoading: isLoading, onLicensePageClick: resolvedLicensePageClick, t: translate })),
|
|
1093
|
-
showDonation && (React.createElement(DonationBadge, { donationUrl: donationUrl, text: donationText, iconType: "heart", size: "s", iconOnly: true, showPrompt: false, t: translate })))),
|
|
1094
|
-
children));
|
|
1095
|
-
};
|
|
1096
|
-
FigmaFooter.displayName = 'FigmaFooter';
|
|
1097
|
-
|
|
1098
1150
|
const FigmaToastStack = ({ toasts, position, maxToasts, onRemoveToast, }) => {
|
|
1099
1151
|
const visibleToasts = [...toasts]
|
|
1100
1152
|
.sort((a, b) => b.createdAt - a.createdAt)
|
|
@@ -1155,26 +1207,195 @@ const FigmaToast = ({ message, isVisible, onClose, type = 'info', duration = 300
|
|
|
1155
1207
|
React.createElement(Toast, { toastId: "figma-toast-simple", title: message, status: type, duration: duration, showProgress: false, showClose: true, onClose: onClose })));
|
|
1156
1208
|
};
|
|
1157
1209
|
const useFigmaToast = () => {
|
|
1158
|
-
const [toast, setToast] = useState({
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1210
|
+
const [toast, setToast] = useState({
|
|
1211
|
+
message: '',
|
|
1212
|
+
isVisible: false,
|
|
1213
|
+
type: 'info',
|
|
1214
|
+
});
|
|
1215
|
+
const timeoutRef = useRef(null);
|
|
1216
|
+
const showToast = useCallback((message, type = 'info') => {
|
|
1217
|
+
if (timeoutRef.current)
|
|
1218
|
+
clearTimeout(timeoutRef.current);
|
|
1219
|
+
setToast({ message, isVisible: true, type });
|
|
1220
|
+
timeoutRef.current = setTimeout(() => {
|
|
1221
|
+
setToast({ message: '', isVisible: false, type: 'info' });
|
|
1222
|
+
timeoutRef.current = null;
|
|
1223
|
+
}, 5000);
|
|
1224
|
+
}, []);
|
|
1225
|
+
useEffect(() => () => {
|
|
1226
|
+
if (timeoutRef.current)
|
|
1227
|
+
clearTimeout(timeoutRef.current);
|
|
1162
1228
|
}, []);
|
|
1163
|
-
const toastComponent = toast.isVisible ? (React.createElement(FigmaToast, { message: toast.message, isVisible: toast.isVisible, onClose: () => setToast({ message: '', isVisible: false }), type:
|
|
1229
|
+
const toastComponent = toast.isVisible ? (React.createElement(FigmaToast, { message: toast.message, isVisible: toast.isVisible, onClose: () => setToast({ message: '', isVisible: false, type: 'info' }), type: toast.type, duration: 5000 })) : null;
|
|
1164
1230
|
return { showToast, toastComponent };
|
|
1165
1231
|
};
|
|
1166
1232
|
FigmaToastProvider.displayName = 'FigmaToastProvider';
|
|
1167
1233
|
FigmaToast.displayName = 'FigmaToast';
|
|
1168
1234
|
|
|
1235
|
+
const DEFAULT_LOW_USAGE_WARNING = { key: 'lowUsageWarning' };
|
|
1236
|
+
const FigmaFooter = ({ onLicensePageClick, paymentStatus = 'FREE', usageCount = 0, remainingUsage, listenForUsageMessages = true, isLoading = false, showPaymentStatus = true, showUsageInfo = true, maxDailyUsage = 20, licenseSource, accessScope, expiresAt, lastVerifiedAt, showLowUsageWarning = true, lowUsageThresholdPercent = 10, lowUsageWarningText = DEFAULT_LOW_USAGE_WARNING, onLowUsageWarning, showPaymentBadge = true, showDonation = true, donationUrl = 'https://buymeacoffee.com/designbase', donationText = { key: 'donation.buy_me_a_coffee' }, showDonationText = true, donationPromptText, showLanguageSelector = true, currentLanguage = 'ko', languages, onLanguageChange, logoSrc, logoAlt = 'DesignBase', logoType = 'designbase', logoSize = 'xs', logoLinks, className, t, children, }) => {
|
|
1237
|
+
const { t: translate } = useFigmaUiI18n(currentLanguage, t);
|
|
1238
|
+
const { showToast, toastComponent } = useFigmaToast();
|
|
1239
|
+
const [liveUsage, setLiveUsage] = useState({});
|
|
1240
|
+
const previousRemainingRef = useRef(null);
|
|
1241
|
+
const hasWarnedLowUsageRef = useRef(false);
|
|
1242
|
+
const effectivePaymentStatus = liveUsage.paymentStatus ?? paymentStatus;
|
|
1243
|
+
const effectiveUsageCount = liveUsage.usageCount ?? usageCount;
|
|
1244
|
+
const effectiveRemainingUsage = liveUsage.remainingUsage ?? remainingUsage;
|
|
1245
|
+
const effectiveMaxDailyUsage = liveUsage.maxDailyUsage ?? maxDailyUsage;
|
|
1246
|
+
const effectiveLicenseSource = liveUsage.licenseSource ?? licenseSource;
|
|
1247
|
+
const effectiveAccessScope = liveUsage.accessScope ?? accessScope;
|
|
1248
|
+
const effectiveExpiresAt = liveUsage.expiresAt ?? expiresAt;
|
|
1249
|
+
const effectiveLastVerifiedAt = liveUsage.lastVerifiedAt ?? lastVerifiedAt;
|
|
1250
|
+
const limit = Math.max(1, Math.round(effectiveMaxDailyUsage));
|
|
1251
|
+
const resolvedRemaining = typeof effectiveRemainingUsage === 'number' && Number.isFinite(effectiveRemainingUsage)
|
|
1252
|
+
? Math.min(limit, Math.max(0, Math.round(effectiveRemainingUsage)))
|
|
1253
|
+
: Math.max(0, limit - Math.min(limit, Math.max(0, Math.round(effectiveUsageCount))));
|
|
1254
|
+
const remainingPercent = Math.round((resolvedRemaining / limit) * 100);
|
|
1255
|
+
const resolvedShowUsageInfo = showPaymentStatus
|
|
1256
|
+
&& (showUsageInfo || effectivePaymentStatus === 'FREE' || effectivePaymentStatus === 'PAID');
|
|
1257
|
+
const resolvedShowPaymentBadge = showPaymentStatus && showPaymentBadge;
|
|
1258
|
+
const resolvedLicensePageClick = effectivePaymentStatus === 'BETA' ? undefined : onLicensePageClick;
|
|
1259
|
+
const hasChildren = React.Children.count(children) > 0;
|
|
1260
|
+
useEffect(() => {
|
|
1261
|
+
if (!listenForUsageMessages || typeof window === 'undefined')
|
|
1262
|
+
return undefined;
|
|
1263
|
+
const handleUsageMessage = (event) => {
|
|
1264
|
+
const message = event.data?.pluginMessage;
|
|
1265
|
+
if (!message || !['initialize', 'update-plugin-status', 'usage-consume-result'].includes(message.type)) {
|
|
1266
|
+
return;
|
|
1267
|
+
}
|
|
1268
|
+
const nextLimit = [message.usageLimit, message.freeDailyLimit, message.maxDailyUsage]
|
|
1269
|
+
.find((value) => typeof value === 'number' && Number.isFinite(value) && value > 0);
|
|
1270
|
+
const nextUsed = [message.usageUsed, message.dailyUsage]
|
|
1271
|
+
.find((value) => typeof value === 'number' && Number.isFinite(value) && value >= 0);
|
|
1272
|
+
const nextRemaining = typeof message.remainingUsage === 'number' && Number.isFinite(message.remainingUsage)
|
|
1273
|
+
? message.remainingUsage
|
|
1274
|
+
: typeof nextLimit === 'number' && typeof nextUsed === 'number'
|
|
1275
|
+
? Math.max(0, nextLimit - nextUsed)
|
|
1276
|
+
: undefined;
|
|
1277
|
+
const nextPaymentStatus = message.paymentStatus === 'PAID'
|
|
1278
|
+
? 'PAID'
|
|
1279
|
+
: message.paymentStatus === 'FREE' || message.paymentStatus === 'UNPAID'
|
|
1280
|
+
? 'FREE'
|
|
1281
|
+
: undefined;
|
|
1282
|
+
const nextLicenseSource = typeof message.licenseSource === 'string' ? message.licenseSource : undefined;
|
|
1283
|
+
const nextAccessScope = typeof message.accessScope === 'string' ? message.accessScope : undefined;
|
|
1284
|
+
const nextExpiresAt = typeof message.expiresAt === 'string' ? message.expiresAt : undefined;
|
|
1285
|
+
const rawLastVerifiedAt = message.lastVerifiedAt;
|
|
1286
|
+
const numericLastVerifiedAt = typeof rawLastVerifiedAt === 'number' && Number.isFinite(rawLastVerifiedAt)
|
|
1287
|
+
? rawLastVerifiedAt < 1000000000000
|
|
1288
|
+
? rawLastVerifiedAt * 1000
|
|
1289
|
+
: rawLastVerifiedAt
|
|
1290
|
+
: undefined;
|
|
1291
|
+
const parsedLastVerifiedAt = numericLastVerifiedAt === undefined
|
|
1292
|
+
? undefined
|
|
1293
|
+
: new Date(numericLastVerifiedAt);
|
|
1294
|
+
const nextLastVerifiedAt = typeof rawLastVerifiedAt === 'string'
|
|
1295
|
+
? rawLastVerifiedAt
|
|
1296
|
+
: parsedLastVerifiedAt && !Number.isNaN(parsedLastVerifiedAt.getTime())
|
|
1297
|
+
? parsedLastVerifiedAt.toISOString()
|
|
1298
|
+
: undefined;
|
|
1299
|
+
if (nextPaymentStatus === undefined
|
|
1300
|
+
&& nextLimit === undefined
|
|
1301
|
+
&& nextUsed === undefined
|
|
1302
|
+
&& nextRemaining === undefined
|
|
1303
|
+
&& nextLicenseSource === undefined
|
|
1304
|
+
&& nextAccessScope === undefined
|
|
1305
|
+
&& nextExpiresAt === undefined
|
|
1306
|
+
&& nextLastVerifiedAt === undefined) {
|
|
1307
|
+
return;
|
|
1308
|
+
}
|
|
1309
|
+
setLiveUsage((previous) => ({
|
|
1310
|
+
...previous,
|
|
1311
|
+
...(nextPaymentStatus ? { paymentStatus: nextPaymentStatus } : {}),
|
|
1312
|
+
...(typeof nextLimit === 'number' ? { maxDailyUsage: nextLimit } : {}),
|
|
1313
|
+
...(typeof nextUsed === 'number' ? { usageCount: nextUsed } : {}),
|
|
1314
|
+
...(typeof nextRemaining === 'number' ? { remainingUsage: nextRemaining } : {}),
|
|
1315
|
+
...(nextLicenseSource ? { licenseSource: nextLicenseSource } : {}),
|
|
1316
|
+
...(nextAccessScope ? { accessScope: nextAccessScope } : {}),
|
|
1317
|
+
...(nextExpiresAt ? { expiresAt: nextExpiresAt } : {}),
|
|
1318
|
+
...(nextLastVerifiedAt ? { lastVerifiedAt: nextLastVerifiedAt } : {}),
|
|
1319
|
+
}));
|
|
1320
|
+
};
|
|
1321
|
+
window.addEventListener('message', handleUsageMessage);
|
|
1322
|
+
return () => window.removeEventListener('message', handleUsageMessage);
|
|
1323
|
+
}, [listenForUsageMessages]);
|
|
1324
|
+
useEffect(() => {
|
|
1325
|
+
const previousRemaining = previousRemainingRef.current;
|
|
1326
|
+
const shouldTrack = showLowUsageWarning && effectivePaymentStatus === 'FREE' && effectiveMaxDailyUsage > 0;
|
|
1327
|
+
if (!shouldTrack) {
|
|
1328
|
+
previousRemainingRef.current = resolvedRemaining;
|
|
1329
|
+
hasWarnedLowUsageRef.current = false;
|
|
1330
|
+
return;
|
|
1331
|
+
}
|
|
1332
|
+
if (remainingPercent >= lowUsageThresholdPercent) {
|
|
1333
|
+
hasWarnedLowUsageRef.current = false;
|
|
1334
|
+
}
|
|
1335
|
+
else if (previousRemaining !== null
|
|
1336
|
+
&& resolvedRemaining < previousRemaining
|
|
1337
|
+
&& !hasWarnedLowUsageRef.current) {
|
|
1338
|
+
hasWarnedLowUsageRef.current = true;
|
|
1339
|
+
const warningText = typeof lowUsageWarningText === 'string'
|
|
1340
|
+
? lowUsageWarningText
|
|
1341
|
+
: {
|
|
1342
|
+
...lowUsageWarningText,
|
|
1343
|
+
values: {
|
|
1344
|
+
...(lowUsageWarningText.values || {}),
|
|
1345
|
+
count: resolvedRemaining,
|
|
1346
|
+
percent: remainingPercent,
|
|
1347
|
+
},
|
|
1348
|
+
};
|
|
1349
|
+
onLowUsageWarning?.({
|
|
1350
|
+
remaining: resolvedRemaining,
|
|
1351
|
+
percent: remainingPercent,
|
|
1352
|
+
limit,
|
|
1353
|
+
});
|
|
1354
|
+
showToast(resolveText(translate, warningText, `무료 사용량이 ${resolvedRemaining}회(${remainingPercent}%) 남았습니다.`) || `무료 사용량이 ${resolvedRemaining}회(${remainingPercent}%) 남았습니다.`, 'warning');
|
|
1355
|
+
}
|
|
1356
|
+
previousRemainingRef.current = resolvedRemaining;
|
|
1357
|
+
}, [
|
|
1358
|
+
lowUsageThresholdPercent,
|
|
1359
|
+
lowUsageWarningText,
|
|
1360
|
+
effectiveMaxDailyUsage,
|
|
1361
|
+
effectivePaymentStatus,
|
|
1362
|
+
onLowUsageWarning,
|
|
1363
|
+
remainingPercent,
|
|
1364
|
+
resolvedRemaining,
|
|
1365
|
+
showLowUsageWarning,
|
|
1366
|
+
showToast,
|
|
1367
|
+
translate,
|
|
1368
|
+
]);
|
|
1369
|
+
const classes = clsx('designbase-figma-footer', {
|
|
1370
|
+
'designbase-figma-footer--has-children': hasChildren,
|
|
1371
|
+
}, className);
|
|
1372
|
+
return (React.createElement("footer", { className: classes },
|
|
1373
|
+
React.createElement("div", { className: "designbase-figma-footer__wrap" },
|
|
1374
|
+
React.createElement("div", { className: "designbase-figma-footer__left" },
|
|
1375
|
+
React.createElement(LogoDropdown, { logoSrc: logoSrc, logoAlt: logoAlt, logoType: logoType, logoSize: logoSize, links: logoLinks, languages: languages, currentLanguage: currentLanguage, showLanguageSelector: showLanguageSelector, onLanguageChange: onLanguageChange, t: translate })),
|
|
1376
|
+
React.createElement("div", { className: "designbase-figma-footer__right" },
|
|
1377
|
+
(resolvedShowUsageInfo || resolvedShowPaymentBadge) && (React.createElement(UsageStatusPopover, { paymentStatus: effectivePaymentStatus, usageCount: effectiveUsageCount, remainingUsage: effectiveRemainingUsage, maxDailyUsage: effectiveMaxDailyUsage, showUsage: resolvedShowUsageInfo, showPlan: resolvedShowPaymentBadge, isLoading: isLoading, onLicensePageClick: resolvedLicensePageClick, licenseSource: effectiveLicenseSource, accessScope: effectiveAccessScope, expiresAt: effectiveExpiresAt, lastVerifiedAt: effectiveLastVerifiedAt, locale: currentLanguage, t: translate })),
|
|
1378
|
+
showDonation && (React.createElement(DonationBadge, { donationUrl: donationUrl, text: donationText, iconType: "heart", size: "s", iconOnly: true, showPrompt: false, t: translate })))),
|
|
1379
|
+
children,
|
|
1380
|
+
toastComponent));
|
|
1381
|
+
};
|
|
1382
|
+
FigmaFooter.displayName = 'FigmaFooter';
|
|
1383
|
+
|
|
1169
1384
|
const FormWithSubmit = ({ onLicenseSubmit, disabled = false, isSubmitting = false, value = '', onValueChange, label = { key: 'form.license_key' }, placeholder = { key: 'form.license_placeholder' }, submitText = { key: 'form.submit' }, submittingText = { key: 'form.verifying' }, helperText, className, t, locale, }) => {
|
|
1170
1385
|
const { t: translate } = useFigmaUiI18n(locale, t);
|
|
1171
1386
|
const [inputValue, setInputValue] = useState(value);
|
|
1172
1387
|
useEffect(() => {
|
|
1173
1388
|
setInputValue(value);
|
|
1174
1389
|
}, [value]);
|
|
1390
|
+
const submitLicense = async () => {
|
|
1391
|
+
const normalizedValue = inputValue.trim();
|
|
1392
|
+
if (!normalizedValue || disabled || isSubmitting)
|
|
1393
|
+
return;
|
|
1394
|
+
await onLicenseSubmit(normalizedValue);
|
|
1395
|
+
};
|
|
1175
1396
|
const handleSubmit = async (e) => {
|
|
1176
1397
|
e.preventDefault();
|
|
1177
|
-
await
|
|
1398
|
+
await submitLicense();
|
|
1178
1399
|
};
|
|
1179
1400
|
const handleChange = (value) => {
|
|
1180
1401
|
setInputValue(value);
|
|
@@ -1194,7 +1415,9 @@ const FormWithSubmit = ({ onLicenseSubmit, disabled = false, isSubmitting = fals
|
|
|
1194
1415
|
handleChange(value);
|
|
1195
1416
|
}
|
|
1196
1417
|
}, placeholder: displayPlaceholder, helperText: displayHelperText, disabled: disabled || isSubmitting, size: "m" })),
|
|
1197
|
-
React.createElement(Button, { type: "
|
|
1418
|
+
React.createElement(Button, { type: "button", variant: "primary", size: "m", loading: isSubmitting, disabled: !inputValue.trim() || isSubmitting || disabled, fullWidth: true, onPress: () => {
|
|
1419
|
+
void submitLicense();
|
|
1420
|
+
} }, isSubmitting ? displaySubmittingText : displaySubmitText)));
|
|
1198
1421
|
};
|
|
1199
1422
|
FormWithSubmit.displayName = 'FormWithSubmit';
|
|
1200
1423
|
|
|
@@ -1317,7 +1540,7 @@ function formatSource(value, t) {
|
|
|
1317
1540
|
}
|
|
1318
1541
|
return value;
|
|
1319
1542
|
}
|
|
1320
|
-
const LicenseStatusCard = ({ status, licenseKey = '', licenseStatus, licenseSource, accessScope, expiresAt, lastVerifiedAt, validationMessage, allowRevealKey = false, t, locale, className, }) => {
|
|
1543
|
+
const LicenseStatusCard = ({ status, licenseKey = '', licenseStatus, licenseSource, accessScope, expiresAt, lastVerifiedAt, validationMessage, showHeader = true, allowRevealKey = false, t, locale, className, }) => {
|
|
1321
1544
|
const { locale: resolvedLocale, t: translate } = useFigmaUiI18n(locale, t);
|
|
1322
1545
|
const [isKeyRevealed, setIsKeyRevealed] = useState(false);
|
|
1323
1546
|
const normalizedLicenseStatus = licenseStatus?.toLowerCase();
|
|
@@ -1328,13 +1551,15 @@ const LicenseStatusCard = ({ status, licenseKey = '', licenseStatus, licenseSour
|
|
|
1328
1551
|
: resolveText(translate, { key: 'license.status_inactive' }, 'License check failed');
|
|
1329
1552
|
const statusClass = isActive ? 'active' : 'inactive';
|
|
1330
1553
|
return (React.createElement("section", { className: `designbase-figma-license-status-card designbase-figma-license-status-card--${statusClass} ${className || ''}`, "aria-label": statusText },
|
|
1331
|
-
React.createElement("div", { className: "designbase-figma-license-status-card__header" },
|
|
1554
|
+
showHeader && (React.createElement("div", { className: "designbase-figma-license-status-card__header" },
|
|
1332
1555
|
React.createElement("div", null,
|
|
1333
1556
|
React.createElement("div", { className: "designbase-figma-license-status-card__eyebrow" }, isActive
|
|
1334
1557
|
? resolveText(translate, { key: 'license.activated' }, '라이선스 활성화됨')
|
|
1335
1558
|
: resolveText(translate, { key: 'license.status_label' }, '라이선스 상태')),
|
|
1336
1559
|
React.createElement("h3", null, statusText)),
|
|
1337
|
-
React.createElement("span", { className: "designbase-figma-license-status-card__badge" },
|
|
1560
|
+
React.createElement("span", { className: "designbase-figma-license-status-card__badge" }, isActive
|
|
1561
|
+
? resolveText(translate, { key: 'license.status_active' }, '활성')
|
|
1562
|
+
: resolveText(translate, { key: 'license.status_inactive' }, '비활성')))),
|
|
1338
1563
|
validationMessage && !isActive && (React.createElement("p", { className: "designbase-figma-license-status-card__message" }, validationMessage)),
|
|
1339
1564
|
licenseKey && (React.createElement("div", { className: "designbase-figma-license-status-card__key-row" },
|
|
1340
1565
|
React.createElement("span", null, resolveText(translate, { key: 'license.key_label' }, '라이선스 키')),
|
|
@@ -1729,7 +1954,7 @@ function formatUsageLabel(state, t) {
|
|
|
1729
1954
|
}
|
|
1730
1955
|
return resolveText(t, { key: 'license.usage_generic', values: { remaining, limit } }, `남은 횟수 ${remaining}/${limit}`) ?? `남은 횟수 ${remaining}/${limit}`;
|
|
1731
1956
|
}
|
|
1732
|
-
const PageLicense = ({ status: initialStatus, onClose, licenseKey: initialLicenseKey = '', wordpressLinks, paymentPageUrl, mode = 'pricing', membershipUrl, enableAccountAuth = true, showLicenseKeyInput = true, pricingFeatures, pricing, paymentType = 'subscription', messageTypes, onStateChange, setPaymentStatus, setUsageCount, onLicenseSubmit, usageCount: initialUsageCount = 0, t, locale, className, }) => {
|
|
1957
|
+
const PageLicense = ({ status: initialStatus, onClose, licenseKey: initialLicenseKey = '', licenseStatus: initialLicenseStatus, licenseSource: initialLicenseSource, accessScope: initialAccessScope, expiresAt: initialExpiresAt, lastVerifiedAt: initialLastVerifiedAt, wordpressLinks, paymentPageUrl, mode = 'pricing', membershipUrl, enableAccountAuth = true, showLicenseKeyInput = true, pricingFeatures, pricing, paymentType = 'subscription', messageTypes, onStateChange, setPaymentStatus, setUsageCount, onLicenseSubmit, usageCount: initialUsageCount = 0, t, locale, className, }) => {
|
|
1733
1958
|
const { t: translate } = useFigmaUiI18n(locale, t);
|
|
1734
1959
|
const links = useMemo(() => ({
|
|
1735
1960
|
purchaseUrl: wordpressLinks?.purchaseUrl ?? paymentPageUrl,
|
|
@@ -1742,13 +1967,21 @@ const PageLicense = ({ status: initialStatus, onClose, licenseKey: initialLicens
|
|
|
1742
1967
|
initialState: {
|
|
1743
1968
|
paymentStatus: initialStatus,
|
|
1744
1969
|
licenseKey: initialLicenseKey,
|
|
1970
|
+
hasLicenseKey: Boolean(initialLicenseKey),
|
|
1971
|
+
isLicenseRegistered: initialStatus === 'PAID',
|
|
1972
|
+
planTier: initialStatus === 'PAID' ? 'PRO' : 'FREE',
|
|
1973
|
+
usageLimit: initialStatus === 'PAID' ? 'unlimited' : 0,
|
|
1974
|
+
licenseStatus: initialLicenseStatus,
|
|
1975
|
+
licenseSource: initialLicenseSource,
|
|
1976
|
+
accessScope: initialAccessScope,
|
|
1977
|
+
expiresAt: initialExpiresAt,
|
|
1978
|
+
lastVerifiedAt: initialLastVerifiedAt,
|
|
1745
1979
|
usageCount: initialUsageCount,
|
|
1746
1980
|
},
|
|
1747
1981
|
onStateChange,
|
|
1748
1982
|
onPaymentStatusChange: setPaymentStatus,
|
|
1749
1983
|
onUsageCountChange: setUsageCount,
|
|
1750
1984
|
});
|
|
1751
|
-
const [showDetails, setShowDetails] = useState(false);
|
|
1752
1985
|
const [licenseKeyDraft, setLicenseKeyDraft] = useState(initialLicenseKey);
|
|
1753
1986
|
const isPaid = state.isLicenseRegistered && state.paymentStatus === 'PAID';
|
|
1754
1987
|
const isMembershipMode = mode === 'youtube-membership';
|
|
@@ -1773,7 +2006,7 @@ const PageLicense = ({ status: initialStatus, onClose, licenseKey: initialLicens
|
|
|
1773
2006
|
: isPaid
|
|
1774
2007
|
? resolveText(translate, { key: 'license.pro_account' }, '프로 계정')
|
|
1775
2008
|
: resolveText(translate, { key: 'license.upgrade_to_pro' }, '프로로 업그레이드')),
|
|
1776
|
-
|
|
2009
|
+
isPaid && (React.createElement("span", { className: "designbase-figma-page-license__tier" }, resolveText(translate, { key: 'license.pro_plan' }, '프로 플랜')))),
|
|
1777
2010
|
React.createElement("p", { className: "designbase-figma-page-license__description" }, isPaid
|
|
1778
2011
|
? resolveText(translate, { key: 'license.all_features_available' }, '모든 기능을 사용할 수 있습니다.')
|
|
1779
2012
|
: isMembershipMode
|
|
@@ -1781,16 +2014,16 @@ const PageLicense = ({ status: initialStatus, onClose, licenseKey: initialLicens
|
|
|
1781
2014
|
: resolveText(translate, { key: 'license.purchase_for_unlimited' }, '디자인베이스 멤버십 또는 라이선스로 무제한 이용이 가능합니다.')),
|
|
1782
2015
|
!isPaid && !isMembershipMode && (React.createElement("p", { className: "designbase-figma-page-license__usage" }, usageLabel))),
|
|
1783
2016
|
!isMembershipMode && (React.createElement(PricingComparison, { features: pricingFeatures, pricing: pricing, paymentType: paymentType, t: translate })),
|
|
1784
|
-
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)),
|
|
1785
|
-
isPaid && (React.createElement("div", { className: "designbase-figma-page-
|
|
1786
|
-
React.createElement("div", { className: "designbase-figma-page-
|
|
1787
|
-
React.createElement("
|
|
1788
|
-
React.createElement(
|
|
1789
|
-
React.createElement(
|
|
1790
|
-
|
|
1791
|
-
React.createElement(
|
|
1792
|
-
|
|
1793
|
-
React.createElement("div", { className: "designbase-figma-page-license__activation-info" },
|
|
2017
|
+
state.lastMessage && state.lastErrorCode && !state.deactivateMessage && (React.createElement("p", { className: `designbase-figma-page-license__feedback ${state.lastErrorCode ? 'designbase-figma-page-license__feedback--error' : ''}`, "aria-live": "polite" }, state.lastMessage)),
|
|
2018
|
+
isPaid && (React.createElement("div", { className: "designbase-figma-page-license__paid-summary" },
|
|
2019
|
+
React.createElement("div", { className: "designbase-figma-page-license__paid-hero" },
|
|
2020
|
+
React.createElement("span", { "aria-hidden": "true" }, "\u221E"),
|
|
2021
|
+
React.createElement("div", null,
|
|
2022
|
+
React.createElement("strong", null, resolveText(translate, { key: 'license.usage_unlimited' }, '무제한 사용')),
|
|
2023
|
+
React.createElement("p", null, resolveText(translate, { key: 'license.active_until_expiry' }, '만료일까지 모든 기능을 제한 없이 사용할 수 있습니다.')))),
|
|
2024
|
+
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, showHeader: false, t: translate }),
|
|
2025
|
+
React.createElement("div", { className: "designbase-figma-page-license__paid-actions" },
|
|
2026
|
+
state.activationLimit > 0 && (React.createElement("div", { className: "designbase-figma-page-license__activation-info" },
|
|
1794
2027
|
React.createElement("div", { className: "designbase-figma-page-license__remaining" },
|
|
1795
2028
|
Math.max(0, state.activationLimit - state.activationUsage),
|
|
1796
2029
|
' ',
|
|
@@ -1800,12 +2033,12 @@ const PageLicense = ({ status: initialStatus, onClose, licenseKey: initialLicens
|
|
|
1800
2033
|
React.createElement("div", { className: "designbase-figma-page-license__usage-count" },
|
|
1801
2034
|
state.activationUsage,
|
|
1802
2035
|
"/",
|
|
1803
|
-
state.activationLimit)),
|
|
1804
|
-
React.createElement(
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
2036
|
+
state.activationLimit))),
|
|
2037
|
+
React.createElement(Button, { onPress: deactivateLicense, variant: "secondary", size: "m", fullWidth: true, disabled: isDeactivating }, isDeactivating
|
|
2038
|
+
? resolveText(translate, { key: 'license.deactivating' }, '비활성화 중...')
|
|
2039
|
+
: resolveText(translate, { key: 'license.deactivate' }, '라이선스 비활성화')),
|
|
2040
|
+
state.deactivateMessage && state.deactivateErrorCode && (React.createElement("p", { className: `designbase-figma-page-license__action-feedback designbase-figma-page-license__action-feedback--${resolvePaymentFeedbackVariant(state.deactivateErrorCode)}`, role: "alert", "aria-live": "polite" }, state.deactivateMessage)),
|
|
2041
|
+
React.createElement("p", { className: "designbase-figma-page-license__deactivate-hint" }, resolveText(translate, { key: 'license.deactivate_hint' }, '이 기기에서 라이선스 연결을 해제합니다.'))))),
|
|
1809
2042
|
!isPaid && (React.createElement(React.Fragment, null,
|
|
1810
2043
|
isMembershipMode && (React.createElement(LicenseOffer, { joinUrl: links.membershipUrl || links.purchaseUrl, guideUrl: links.helpUrl || links.purchaseUrl, t: translate })),
|
|
1811
2044
|
!isMembershipMode && (enableAccountAuth || links.purchaseUrl || links.accountUrl) && (React.createElement("div", { className: "designbase-figma-page-license__section" },
|