@designbasekorea/figma-ui 0.6.9 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -2
- package/dist/index.css +1 -1
- package/dist/index.css.map +1 -1
- package/dist/index.d.ts +53 -6
- package/dist/index.esm.css +1 -1
- package/dist/index.esm.css.map +1 -1
- package/dist/index.esm.js +819 -295
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +959 -428
- package/dist/index.js.map +1 -1
- package/package.json +9 -6
package/dist/index.esm.js
CHANGED
|
@@ -1,11 +1,432 @@
|
|
|
1
|
-
import React, { useRef, useState, useLayoutEffect, useEffect, useCallback, useId
|
|
1
|
+
import React, { createContext, useMemo, useContext, useRef, useState, useLayoutEffect, useEffect, useCallback, useId } from 'react';
|
|
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
5
|
import { ChevronDownIcon, AppWindowIcon, YoutubeIcon, InstagramIcon, FigmaIcon, MailIcon, ExternalLinkIcon, TranslateIcon, MoreHorizontalIcon, CloseIcon, CircleCheckFilledIcon, ResizableIcon, GripVerticalIcon, ChevronRightIcon } from '@designbasekorea/icons';
|
|
6
|
+
import { DEFAULT_LICENSE_MESSAGE_TYPES } from '@designbasekorea/figma-license-core';
|
|
7
|
+
export { DEFAULT_LICENSE_MESSAGE_TYPES } from '@designbasekorea/figma-license-core';
|
|
6
8
|
|
|
7
9
|
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
10
|
|
|
11
|
+
const FIGMA_UI_TRANSLATIONS = {
|
|
12
|
+
ko: {
|
|
13
|
+
close: '닫기',
|
|
14
|
+
back: '뒤로가기',
|
|
15
|
+
officialWebsite: '공식 웹사이트',
|
|
16
|
+
youtube: '유튜브',
|
|
17
|
+
instagram: '인스타그램',
|
|
18
|
+
figmaCommunity: '피그마 커뮤니티',
|
|
19
|
+
contact: '문의하기',
|
|
20
|
+
languageSettings: '언어 설정',
|
|
21
|
+
languageSettingsDescription: '플러그인에 표시할 언어를 선택하세요.',
|
|
22
|
+
'language.korean': '한국어',
|
|
23
|
+
'language.english': 'English',
|
|
24
|
+
'language.japanese': '日本語',
|
|
25
|
+
unlimitedUsage: '무제한 사용',
|
|
26
|
+
proUser: '프로',
|
|
27
|
+
usingFree: '무료 사용',
|
|
28
|
+
free: '무료',
|
|
29
|
+
trialPlan: '체험판',
|
|
30
|
+
planSuffix: '플랜',
|
|
31
|
+
usageDetails: '사용량 상세',
|
|
32
|
+
dailyUsage: '오늘 사용량',
|
|
33
|
+
usedToday: '오늘 사용',
|
|
34
|
+
remainingToday: '오늘 남은 사용량',
|
|
35
|
+
remainingUsage: '오늘 남은 사용량',
|
|
36
|
+
managePlan: '플랜 관리',
|
|
37
|
+
upgradePlan: '플랜 업그레이드',
|
|
38
|
+
remainingShort: '남음',
|
|
39
|
+
usageCountCaption: '오늘 사용',
|
|
40
|
+
remainingCopy: '{{count}}회 남음 · 매일 리셋',
|
|
41
|
+
lowUsageWarning: '무료 사용량이 {{count}}회({{percent}}%) 남았습니다. 멤버십으로 무제한 이용해보세요.',
|
|
42
|
+
usageUnavailable: '사용량 제한이 없습니다',
|
|
43
|
+
unlimitedUsageTooltip: '모든 기능을 제한 없이 사용할 수 있습니다',
|
|
44
|
+
betaUsageDescription: '베타 기간에는 사용량 제한이 적용되지 않습니다',
|
|
45
|
+
perDay: '일',
|
|
46
|
+
resetsDaily: '매일 리셋',
|
|
47
|
+
donationPrompt: '이 플러그인이 유용했나요?',
|
|
48
|
+
betaStatus: 'BETA',
|
|
49
|
+
'donation.buy_me_a_coffee': '커피 한 잔 사기',
|
|
50
|
+
'donation.prompt': '도움이 되었다면 커피 한 잔 사주세요!',
|
|
51
|
+
'form.license_key': '라이선스 키',
|
|
52
|
+
'form.license_placeholder': 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx',
|
|
53
|
+
'form.submit': '제출',
|
|
54
|
+
'form.verifying': '확인 중...',
|
|
55
|
+
'license.youtube_membership': 'YouTube 멤버십',
|
|
56
|
+
'license.pro_account': '프로 계정',
|
|
57
|
+
'license.upgrade_to_pro': '프로로 업그레이드',
|
|
58
|
+
'license.all_features_available': '모든 기능을 사용할 수 있습니다.',
|
|
59
|
+
'license.youtube_membership_description': 'YouTube 멤버십에 가입하면 Designbase 플러그인을 무제한으로 사용할 수 있습니다.',
|
|
60
|
+
'license.purchase_for_unlimited': '디자인베이스 멤버십 또는 라이선스로 무제한 이용이 가능합니다.',
|
|
61
|
+
'license.usage_unlimited': '사용 횟수 제한 없음',
|
|
62
|
+
'license.usage_daily': '오늘 남은 횟수 {{remaining}}/{{limit}}',
|
|
63
|
+
'license.usage_weekly': '이번 주 남은 횟수 {{remaining}}/{{limit}}',
|
|
64
|
+
'license.usage_monthly': '이번 달 남은 횟수 {{remaining}}/{{limit}}',
|
|
65
|
+
'license.usage_generic': '남은 횟수 {{remaining}}/{{limit}}',
|
|
66
|
+
'license.activated': '라이선스 활성화됨',
|
|
67
|
+
'license.activations_remaining': '활성',
|
|
68
|
+
'license.spots_remaining': '자리 남음',
|
|
69
|
+
'license.deactivating': '비활성화 중...',
|
|
70
|
+
'license.deactivate': '라이선스 비활성화',
|
|
71
|
+
'license.designbase_account': '디자인베이스 계정',
|
|
72
|
+
'license.designbase_account_hint': 'WordPress 계정으로 로그인하거나 멤버십을 구매하면 플러그인이 자동으로 활성화됩니다.',
|
|
73
|
+
'license.authenticating': '인증 중...',
|
|
74
|
+
'license.login_with_designbase': '디자인베이스로 로그인',
|
|
75
|
+
'license.purchase_membership': '멤버십 안내',
|
|
76
|
+
'license.manage_account': '계정·멤버십 관리',
|
|
77
|
+
'license.enter_key': '라이선스 키 입력',
|
|
78
|
+
'license.key': '라이선스 키',
|
|
79
|
+
'license.submit': '활성화',
|
|
80
|
+
'license.verifying': '확인 중...',
|
|
81
|
+
'license.enter_from_email': '이메일로 받은 라이선스 키를 입력하세요.',
|
|
82
|
+
'license.enter_from_youtube': '멤버 전용 커뮤니티에서 받은 키를 입력하세요.',
|
|
83
|
+
'license.help_link': '라이선스·결제 도움말',
|
|
84
|
+
'license.youtube_membership_howto': '이용 방법',
|
|
85
|
+
'license.youtube_membership_step_1': 'Designbase YouTube 채널 멤버십에 가입합니다.',
|
|
86
|
+
'license.youtube_membership_step_2': '멤버 전용 커뮤니티에서 이번 달 키를 복사합니다.',
|
|
87
|
+
'license.youtube_membership_step_3': '아래 입력창에 키를 붙여 넣고 활성화합니다.',
|
|
88
|
+
'license.youtube_membership_join': '멤버십 가입',
|
|
89
|
+
'license.youtube_membership_guide': '안내보기',
|
|
90
|
+
'license.all_plugins': '모든 플러그인',
|
|
91
|
+
'license.source_youtube_membership': 'YouTube 멤버십',
|
|
92
|
+
'license.source_purchase': '구매 라이선스',
|
|
93
|
+
'license.status_active': '활성',
|
|
94
|
+
'license.status_inactive': '라이선스 확인 실패',
|
|
95
|
+
'license.status_label': '라이선스 상태',
|
|
96
|
+
'license.key_label': '라이선스 키',
|
|
97
|
+
'license.hide_key': '숨기기',
|
|
98
|
+
'license.reveal_key': '보기',
|
|
99
|
+
'license.source': '출처',
|
|
100
|
+
'license.access': '접근 범위',
|
|
101
|
+
'license.expires': '만료일',
|
|
102
|
+
'license.last_checked': '최근 확인',
|
|
103
|
+
'payment.license_key_label': '라이선스 키',
|
|
104
|
+
'payment.activations_remaining': '활성',
|
|
105
|
+
'payment.spots_remaining': '자리 남음',
|
|
106
|
+
'payment.deactivating': '비활성화 중...',
|
|
107
|
+
'payment.deactivate_license': '라이선스 비활성화',
|
|
108
|
+
lifetimeAccess: '평생 이용',
|
|
109
|
+
perYear: '년',
|
|
110
|
+
perMonth: '월',
|
|
111
|
+
monthlyBilling: '월간 결제',
|
|
112
|
+
yearlyBilling: '연간 결제',
|
|
113
|
+
oneTimePaymentLifetime: '평생 이용을 위한 일회성 결제입니다.',
|
|
114
|
+
feature: '기능',
|
|
115
|
+
freePrice: '$0',
|
|
116
|
+
pro: '프로',
|
|
117
|
+
recommended: '추천',
|
|
118
|
+
'pricing.usage_limit': '사용 제한',
|
|
119
|
+
'pricing.daily_limit': '일일 제한',
|
|
120
|
+
'pricing.unlimited': '무제한',
|
|
121
|
+
'pricing.all_topics': '모든 주제 사용',
|
|
122
|
+
'pricing.max_selection': '최대 선택 제한',
|
|
123
|
+
'pricing.max_selection_free': '최대 5개',
|
|
124
|
+
'pricing.max_selection_pro': '최대 30개',
|
|
125
|
+
'pricing.dummy_count': '더미 개수',
|
|
126
|
+
'pricing.dummy_count_free': '1,400개',
|
|
127
|
+
'pricing.dummy_count_pro': '4,400개 이상',
|
|
128
|
+
'pricing.custom_number': '커스텀 숫자 사용',
|
|
129
|
+
'pricing.discount_suffix': '절약',
|
|
130
|
+
'settings.modal_title': '목록 변경',
|
|
131
|
+
'settings.modal_description': '드래그하여 순서를 변경하거나, 토글하여 카테고리를 숨길 수 있습니다.',
|
|
132
|
+
'settings.reset': '초기화',
|
|
133
|
+
'settings.cancel': '취소',
|
|
134
|
+
'settings.save': '저장',
|
|
135
|
+
'progress.working': '작업 중',
|
|
136
|
+
'progress.processing': '처리 중...',
|
|
137
|
+
'progress.completed': '완료되었습니다',
|
|
138
|
+
'progress.stop': '중지',
|
|
139
|
+
'progress.confirm': '확인',
|
|
140
|
+
'banner.upgrade_title': '프로로 업그레이드하세요',
|
|
141
|
+
'banner.upgrade_description': '무제한 기능을 사용하고 더 많은 혜택을 누리세요.',
|
|
142
|
+
'banner.upgrade_button': '지금 업그레이드',
|
|
143
|
+
loading: '불러오는 중',
|
|
144
|
+
},
|
|
145
|
+
en: {
|
|
146
|
+
close: 'Close',
|
|
147
|
+
back: 'Back',
|
|
148
|
+
officialWebsite: 'Official website',
|
|
149
|
+
youtube: 'YouTube',
|
|
150
|
+
instagram: 'Instagram',
|
|
151
|
+
figmaCommunity: 'Figma Community',
|
|
152
|
+
contact: 'Contact',
|
|
153
|
+
languageSettings: 'Language settings',
|
|
154
|
+
languageSettingsDescription: 'Choose the language to display in the plugin.',
|
|
155
|
+
'language.korean': 'Korean',
|
|
156
|
+
'language.english': 'English',
|
|
157
|
+
'language.japanese': 'Japanese',
|
|
158
|
+
unlimitedUsage: 'Unlimited use',
|
|
159
|
+
proUser: 'Pro',
|
|
160
|
+
usingFree: 'Free use',
|
|
161
|
+
free: 'Free',
|
|
162
|
+
trialPlan: 'Trial',
|
|
163
|
+
planSuffix: 'plan',
|
|
164
|
+
usageDetails: 'Usage details',
|
|
165
|
+
dailyUsage: "Today's usage",
|
|
166
|
+
usedToday: 'Used today',
|
|
167
|
+
remainingToday: 'Remaining today',
|
|
168
|
+
remainingUsage: 'Remaining today',
|
|
169
|
+
managePlan: 'Manage plan',
|
|
170
|
+
upgradePlan: 'Upgrade plan',
|
|
171
|
+
remainingShort: 'left',
|
|
172
|
+
usageCountCaption: 'Used today',
|
|
173
|
+
remainingCopy: '{{count}} remaining · resets daily',
|
|
174
|
+
lowUsageWarning: 'Only {{count}} free uses ({{percent}}%) remain. Upgrade for unlimited access.',
|
|
175
|
+
usageUnavailable: 'No usage limit',
|
|
176
|
+
unlimitedUsageTooltip: 'Use every feature without limits',
|
|
177
|
+
betaUsageDescription: 'Usage limits are not applied during the beta period',
|
|
178
|
+
perDay: 'day',
|
|
179
|
+
resetsDaily: 'Resets daily',
|
|
180
|
+
donationPrompt: 'Is this plugin useful?',
|
|
181
|
+
betaStatus: 'BETA',
|
|
182
|
+
'donation.buy_me_a_coffee': 'Buy me a coffee',
|
|
183
|
+
'donation.prompt': 'If this helped, buy me a coffee!',
|
|
184
|
+
'form.license_key': 'License key',
|
|
185
|
+
'form.license_placeholder': 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx',
|
|
186
|
+
'form.submit': 'Activate',
|
|
187
|
+
'form.verifying': 'Verifying...',
|
|
188
|
+
'license.youtube_membership': 'YouTube Membership',
|
|
189
|
+
'license.pro_account': 'Pro account',
|
|
190
|
+
'license.upgrade_to_pro': 'Upgrade to Pro',
|
|
191
|
+
'license.all_features_available': 'All features are available.',
|
|
192
|
+
'license.youtube_membership_description': 'Join YouTube Membership to use Designbase plugins without limits.',
|
|
193
|
+
'license.purchase_for_unlimited': 'Get unlimited access with a Designbase membership or license.',
|
|
194
|
+
'license.usage_unlimited': 'Unlimited usage',
|
|
195
|
+
'license.usage_daily': '{{remaining}}/{{limit}} remaining today',
|
|
196
|
+
'license.usage_weekly': '{{remaining}}/{{limit}} remaining this week',
|
|
197
|
+
'license.usage_monthly': '{{remaining}}/{{limit}} remaining this month',
|
|
198
|
+
'license.usage_generic': '{{remaining}}/{{limit}} remaining',
|
|
199
|
+
'license.activated': 'License activated',
|
|
200
|
+
'license.activations_remaining': 'activations',
|
|
201
|
+
'license.spots_remaining': 'spots remaining',
|
|
202
|
+
'license.deactivating': 'Deactivating...',
|
|
203
|
+
'license.deactivate': 'Deactivate license',
|
|
204
|
+
'license.designbase_account': 'Designbase account',
|
|
205
|
+
'license.designbase_account_hint': 'Sign in with your WordPress account or purchase a membership to activate the plugin automatically.',
|
|
206
|
+
'license.authenticating': 'Authenticating...',
|
|
207
|
+
'license.login_with_designbase': 'Sign in with Designbase',
|
|
208
|
+
'license.purchase_membership': 'Membership details',
|
|
209
|
+
'license.manage_account': 'Manage account · membership',
|
|
210
|
+
'license.enter_key': 'Enter license key',
|
|
211
|
+
'license.key': 'License key',
|
|
212
|
+
'license.submit': 'Activate',
|
|
213
|
+
'license.verifying': 'Verifying...',
|
|
214
|
+
'license.enter_from_email': 'Enter the license key you received by email.',
|
|
215
|
+
'license.enter_from_youtube': 'Enter the key from the members-only community.',
|
|
216
|
+
'license.help_link': 'License · payment help',
|
|
217
|
+
'license.youtube_membership_howto': 'How to use',
|
|
218
|
+
'license.youtube_membership_step_1': 'Join the Designbase YouTube channel membership.',
|
|
219
|
+
'license.youtube_membership_step_2': 'Copy this month’s key from the members-only community.',
|
|
220
|
+
'license.youtube_membership_step_3': 'Paste the key below and activate it.',
|
|
221
|
+
'license.youtube_membership_join': 'Join membership',
|
|
222
|
+
'license.youtube_membership_guide': 'View guide',
|
|
223
|
+
'license.all_plugins': 'All plugins',
|
|
224
|
+
'license.source_youtube_membership': 'YouTube Membership',
|
|
225
|
+
'license.source_purchase': 'Purchased license',
|
|
226
|
+
'license.status_active': 'Active',
|
|
227
|
+
'license.status_inactive': 'License check failed',
|
|
228
|
+
'license.status_label': 'License status',
|
|
229
|
+
'license.key_label': 'License key',
|
|
230
|
+
'license.hide_key': 'Hide',
|
|
231
|
+
'license.reveal_key': 'Show',
|
|
232
|
+
'license.source': 'Source',
|
|
233
|
+
'license.access': 'Access',
|
|
234
|
+
'license.expires': 'Expires',
|
|
235
|
+
'license.last_checked': 'Last checked',
|
|
236
|
+
'payment.license_key_label': 'License key',
|
|
237
|
+
'payment.activations_remaining': 'activations',
|
|
238
|
+
'payment.spots_remaining': 'spots remaining',
|
|
239
|
+
'payment.deactivating': 'Deactivating...',
|
|
240
|
+
'payment.deactivate_license': 'Deactivate license',
|
|
241
|
+
lifetimeAccess: 'Lifetime access',
|
|
242
|
+
perYear: 'year',
|
|
243
|
+
perMonth: 'month',
|
|
244
|
+
monthlyBilling: 'Monthly billing',
|
|
245
|
+
yearlyBilling: 'Yearly billing',
|
|
246
|
+
oneTimePaymentLifetime: 'One-time payment for lifetime access.',
|
|
247
|
+
feature: 'Feature',
|
|
248
|
+
freePrice: '$0',
|
|
249
|
+
pro: 'Pro',
|
|
250
|
+
recommended: 'Recommended',
|
|
251
|
+
'pricing.usage_limit': 'Usage limit',
|
|
252
|
+
'pricing.daily_limit': 'Daily limit',
|
|
253
|
+
'pricing.unlimited': 'Unlimited',
|
|
254
|
+
'pricing.all_topics': 'All topics',
|
|
255
|
+
'pricing.max_selection': 'Selection limit',
|
|
256
|
+
'pricing.max_selection_free': 'Up to 5',
|
|
257
|
+
'pricing.max_selection_pro': 'Up to 30',
|
|
258
|
+
'pricing.dummy_count': 'Dummy count',
|
|
259
|
+
'pricing.dummy_count_free': '1,400',
|
|
260
|
+
'pricing.dummy_count_pro': '4,400+',
|
|
261
|
+
'pricing.custom_number': 'Custom numbers',
|
|
262
|
+
'pricing.discount_suffix': 'saved',
|
|
263
|
+
'settings.modal_title': 'Change list',
|
|
264
|
+
'settings.modal_description': 'Drag to reorder or toggle a category to hide it.',
|
|
265
|
+
'settings.reset': 'Reset',
|
|
266
|
+
'settings.cancel': 'Cancel',
|
|
267
|
+
'settings.save': 'Save',
|
|
268
|
+
'progress.working': 'Working',
|
|
269
|
+
'progress.processing': 'Processing...',
|
|
270
|
+
'progress.completed': 'Completed',
|
|
271
|
+
'progress.stop': 'Stop',
|
|
272
|
+
'progress.confirm': 'Done',
|
|
273
|
+
'banner.upgrade_title': 'Upgrade to Pro',
|
|
274
|
+
'banner.upgrade_description': 'Unlock unlimited features and more benefits.',
|
|
275
|
+
'banner.upgrade_button': 'Upgrade now',
|
|
276
|
+
loading: 'Loading',
|
|
277
|
+
},
|
|
278
|
+
ja: {
|
|
279
|
+
close: '閉じる',
|
|
280
|
+
back: '戻る',
|
|
281
|
+
officialWebsite: '公式ウェブサイト',
|
|
282
|
+
youtube: 'YouTube',
|
|
283
|
+
instagram: 'Instagram',
|
|
284
|
+
figmaCommunity: 'Figmaコミュニティ',
|
|
285
|
+
contact: 'お問い合わせ',
|
|
286
|
+
languageSettings: '言語設定',
|
|
287
|
+
languageSettingsDescription: 'プラグインに表示する言語を選択してください。',
|
|
288
|
+
'language.korean': '韓国語',
|
|
289
|
+
'language.english': '英語',
|
|
290
|
+
'language.japanese': '日本語',
|
|
291
|
+
unlimitedUsage: '無制限で使用',
|
|
292
|
+
proUser: 'Pro',
|
|
293
|
+
usingFree: '無料で使用中',
|
|
294
|
+
free: '無料',
|
|
295
|
+
trialPlan: 'トライアル',
|
|
296
|
+
planSuffix: 'プラン',
|
|
297
|
+
usageDetails: '使用量の詳細',
|
|
298
|
+
dailyUsage: '今日の使用量',
|
|
299
|
+
usedToday: '今日の使用',
|
|
300
|
+
remainingToday: '今日の残り使用量',
|
|
301
|
+
remainingUsage: '今日の残り使用量',
|
|
302
|
+
managePlan: 'プランを管理',
|
|
303
|
+
upgradePlan: 'プランをアップグレード',
|
|
304
|
+
remainingShort: '残り',
|
|
305
|
+
usageCountCaption: '今日の使用',
|
|
306
|
+
remainingCopy: '{{count}}回残り · 毎日リセット',
|
|
307
|
+
lowUsageWarning: '無料使用回数は残り{{count}}回({{percent}}%)です。無制限利用にアップグレードできます。',
|
|
308
|
+
usageUnavailable: '使用制限はありません',
|
|
309
|
+
unlimitedUsageTooltip: 'すべての機能を無制限で使用できます',
|
|
310
|
+
betaUsageDescription: 'ベータ期間中は使用制限がありません',
|
|
311
|
+
perDay: '日',
|
|
312
|
+
resetsDaily: '毎日リセット',
|
|
313
|
+
donationPrompt: 'このプラグインは役に立ちましたか?',
|
|
314
|
+
betaStatus: 'BETA',
|
|
315
|
+
'donation.buy_me_a_coffee': 'コーヒーを一杯おごる',
|
|
316
|
+
'donation.prompt': '役に立ったらコーヒーを一杯おごってください!',
|
|
317
|
+
'form.license_key': 'ライセンスキー',
|
|
318
|
+
'form.license_placeholder': 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx',
|
|
319
|
+
'form.submit': '有効化',
|
|
320
|
+
'form.verifying': '確認中...',
|
|
321
|
+
'license.youtube_membership': 'YouTubeメンバーシップ',
|
|
322
|
+
'license.pro_account': 'Proアカウント',
|
|
323
|
+
'license.upgrade_to_pro': 'Proにアップグレード',
|
|
324
|
+
'license.all_features_available': 'すべての機能を使用できます。',
|
|
325
|
+
'license.youtube_membership_description': 'YouTubeメンバーシップに参加すると、Designbaseプラグインを無制限で使用できます。',
|
|
326
|
+
'license.purchase_for_unlimited': 'Designbaseのメンバーシップまたはライセンスで無制限に利用できます。',
|
|
327
|
+
'license.usage_unlimited': '使用回数の制限なし',
|
|
328
|
+
'license.usage_daily': '今日の残り {{remaining}}/{{limit}}回',
|
|
329
|
+
'license.usage_weekly': '今週の残り {{remaining}}/{{limit}}回',
|
|
330
|
+
'license.usage_monthly': '今月の残り {{remaining}}/{{limit}}回',
|
|
331
|
+
'license.usage_generic': '残り {{remaining}}/{{limit}}回',
|
|
332
|
+
'license.activated': 'ライセンス有効化済み',
|
|
333
|
+
'license.activations_remaining': '有効化',
|
|
334
|
+
'license.spots_remaining': '枠残り',
|
|
335
|
+
'license.deactivating': '無効化中...',
|
|
336
|
+
'license.deactivate': 'ライセンスを無効化',
|
|
337
|
+
'license.designbase_account': 'Designbaseアカウント',
|
|
338
|
+
'license.designbase_account_hint': 'WordPressアカウントでサインインするか、メンバーシップを購入するとプラグインが自動的に有効化されます。',
|
|
339
|
+
'license.authenticating': '認証中...',
|
|
340
|
+
'license.login_with_designbase': 'Designbaseでサインイン',
|
|
341
|
+
'license.purchase_membership': 'メンバーシップの詳細',
|
|
342
|
+
'license.manage_account': 'アカウント・メンバーシップを管理',
|
|
343
|
+
'license.enter_key': 'ライセンスキーを入力',
|
|
344
|
+
'license.key': 'ライセンスキー',
|
|
345
|
+
'license.submit': '有効化',
|
|
346
|
+
'license.verifying': '確認中...',
|
|
347
|
+
'license.enter_from_email': 'メールで受け取ったライセンスキーを入力してください。',
|
|
348
|
+
'license.enter_from_youtube': 'メンバー限定コミュニティのキーを入力してください。',
|
|
349
|
+
'license.help_link': 'ライセンス・支払いヘルプ',
|
|
350
|
+
'license.youtube_membership_howto': '利用方法',
|
|
351
|
+
'license.youtube_membership_step_1': 'Designbase YouTubeチャンネルのメンバーシップに参加します。',
|
|
352
|
+
'license.youtube_membership_step_2': 'メンバー限定コミュニティから今月のキーをコピーします。',
|
|
353
|
+
'license.youtube_membership_step_3': '下の入力欄にキーを貼り付けて有効化します。',
|
|
354
|
+
'license.youtube_membership_join': 'メンバーシップに参加',
|
|
355
|
+
'license.youtube_membership_guide': 'ガイドを見る',
|
|
356
|
+
'license.all_plugins': 'すべてのプラグイン',
|
|
357
|
+
'license.source_youtube_membership': 'YouTubeメンバーシップ',
|
|
358
|
+
'license.source_purchase': '購入ライセンス',
|
|
359
|
+
'license.status_active': '有効',
|
|
360
|
+
'license.status_inactive': 'ライセンス確認に失敗しました',
|
|
361
|
+
'license.status_label': 'ライセンス状態',
|
|
362
|
+
'license.key_label': 'ライセンスキー',
|
|
363
|
+
'license.hide_key': '隠す',
|
|
364
|
+
'license.reveal_key': '表示',
|
|
365
|
+
'license.source': '取得元',
|
|
366
|
+
'license.access': 'アクセス範囲',
|
|
367
|
+
'license.expires': '有効期限',
|
|
368
|
+
'license.last_checked': '最終確認',
|
|
369
|
+
'payment.license_key_label': 'ライセンスキー',
|
|
370
|
+
'payment.activations_remaining': '有効化',
|
|
371
|
+
'payment.spots_remaining': '枠残り',
|
|
372
|
+
'payment.deactivating': '無効化中...',
|
|
373
|
+
'payment.deactivate_license': 'ライセンスを無効化',
|
|
374
|
+
lifetimeAccess: '生涯利用',
|
|
375
|
+
perYear: '年',
|
|
376
|
+
perMonth: '月',
|
|
377
|
+
monthlyBilling: '月払い',
|
|
378
|
+
yearlyBilling: '年払い',
|
|
379
|
+
oneTimePaymentLifetime: '生涯利用のための一括払いです。',
|
|
380
|
+
feature: '機能',
|
|
381
|
+
freePrice: '$0',
|
|
382
|
+
pro: 'Pro',
|
|
383
|
+
recommended: 'おすすめ',
|
|
384
|
+
'pricing.usage_limit': '使用制限',
|
|
385
|
+
'pricing.daily_limit': '1日の制限',
|
|
386
|
+
'pricing.unlimited': '無制限',
|
|
387
|
+
'pricing.all_topics': 'すべてのトピック',
|
|
388
|
+
'pricing.max_selection': '選択上限',
|
|
389
|
+
'pricing.max_selection_free': '最大5個',
|
|
390
|
+
'pricing.max_selection_pro': '最大30個',
|
|
391
|
+
'pricing.dummy_count': 'ダミー数',
|
|
392
|
+
'pricing.dummy_count_free': '1,400個',
|
|
393
|
+
'pricing.dummy_count_pro': '4,400個以上',
|
|
394
|
+
'pricing.custom_number': 'カスタム数値',
|
|
395
|
+
'pricing.discount_suffix': '割引',
|
|
396
|
+
'settings.modal_title': 'リストを変更',
|
|
397
|
+
'settings.modal_description': 'ドラッグして並べ替えるか、トグルでカテゴリを非表示にできます。',
|
|
398
|
+
'settings.reset': 'リセット',
|
|
399
|
+
'settings.cancel': 'キャンセル',
|
|
400
|
+
'settings.save': '保存',
|
|
401
|
+
'progress.working': '処理中',
|
|
402
|
+
'progress.processing': '処理しています...',
|
|
403
|
+
'progress.completed': '完了しました',
|
|
404
|
+
'progress.stop': '停止',
|
|
405
|
+
'progress.confirm': '完了',
|
|
406
|
+
'banner.upgrade_title': 'Proにアップグレード',
|
|
407
|
+
'banner.upgrade_description': '無制限の機能とさらに多くの特典を利用できます。',
|
|
408
|
+
'banner.upgrade_button': '今すぐアップグレード',
|
|
409
|
+
loading: '読み込み中',
|
|
410
|
+
},
|
|
411
|
+
};
|
|
412
|
+
function normalizeFigmaUiLocale(locale) {
|
|
413
|
+
const normalized = (locale || '').toLowerCase().split(/[-_]/)[0];
|
|
414
|
+
if (normalized === 'en' || normalized === 'ja')
|
|
415
|
+
return normalized;
|
|
416
|
+
return 'ko';
|
|
417
|
+
}
|
|
418
|
+
function createFigmaUiTranslator(locale = 'ko', parentT) {
|
|
419
|
+
const normalizedLocale = normalizeFigmaUiLocale(locale);
|
|
420
|
+
const translations = FIGMA_UI_TRANSLATIONS[normalizedLocale];
|
|
421
|
+
return (key, values) => {
|
|
422
|
+
const parentValue = parentT?.(key, values);
|
|
423
|
+
if (parentValue && parentValue !== key) {
|
|
424
|
+
return interpolateText(parentValue, values);
|
|
425
|
+
}
|
|
426
|
+
const value = translations[key] ?? FIGMA_UI_TRANSLATIONS.ko[key] ?? key;
|
|
427
|
+
return interpolateText(value, values);
|
|
428
|
+
};
|
|
429
|
+
}
|
|
9
430
|
function interpolateText(template, values) {
|
|
10
431
|
if (!values)
|
|
11
432
|
return template;
|
|
@@ -32,11 +453,34 @@ function resolveText(t, input, fallback) {
|
|
|
32
453
|
return input.key;
|
|
33
454
|
}
|
|
34
455
|
|
|
456
|
+
const defaultContextValue = {
|
|
457
|
+
locale: 'ko',
|
|
458
|
+
t: createFigmaUiTranslator('ko'),
|
|
459
|
+
};
|
|
460
|
+
const FigmaUiI18nContext = createContext(defaultContextValue);
|
|
461
|
+
const FigmaUiI18nProvider = ({ locale = 'ko', t: parentT, children, }) => {
|
|
462
|
+
const normalizedLocale = normalizeFigmaUiLocale(locale);
|
|
463
|
+
const value = useMemo(() => ({
|
|
464
|
+
locale: normalizedLocale,
|
|
465
|
+
parentT,
|
|
466
|
+
t: createFigmaUiTranslator(normalizedLocale, parentT),
|
|
467
|
+
}), [normalizedLocale, parentT]);
|
|
468
|
+
return React.createElement(FigmaUiI18nContext.Provider, { value: value }, children);
|
|
469
|
+
};
|
|
470
|
+
function useFigmaUiI18n(localeOverride, tOverride) {
|
|
471
|
+
const context = useContext(FigmaUiI18nContext);
|
|
472
|
+
const locale = normalizeFigmaUiLocale(localeOverride ?? context.locale);
|
|
473
|
+
const parentT = tOverride ?? context.parentT;
|
|
474
|
+
const t = useMemo(() => createFigmaUiTranslator(locale, parentT), [locale, parentT]);
|
|
475
|
+
return { locale, parentT, t };
|
|
476
|
+
}
|
|
477
|
+
|
|
35
478
|
const DONATION_ICON_NAMES = {
|
|
36
479
|
heart: 'star-filled',
|
|
37
480
|
coffee: 'cart',
|
|
38
481
|
};
|
|
39
|
-
const DonationBadge = ({ donationUrl = 'https://buymeacoffee.com/designbase', text = { key: 'donation.buy_me_a_coffee' }, iconType = 'heart', size = 's', variant = 'tertiary', fullWidth = false, iconOnly = false, disabled = false, showPrompt = false, promptText = { key: 'donation.prompt' }, className, onClick, t }) => {
|
|
482
|
+
const DonationBadge = ({ donationUrl = 'https://buymeacoffee.com/designbase', text = { key: 'donation.buy_me_a_coffee' }, iconType = 'heart', size = 's', variant = 'tertiary', fullWidth = false, iconOnly = false, disabled = false, showPrompt = false, promptText = { key: 'donation.prompt' }, className, onClick, t, locale, }) => {
|
|
483
|
+
const { t: translate } = useFigmaUiI18n(locale, t);
|
|
40
484
|
const handleClick = () => {
|
|
41
485
|
if (onClick) {
|
|
42
486
|
onClick();
|
|
@@ -45,8 +489,8 @@ const DonationBadge = ({ donationUrl = 'https://buymeacoffee.com/designbase', te
|
|
|
45
489
|
window.open(donationUrl, '_blank', 'noopener,noreferrer');
|
|
46
490
|
}
|
|
47
491
|
};
|
|
48
|
-
const displayText = resolveText(
|
|
49
|
-
const displayPromptText = resolveText(
|
|
492
|
+
const displayText = resolveText(translate, text, 'Buy me a coffee');
|
|
493
|
+
const displayPromptText = resolveText(translate, promptText, '도움이 되었다면 커피 한 잔 사주세요!');
|
|
50
494
|
const buttonSize = size;
|
|
51
495
|
return (React.createElement("div", { className: clsx('designbase-figma-donation-badge-container', className) },
|
|
52
496
|
showPrompt && !iconOnly && (React.createElement("div", { className: "designbase-figma-donation-badge__prompt" }, displayPromptText)),
|
|
@@ -97,13 +541,14 @@ const FigmaHeader = ({ children, actions = [], searchBar, sticky = true, classNa
|
|
|
97
541
|
};
|
|
98
542
|
FigmaHeader.displayName = 'FigmaHeader';
|
|
99
543
|
|
|
100
|
-
const FigmaPageHead = ({ onBack, title, description, actions = [], moreActions = [], className, t }) => {
|
|
544
|
+
const FigmaPageHead = ({ onBack, title, description, actions = [], moreActions = [], className, t, locale, }) => {
|
|
545
|
+
const { t: translate } = useFigmaUiI18n(locale, t);
|
|
101
546
|
const classes = clsx('designbase-figma-page-head', className);
|
|
102
|
-
const displayTitle = resolveText(
|
|
103
|
-
const displayDescription = description ? resolveText(
|
|
547
|
+
const displayTitle = resolveText(translate, title);
|
|
548
|
+
const displayDescription = description ? resolveText(translate, description) : undefined;
|
|
104
549
|
const dropdownItems = moreActions.map((action) => ({
|
|
105
550
|
id: action.id,
|
|
106
|
-
label: resolveText(
|
|
551
|
+
label: resolveText(translate, action.label),
|
|
107
552
|
}));
|
|
108
553
|
const handleDropdownChange = (event) => {
|
|
109
554
|
const actionId = event.detail?.id ?? event.detail?.value;
|
|
@@ -112,7 +557,7 @@ const FigmaPageHead = ({ onBack, title, description, actions = [], moreActions =
|
|
|
112
557
|
};
|
|
113
558
|
return (React.createElement("div", { className: classes },
|
|
114
559
|
React.createElement("div", { className: "designbase-figma-page-head__left" },
|
|
115
|
-
React.createElement(Button, { variant: "tertiary", size: "s", iconOnly: true, startIcon: "chevron-left", onPress: onBack, "aria-label":
|
|
560
|
+
React.createElement(Button, { variant: "tertiary", size: "s", iconOnly: true, startIcon: "chevron-left", onPress: onBack, "aria-label": translate('back') }),
|
|
116
561
|
React.createElement("div", { className: "designbase-figma-page-head__titles" },
|
|
117
562
|
React.createElement("h2", null, displayTitle),
|
|
118
563
|
displayDescription && React.createElement("p", null, displayDescription))),
|
|
@@ -122,7 +567,8 @@ const FigmaPageHead = ({ onBack, title, description, actions = [], moreActions =
|
|
|
122
567
|
};
|
|
123
568
|
FigmaPageHead.displayName = 'FigmaPageHead';
|
|
124
569
|
|
|
125
|
-
const FigmaSection = ({ title, dataCategory, iconButton, children, marginBottom, isEnabled = true, onToggle, className, enableScrollNavigation = false, headerHeight = 94, onActiveSectionChange, t, collapsible = false, defaultCollapsed = false, onCollapseChange, badge, overflowVisible = false, }) => {
|
|
570
|
+
const FigmaSection = ({ title, dataCategory, iconButton, children, marginBottom, isEnabled = true, onToggle, className, enableScrollNavigation = false, headerHeight = 94, onActiveSectionChange, t, locale, collapsible = false, defaultCollapsed = false, onCollapseChange, badge, overflowVisible = false, }) => {
|
|
571
|
+
const { t: translate } = useFigmaUiI18n(locale, t);
|
|
126
572
|
const sectionRef = useRef(null);
|
|
127
573
|
const clipRef = useRef(null);
|
|
128
574
|
const innerRef = useRef(null);
|
|
@@ -242,7 +688,7 @@ const FigmaSection = ({ title, dataCategory, iconButton, children, marginBottom,
|
|
|
242
688
|
React.createElement("div", { className: "designbase-figma-section__title-area" },
|
|
243
689
|
React.createElement("h2", { className: "designbase-figma-section__title" },
|
|
244
690
|
typeof title === 'string' || (typeof title === 'object' && 'key' in title)
|
|
245
|
-
? resolveText(
|
|
691
|
+
? resolveText(translate, title)
|
|
246
692
|
: title,
|
|
247
693
|
badge && (React.createElement(Badge, { variant: "secondary", size: "s" }, badge)))),
|
|
248
694
|
React.createElement("div", { className: "designbase-figma-section__controls", onClick: (e) => e.stopPropagation() },
|
|
@@ -395,54 +841,30 @@ const FigmaSidebar = ({ categories, categoryGroups = {}, categoryLabels = {}, ac
|
|
|
395
841
|
};
|
|
396
842
|
FigmaSidebar.displayName = 'FigmaSidebar';
|
|
397
843
|
|
|
398
|
-
let useTranslation$2 = null;
|
|
399
|
-
try {
|
|
400
|
-
const reactI18next = require('react-i18next');
|
|
401
|
-
useTranslation$2 = reactI18next.useTranslation;
|
|
402
|
-
}
|
|
403
|
-
catch (e) {
|
|
404
|
-
}
|
|
405
844
|
const DEFAULT_LANGUAGES$1 = [
|
|
406
|
-
{ code: 'ko', label: '
|
|
407
|
-
{ code: 'en', label: '
|
|
408
|
-
{ code: 'ja', label: '
|
|
845
|
+
{ code: 'ko', label: { key: 'language.korean' } },
|
|
846
|
+
{ code: 'en', label: { key: 'language.english' } },
|
|
847
|
+
{ code: 'ja', label: { key: 'language.japanese' } },
|
|
409
848
|
];
|
|
410
|
-
const
|
|
411
|
-
const
|
|
412
|
-
|
|
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;
|
|
849
|
+
const LanguageSettingsModal = ({ isOpen, onClose, languages = DEFAULT_LANGUAGES$1, currentLanguage, onLanguageChange, title, description, className, t, }) => {
|
|
850
|
+
const { t: translate } = useFigmaUiI18n(currentLanguage, t);
|
|
851
|
+
const resolvedCurrentLanguage = currentLanguage || languages[0]?.code;
|
|
423
852
|
const [pendingLanguage, setPendingLanguage] = useState(resolvedCurrentLanguage);
|
|
424
853
|
useEffect(() => {
|
|
425
854
|
if (isOpen) {
|
|
426
855
|
setPendingLanguage(resolvedCurrentLanguage);
|
|
427
856
|
}
|
|
428
857
|
}, [isOpen, resolvedCurrentLanguage]);
|
|
429
|
-
const translate = (key, fallback) => {
|
|
430
|
-
const translated = t(key);
|
|
431
|
-
return translated && translated !== key ? translated : fallback;
|
|
432
|
-
};
|
|
433
858
|
const handleSelect = useCallback((languageCode) => {
|
|
434
859
|
setPendingLanguage(languageCode);
|
|
435
|
-
if (i18n) {
|
|
436
|
-
i18n.changeLanguage(languageCode);
|
|
437
|
-
}
|
|
438
860
|
onLanguageChange?.(languageCode);
|
|
439
861
|
onClose();
|
|
440
|
-
}, [
|
|
441
|
-
return (React.createElement(Modal, { isOpen: isOpen, onClose: onClose, title: title || translate('languageSettings'
|
|
862
|
+
}, [onClose, onLanguageChange]);
|
|
863
|
+
return (React.createElement(Modal, { isOpen: isOpen, onClose: onClose, title: title || translate('languageSettings'), size: "s", className: clsx('designbase-figma-language-settings-modal', className) },
|
|
442
864
|
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'
|
|
445
|
-
const label = resolveText(
|
|
865
|
+
React.createElement("p", { className: "designbase-figma-language-settings-modal__description" }, description || translate('languageSettingsDescription')),
|
|
866
|
+
React.createElement("ul", { className: "designbase-figma-language-settings-modal__list", role: "listbox", "aria-label": translate('languageSettings') }, languages.map((language) => {
|
|
867
|
+
const label = resolveText(translate, language.label, language.code.toUpperCase());
|
|
446
868
|
const isActive = language.code === pendingLanguage;
|
|
447
869
|
return (React.createElement("li", { key: language.code, role: "presentation" },
|
|
448
870
|
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) },
|
|
@@ -452,13 +874,6 @@ const LanguageSettingsModal = ({ isOpen, onClose, languages = DEFAULT_LANGUAGES$
|
|
|
452
874
|
};
|
|
453
875
|
LanguageSettingsModal.displayName = 'LanguageSettingsModal';
|
|
454
876
|
|
|
455
|
-
let useTranslation$1 = null;
|
|
456
|
-
try {
|
|
457
|
-
const reactI18next = require('react-i18next');
|
|
458
|
-
useTranslation$1 = reactI18next.useTranslation;
|
|
459
|
-
}
|
|
460
|
-
catch (e) {
|
|
461
|
-
}
|
|
462
877
|
const defaultLinks = [
|
|
463
878
|
{
|
|
464
879
|
name: 'officialWebsite',
|
|
@@ -486,33 +901,20 @@ const defaultLinks = [
|
|
|
486
901
|
icon: React.createElement(MailIcon, { size: 20 }),
|
|
487
902
|
},
|
|
488
903
|
];
|
|
489
|
-
const defaultTranslations$1 = {
|
|
490
|
-
officialWebsite: '공식 웹사이트',
|
|
491
|
-
youtube: '유튜브',
|
|
492
|
-
instagram: '인스타그램',
|
|
493
|
-
figmaCommunity: '피그마 커뮤니티',
|
|
494
|
-
contact: '문의하기',
|
|
495
|
-
languageSettings: '언어 설정',
|
|
496
|
-
};
|
|
497
|
-
const defaultT$1 = (key) => defaultTranslations$1[key] || key;
|
|
498
904
|
const defaultLanguages = [
|
|
499
|
-
{ code: 'ko', label: '
|
|
500
|
-
{ code: 'en', label: '
|
|
501
|
-
{ code: 'ja', label: '
|
|
905
|
+
{ code: 'ko', label: { key: 'language.korean' } },
|
|
906
|
+
{ code: 'en', label: { key: 'language.english' } },
|
|
907
|
+
{ code: 'ja', label: { key: 'language.japanese' } },
|
|
502
908
|
];
|
|
503
|
-
const LogoDropdown = ({ logoSrc, logoAlt = 'DesignBase', logoType = 'designbase', logoSize = 'xs', links = defaultLinks, position = 'top-left', className, t
|
|
909
|
+
const LogoDropdown = ({ logoSrc, logoAlt = 'DesignBase', logoType = 'designbase', logoSize = 'xs', links = defaultLinks, position = 'top-left', className, t, showLanguageSelector = true, languages = defaultLanguages, currentLanguage, onLanguageChange, }) => {
|
|
504
910
|
const [isOpen, setIsOpen] = useState(false);
|
|
505
911
|
const [isLanguageModalOpen, setIsLanguageModalOpen] = useState(false);
|
|
506
912
|
const [internalLanguage, setInternalLanguage] = useState();
|
|
507
|
-
const
|
|
913
|
+
const { t: translate } = useFigmaUiI18n(currentLanguage ?? internalLanguage, t);
|
|
508
914
|
const rootRef = useRef(null);
|
|
509
915
|
const triggerRef = useRef(null);
|
|
510
916
|
const menuId = `designbase-figma-logo-menu-${useId().replace(/:/g, '')}`;
|
|
511
|
-
const activeLanguage = currentLanguage ?? internalLanguage ??
|
|
512
|
-
const translate = (key, fallback) => {
|
|
513
|
-
const translated = t(key);
|
|
514
|
-
return translated && translated !== key ? translated : fallback;
|
|
515
|
-
};
|
|
917
|
+
const activeLanguage = currentLanguage ?? internalLanguage ?? languages[0]?.code;
|
|
516
918
|
useEffect(() => {
|
|
517
919
|
if (!isOpen) {
|
|
518
920
|
return undefined;
|
|
@@ -551,9 +953,6 @@ const LogoDropdown = ({ logoSrc, logoAlt = 'DesignBase', logoType = 'designbase'
|
|
|
551
953
|
if (currentLanguage === undefined) {
|
|
552
954
|
setInternalLanguage(languageCode);
|
|
553
955
|
}
|
|
554
|
-
if (i18n) {
|
|
555
|
-
i18n.changeLanguage(languageCode);
|
|
556
|
-
}
|
|
557
956
|
onLanguageChange?.(languageCode);
|
|
558
957
|
};
|
|
559
958
|
const classes = clsx('designbase-figma-logo-dropdown', `designbase-figma-logo-dropdown--${position}`, {
|
|
@@ -568,14 +967,14 @@ const LogoDropdown = ({ logoSrc, logoAlt = 'DesignBase', logoType = 'designbase'
|
|
|
568
967
|
links.map((link, index) => (React.createElement("li", { key: index, className: "designbase-figma-logo-dropdown__item", role: "none" },
|
|
569
968
|
React.createElement("button", { type: "button", className: "designbase-figma-logo-dropdown__link", role: "menuitem", onClick: () => handleLinkClick(link.url) },
|
|
570
969
|
link.icon && (React.createElement("span", { className: "designbase-figma-logo-dropdown__icon" }, link.icon)),
|
|
571
|
-
React.createElement("span", { className: "designbase-figma-logo-dropdown__text" },
|
|
970
|
+
React.createElement("span", { className: "designbase-figma-logo-dropdown__text" }, translate(link.name)),
|
|
572
971
|
React.createElement(ExternalLinkIcon, { size: 16, className: "designbase-figma-logo-dropdown__arrow" }))))),
|
|
573
972
|
showLanguageSelector && (React.createElement("li", { className: "designbase-figma-logo-dropdown__item designbase-figma-logo-dropdown__language-item", role: "none" },
|
|
574
973
|
React.createElement("button", { type: "button", className: "designbase-figma-logo-dropdown__link", role: "menuitem", onClick: handleLanguageSettingsClick },
|
|
575
974
|
React.createElement("span", { className: "designbase-figma-logo-dropdown__icon" },
|
|
576
975
|
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:
|
|
976
|
+
React.createElement("span", { className: "designbase-figma-logo-dropdown__text" }, translate('languageSettings'))))))),
|
|
977
|
+
showLanguageSelector && (React.createElement(LanguageSettingsModal, { isOpen: isLanguageModalOpen, onClose: () => setIsLanguageModalOpen(false), languages: languages, currentLanguage: activeLanguage, onLanguageChange: handleLanguageChange, t: translate }))));
|
|
579
978
|
};
|
|
580
979
|
LogoDropdown.displayName = 'LogoDropdown';
|
|
581
980
|
|
|
@@ -583,7 +982,8 @@ const fallbackText = (t, key, fallback) => {
|
|
|
583
982
|
const translated = t?.(key);
|
|
584
983
|
return translated && translated !== key ? translated : fallback;
|
|
585
984
|
};
|
|
586
|
-
const UsageStatusPopover = ({ paymentStatus, usageCount = 0, maxDailyUsage = 20, showUsage = true, showPlan = true, isLoading = false, onLicensePageClick, t, className, }) => {
|
|
985
|
+
const UsageStatusPopover = ({ paymentStatus, usageCount = 0, remainingUsage, maxDailyUsage = 20, showUsage = true, showPlan = true, isLoading = false, onLicensePageClick, t, locale, className, }) => {
|
|
986
|
+
const { t: translate } = useFigmaUiI18n(locale, t);
|
|
587
987
|
const [isOpen, setIsOpen] = useState(false);
|
|
588
988
|
const rootRef = useRef(null);
|
|
589
989
|
const triggerRef = useRef(null);
|
|
@@ -592,30 +992,32 @@ const UsageStatusPopover = ({ paymentStatus, usageCount = 0, maxDailyUsage = 20,
|
|
|
592
992
|
const isBeta = paymentStatus === 'BETA';
|
|
593
993
|
const hasDailyLimit = !isPaid && maxDailyUsage > 0;
|
|
594
994
|
const limit = Math.max(1, Math.round(maxDailyUsage));
|
|
595
|
-
const
|
|
596
|
-
const remaining =
|
|
597
|
-
|
|
995
|
+
const hasRemainingUsage = typeof remainingUsage === 'number' && Number.isFinite(remainingUsage);
|
|
996
|
+
const remaining = hasRemainingUsage
|
|
997
|
+
? Math.min(limit, Math.max(0, Math.round(remainingUsage)))
|
|
998
|
+
: Math.max(0, limit - Math.min(limit, Math.max(0, Math.round(usageCount))));
|
|
999
|
+
const remainingPercent = hasDailyLimit ? Math.round((remaining / limit) * 100) : 0;
|
|
598
1000
|
const hasControl = showUsage || showPlan;
|
|
599
1001
|
const planLabel = isPaid
|
|
600
|
-
? fallbackText(
|
|
1002
|
+
? fallbackText(translate, 'proUser', '프로')
|
|
601
1003
|
: isBeta
|
|
602
|
-
? fallbackText(
|
|
1004
|
+
? fallbackText(translate, 'betaStatus', 'BETA')
|
|
603
1005
|
: paymentStatus === 'TRIAL'
|
|
604
|
-
? fallbackText(
|
|
605
|
-
: fallbackText(
|
|
1006
|
+
? fallbackText(translate, 'trialPlan', '체험판')
|
|
1007
|
+
: fallbackText(translate, 'free', '무료');
|
|
606
1008
|
const planTitle = isBeta
|
|
607
1009
|
? planLabel
|
|
608
|
-
: `${planLabel} ${fallbackText(
|
|
1010
|
+
: `${planLabel} ${fallbackText(translate, 'planSuffix', '플랜')}`;
|
|
609
1011
|
const triggerLabel = isPaid
|
|
610
|
-
? `${planLabel}, ${fallbackText(
|
|
1012
|
+
? `${planLabel}, ${fallbackText(translate, 'unlimitedUsage', '무제한 사용 가능')}`
|
|
611
1013
|
: isBeta
|
|
612
1014
|
? planLabel
|
|
613
1015
|
: hasDailyLimit
|
|
614
|
-
? `${remaining}/${limit} ${fallbackText(
|
|
1016
|
+
? `${remaining}/${limit} ${fallbackText(translate, 'remainingShort', '남음')}`
|
|
615
1017
|
: planLabel;
|
|
616
1018
|
const upgradeLabel = isPaid
|
|
617
|
-
? fallbackText(
|
|
618
|
-
: fallbackText(
|
|
1019
|
+
? fallbackText(translate, 'managePlan', '플랜 관리')
|
|
1020
|
+
: fallbackText(translate, 'upgradePlan', '플랜 업그레이드');
|
|
619
1021
|
useEffect(() => {
|
|
620
1022
|
if (!isOpen)
|
|
621
1023
|
return undefined;
|
|
@@ -650,80 +1052,37 @@ const UsageStatusPopover = ({ paymentStatus, usageCount = 0, maxDailyUsage = 20,
|
|
|
650
1052
|
'designbase-figma-usage-status--loading': isLoading,
|
|
651
1053
|
}, className) },
|
|
652
1054
|
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(
|
|
1055
|
+
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("span", { className: "designbase-figma-usage-status__unlimited-mark", "aria-hidden": "true" }, "\u221E")) : hasDailyLimit ? (React.createElement(React.Fragment, null,
|
|
654
1056
|
React.createElement("span", { className: "designbase-figma-usage-status__mini-progress", "aria-hidden": "true" },
|
|
655
|
-
React.createElement("span", { style: { width: `${
|
|
1057
|
+
React.createElement("span", { style: { width: `${remainingPercent}%` } })),
|
|
656
1058
|
React.createElement("span", { className: "designbase-figma-usage-status__usage-value" },
|
|
657
1059
|
React.createElement("strong", null, remaining),
|
|
658
1060
|
React.createElement("span", null,
|
|
659
1061
|
"/",
|
|
660
1062
|
limit)))) : (React.createElement("span", { className: "designbase-figma-usage-status__plan-label" }, planLabel)))),
|
|
661
1063
|
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(
|
|
1064
|
+
isOpen && (React.createElement("div", { id: popoverId, className: "designbase-figma-usage-status__popover", role: "dialog", "aria-label": fallbackText(translate, 'usageDetails', '사용량 상세') },
|
|
663
1065
|
React.createElement("div", { className: "designbase-figma-usage-status__popover-header" },
|
|
664
1066
|
React.createElement("span", { className: "designbase-figma-usage-status__plan-title" }, planTitle),
|
|
665
1067
|
!isBeta && (React.createElement(Button, { className: "designbase-figma-usage-status__upgrade", variant: isPaid ? 'secondary' : 'primary', size: "s", endIcon: "chevron-right", onPress: handleManageClick }, upgradeLabel))),
|
|
666
1068
|
isPaid ? (React.createElement("div", { className: "designbase-figma-usage-status__unlimited-copy" },
|
|
667
1069
|
React.createElement("span", { className: "designbase-figma-usage-status__unlimited-mark", "aria-hidden": "true" }, "\u221E"),
|
|
668
|
-
React.createElement("span", null, fallbackText(
|
|
1070
|
+
React.createElement("span", null, fallbackText(translate, 'unlimitedUsageTooltip', '모든 기능을 제한 없이 사용할 수 있습니다')))) : isBeta ? (React.createElement("p", { className: "designbase-figma-usage-status__note" }, fallbackText(translate, 'betaUsageDescription', '베타 기간에는 사용량 제한이 적용되지 않습니다'))) : hasDailyLimit ? (React.createElement("div", { className: "designbase-figma-usage-status__usage-detail" },
|
|
669
1071
|
React.createElement("div", { className: "designbase-figma-usage-status__usage-summary" },
|
|
670
1072
|
React.createElement("strong", { className: "designbase-figma-usage-status__usage-count" },
|
|
671
|
-
|
|
1073
|
+
remaining,
|
|
672
1074
|
React.createElement("span", { className: "designbase-figma-usage-status__usage-divider" }, "/"),
|
|
673
1075
|
limit),
|
|
674
|
-
React.createElement("span", { className: "designbase-figma-usage-status__usage-caption" }, fallbackText(
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
1076
|
+
React.createElement("span", { className: "designbase-figma-usage-status__usage-caption" }, fallbackText(translate, 'remainingToday', '오늘 남은 사용량')),
|
|
1077
|
+
React.createElement("strong", { className: "designbase-figma-usage-status__usage-percent" },
|
|
1078
|
+
remainingPercent,
|
|
1079
|
+
"%")),
|
|
1080
|
+
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}%` },
|
|
1081
|
+
React.createElement("span", { style: { width: `${remainingPercent}%` } })),
|
|
1082
|
+
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', '사용량 제한이 없습니다')))))));
|
|
678
1083
|
};
|
|
679
1084
|
UsageStatusPopover.displayName = 'UsageStatusPopover';
|
|
680
1085
|
|
|
681
|
-
const defaultTranslations = {
|
|
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: '베타 기간에는 사용량 제한이 적용되지 않습니다',
|
|
699
|
-
perDay: '일',
|
|
700
|
-
resetsDaily: '매일 리셋',
|
|
701
|
-
donationPrompt: '이 플러그인이 유용했나요?',
|
|
702
|
-
betaStatus: 'BETA',
|
|
703
|
-
};
|
|
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;
|
|
710
|
-
const resolvedShowPaymentBadge = showPaymentStatus && showPaymentBadge;
|
|
711
|
-
const resolvedLicensePageClick = paymentStatus === 'BETA' ? undefined : onLicensePageClick;
|
|
712
|
-
const hasChildren = React.Children.count(children) > 0;
|
|
713
|
-
const classes = clsx('designbase-figma-footer', {
|
|
714
|
-
'designbase-figma-footer--has-children': hasChildren,
|
|
715
|
-
}, className);
|
|
716
|
-
return (React.createElement("footer", { className: classes },
|
|
717
|
-
React.createElement("div", { className: "designbase-figma-footer__wrap" },
|
|
718
|
-
React.createElement("div", { className: "designbase-figma-footer__left" },
|
|
719
|
-
React.createElement(LogoDropdown, { logoSrc: logoSrc, logoAlt: logoAlt, logoType: logoType, logoSize: logoSize, links: logoLinks, languages: languages, currentLanguage: currentLanguage, showLanguageSelector: showLanguageSelector, onLanguageChange: onLanguageChange, t: t })),
|
|
720
|
-
React.createElement("div", { className: "designbase-figma-footer__right" },
|
|
721
|
-
(resolvedShowUsageInfo || resolvedShowPaymentBadge) && (React.createElement(UsageStatusPopover, { paymentStatus: paymentStatus, usageCount: usageCount, maxDailyUsage: maxDailyUsage, showUsage: resolvedShowUsageInfo, showPlan: resolvedShowPaymentBadge, isLoading: isLoading, onLicensePageClick: resolvedLicensePageClick, t: t })),
|
|
722
|
-
showDonation && (React.createElement(DonationBadge, { donationUrl: donationUrl, text: donationText, iconType: "heart", size: "s", iconOnly: true, showPrompt: false, t: t })))),
|
|
723
|
-
children));
|
|
724
|
-
};
|
|
725
|
-
FigmaFooter.displayName = 'FigmaFooter';
|
|
726
|
-
|
|
727
1086
|
const FigmaToastStack = ({ toasts, position, maxToasts, onRemoveToast, }) => {
|
|
728
1087
|
const visibleToasts = [...toasts]
|
|
729
1088
|
.sort((a, b) => b.createdAt - a.createdAt)
|
|
@@ -784,18 +1143,152 @@ const FigmaToast = ({ message, isVisible, onClose, type = 'info', duration = 300
|
|
|
784
1143
|
React.createElement(Toast, { toastId: "figma-toast-simple", title: message, status: type, duration: duration, showProgress: false, showClose: true, onClose: onClose })));
|
|
785
1144
|
};
|
|
786
1145
|
const useFigmaToast = () => {
|
|
787
|
-
const [toast, setToast] = useState({
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
1146
|
+
const [toast, setToast] = useState({
|
|
1147
|
+
message: '',
|
|
1148
|
+
isVisible: false,
|
|
1149
|
+
type: 'info',
|
|
1150
|
+
});
|
|
1151
|
+
const timeoutRef = useRef(null);
|
|
1152
|
+
const showToast = useCallback((message, type = 'info') => {
|
|
1153
|
+
if (timeoutRef.current)
|
|
1154
|
+
clearTimeout(timeoutRef.current);
|
|
1155
|
+
setToast({ message, isVisible: true, type });
|
|
1156
|
+
timeoutRef.current = setTimeout(() => {
|
|
1157
|
+
setToast({ message: '', isVisible: false, type: 'info' });
|
|
1158
|
+
timeoutRef.current = null;
|
|
1159
|
+
}, 5000);
|
|
1160
|
+
}, []);
|
|
1161
|
+
useEffect(() => () => {
|
|
1162
|
+
if (timeoutRef.current)
|
|
1163
|
+
clearTimeout(timeoutRef.current);
|
|
791
1164
|
}, []);
|
|
792
|
-
const toastComponent = toast.isVisible ? (React.createElement(FigmaToast, { message: toast.message, isVisible: toast.isVisible, onClose: () => setToast({ message: '', isVisible: false }), type:
|
|
1165
|
+
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;
|
|
793
1166
|
return { showToast, toastComponent };
|
|
794
1167
|
};
|
|
795
1168
|
FigmaToastProvider.displayName = 'FigmaToastProvider';
|
|
796
1169
|
FigmaToast.displayName = 'FigmaToast';
|
|
797
1170
|
|
|
798
|
-
const
|
|
1171
|
+
const DEFAULT_LOW_USAGE_WARNING = { key: 'lowUsageWarning' };
|
|
1172
|
+
const FigmaFooter = ({ onLicensePageClick, paymentStatus = 'FREE', usageCount = 0, remainingUsage, listenForUsageMessages = true, isLoading = false, showPaymentStatus = true, showUsageInfo = true, maxDailyUsage = 20, 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, }) => {
|
|
1173
|
+
const { t: translate } = useFigmaUiI18n(currentLanguage, t);
|
|
1174
|
+
const { showToast, toastComponent } = useFigmaToast();
|
|
1175
|
+
const [liveUsage, setLiveUsage] = useState({});
|
|
1176
|
+
const previousRemainingRef = useRef(null);
|
|
1177
|
+
const hasWarnedLowUsageRef = useRef(false);
|
|
1178
|
+
const effectivePaymentStatus = liveUsage.paymentStatus ?? paymentStatus;
|
|
1179
|
+
const effectiveUsageCount = liveUsage.usageCount ?? usageCount;
|
|
1180
|
+
const effectiveRemainingUsage = liveUsage.remainingUsage ?? remainingUsage;
|
|
1181
|
+
const effectiveMaxDailyUsage = liveUsage.maxDailyUsage ?? maxDailyUsage;
|
|
1182
|
+
const limit = Math.max(1, Math.round(effectiveMaxDailyUsage));
|
|
1183
|
+
const resolvedRemaining = typeof effectiveRemainingUsage === 'number' && Number.isFinite(effectiveRemainingUsage)
|
|
1184
|
+
? Math.min(limit, Math.max(0, Math.round(effectiveRemainingUsage)))
|
|
1185
|
+
: Math.max(0, limit - Math.min(limit, Math.max(0, Math.round(effectiveUsageCount))));
|
|
1186
|
+
const remainingPercent = Math.round((resolvedRemaining / limit) * 100);
|
|
1187
|
+
const resolvedShowUsageInfo = showPaymentStatus && (showUsageInfo || effectivePaymentStatus === 'FREE');
|
|
1188
|
+
const resolvedShowPaymentBadge = showPaymentStatus && showPaymentBadge;
|
|
1189
|
+
const resolvedLicensePageClick = effectivePaymentStatus === 'BETA' ? undefined : onLicensePageClick;
|
|
1190
|
+
const hasChildren = React.Children.count(children) > 0;
|
|
1191
|
+
useEffect(() => {
|
|
1192
|
+
if (!listenForUsageMessages || typeof window === 'undefined')
|
|
1193
|
+
return undefined;
|
|
1194
|
+
const handleUsageMessage = (event) => {
|
|
1195
|
+
const message = event.data?.pluginMessage;
|
|
1196
|
+
if (!message || !['initialize', 'update-plugin-status', 'usage-consume-result'].includes(message.type)) {
|
|
1197
|
+
return;
|
|
1198
|
+
}
|
|
1199
|
+
const nextLimit = [message.usageLimit, message.freeDailyLimit, message.maxDailyUsage]
|
|
1200
|
+
.find((value) => typeof value === 'number' && Number.isFinite(value) && value > 0);
|
|
1201
|
+
const nextUsed = [message.usageUsed, message.dailyUsage]
|
|
1202
|
+
.find((value) => typeof value === 'number' && Number.isFinite(value) && value >= 0);
|
|
1203
|
+
const nextRemaining = typeof message.remainingUsage === 'number' && Number.isFinite(message.remainingUsage)
|
|
1204
|
+
? message.remainingUsage
|
|
1205
|
+
: typeof nextLimit === 'number' && typeof nextUsed === 'number'
|
|
1206
|
+
? Math.max(0, nextLimit - nextUsed)
|
|
1207
|
+
: undefined;
|
|
1208
|
+
const nextPaymentStatus = message.paymentStatus === 'PAID'
|
|
1209
|
+
? 'PAID'
|
|
1210
|
+
: message.paymentStatus === 'FREE' || message.paymentStatus === 'UNPAID'
|
|
1211
|
+
? 'FREE'
|
|
1212
|
+
: undefined;
|
|
1213
|
+
if (nextPaymentStatus === undefined
|
|
1214
|
+
&& nextLimit === undefined
|
|
1215
|
+
&& nextUsed === undefined
|
|
1216
|
+
&& nextRemaining === undefined) {
|
|
1217
|
+
return;
|
|
1218
|
+
}
|
|
1219
|
+
setLiveUsage((previous) => ({
|
|
1220
|
+
...previous,
|
|
1221
|
+
...(nextPaymentStatus ? { paymentStatus: nextPaymentStatus } : {}),
|
|
1222
|
+
...(typeof nextLimit === 'number' ? { maxDailyUsage: nextLimit } : {}),
|
|
1223
|
+
...(typeof nextUsed === 'number' ? { usageCount: nextUsed } : {}),
|
|
1224
|
+
...(typeof nextRemaining === 'number' ? { remainingUsage: nextRemaining } : {}),
|
|
1225
|
+
}));
|
|
1226
|
+
};
|
|
1227
|
+
window.addEventListener('message', handleUsageMessage);
|
|
1228
|
+
return () => window.removeEventListener('message', handleUsageMessage);
|
|
1229
|
+
}, [listenForUsageMessages]);
|
|
1230
|
+
useEffect(() => {
|
|
1231
|
+
const previousRemaining = previousRemainingRef.current;
|
|
1232
|
+
const shouldTrack = showLowUsageWarning && effectivePaymentStatus === 'FREE' && effectiveMaxDailyUsage > 0;
|
|
1233
|
+
if (!shouldTrack) {
|
|
1234
|
+
previousRemainingRef.current = resolvedRemaining;
|
|
1235
|
+
hasWarnedLowUsageRef.current = false;
|
|
1236
|
+
return;
|
|
1237
|
+
}
|
|
1238
|
+
if (remainingPercent >= lowUsageThresholdPercent) {
|
|
1239
|
+
hasWarnedLowUsageRef.current = false;
|
|
1240
|
+
}
|
|
1241
|
+
else if (previousRemaining !== null
|
|
1242
|
+
&& resolvedRemaining < previousRemaining
|
|
1243
|
+
&& !hasWarnedLowUsageRef.current) {
|
|
1244
|
+
hasWarnedLowUsageRef.current = true;
|
|
1245
|
+
const warningText = typeof lowUsageWarningText === 'string'
|
|
1246
|
+
? lowUsageWarningText
|
|
1247
|
+
: {
|
|
1248
|
+
...lowUsageWarningText,
|
|
1249
|
+
values: {
|
|
1250
|
+
...(lowUsageWarningText.values || {}),
|
|
1251
|
+
count: resolvedRemaining,
|
|
1252
|
+
percent: remainingPercent,
|
|
1253
|
+
},
|
|
1254
|
+
};
|
|
1255
|
+
onLowUsageWarning?.({
|
|
1256
|
+
remaining: resolvedRemaining,
|
|
1257
|
+
percent: remainingPercent,
|
|
1258
|
+
limit,
|
|
1259
|
+
});
|
|
1260
|
+
showToast(resolveText(translate, warningText, `무료 사용량이 ${resolvedRemaining}회(${remainingPercent}%) 남았습니다.`) || `무료 사용량이 ${resolvedRemaining}회(${remainingPercent}%) 남았습니다.`, 'warning');
|
|
1261
|
+
}
|
|
1262
|
+
previousRemainingRef.current = resolvedRemaining;
|
|
1263
|
+
}, [
|
|
1264
|
+
lowUsageThresholdPercent,
|
|
1265
|
+
lowUsageWarningText,
|
|
1266
|
+
effectiveMaxDailyUsage,
|
|
1267
|
+
effectivePaymentStatus,
|
|
1268
|
+
onLowUsageWarning,
|
|
1269
|
+
remainingPercent,
|
|
1270
|
+
resolvedRemaining,
|
|
1271
|
+
showLowUsageWarning,
|
|
1272
|
+
showToast,
|
|
1273
|
+
translate,
|
|
1274
|
+
]);
|
|
1275
|
+
const classes = clsx('designbase-figma-footer', {
|
|
1276
|
+
'designbase-figma-footer--has-children': hasChildren,
|
|
1277
|
+
}, className);
|
|
1278
|
+
return (React.createElement("footer", { className: classes },
|
|
1279
|
+
React.createElement("div", { className: "designbase-figma-footer__wrap" },
|
|
1280
|
+
React.createElement("div", { className: "designbase-figma-footer__left" },
|
|
1281
|
+
React.createElement(LogoDropdown, { logoSrc: logoSrc, logoAlt: logoAlt, logoType: logoType, logoSize: logoSize, links: logoLinks, languages: languages, currentLanguage: currentLanguage, showLanguageSelector: showLanguageSelector, onLanguageChange: onLanguageChange, t: translate })),
|
|
1282
|
+
React.createElement("div", { className: "designbase-figma-footer__right" },
|
|
1283
|
+
(resolvedShowUsageInfo || resolvedShowPaymentBadge) && (React.createElement(UsageStatusPopover, { paymentStatus: effectivePaymentStatus, usageCount: effectiveUsageCount, remainingUsage: effectiveRemainingUsage, maxDailyUsage: effectiveMaxDailyUsage, showUsage: resolvedShowUsageInfo, showPlan: resolvedShowPaymentBadge, isLoading: isLoading, onLicensePageClick: resolvedLicensePageClick, t: translate })),
|
|
1284
|
+
showDonation && (React.createElement(DonationBadge, { donationUrl: donationUrl, text: donationText, iconType: "heart", size: "s", iconOnly: true, showPrompt: false, t: translate })))),
|
|
1285
|
+
children,
|
|
1286
|
+
toastComponent));
|
|
1287
|
+
};
|
|
1288
|
+
FigmaFooter.displayName = 'FigmaFooter';
|
|
1289
|
+
|
|
1290
|
+
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, }) => {
|
|
1291
|
+
const { t: translate } = useFigmaUiI18n(locale, t);
|
|
799
1292
|
const [inputValue, setInputValue] = useState(value);
|
|
800
1293
|
useEffect(() => {
|
|
801
1294
|
setInputValue(value);
|
|
@@ -808,11 +1301,11 @@ const FormWithSubmit = ({ onLicenseSubmit, disabled = false, isSubmitting = fals
|
|
|
808
1301
|
setInputValue(value);
|
|
809
1302
|
onValueChange?.(value);
|
|
810
1303
|
};
|
|
811
|
-
const displayLabel = resolveText(
|
|
812
|
-
const displayPlaceholder = resolveText(
|
|
813
|
-
const displaySubmitText = resolveText(
|
|
814
|
-
const displaySubmittingText = resolveText(
|
|
815
|
-
const displayHelperText = helperText ? resolveText(
|
|
1304
|
+
const displayLabel = resolveText(translate, label, 'License Key');
|
|
1305
|
+
const displayPlaceholder = resolveText(translate, placeholder, 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx');
|
|
1306
|
+
const displaySubmitText = resolveText(translate, submitText, 'Submit');
|
|
1307
|
+
const displaySubmittingText = resolveText(translate, submittingText, 'Verifying...');
|
|
1308
|
+
const displayHelperText = helperText ? resolveText(translate, helperText, '') : undefined;
|
|
816
1309
|
return (React.createElement("form", { className: `designbase-figma-form-with-submit ${className || ''}`, onSubmit: handleSubmit },
|
|
817
1310
|
React.createElement("div", { className: "designbase-figma-form-with-submit__field" },
|
|
818
1311
|
React.createElement("label", { className: "designbase-figma-form-with-submit__label" }, displayLabel),
|
|
@@ -826,7 +1319,8 @@ const FormWithSubmit = ({ onLicenseSubmit, disabled = false, isSubmitting = fals
|
|
|
826
1319
|
};
|
|
827
1320
|
FormWithSubmit.displayName = 'FormWithSubmit';
|
|
828
1321
|
|
|
829
|
-
const InteractionFeedback = ({ status = 'default', message, statusMessage, leftContent, rightAction, visible = true, className, t }) => {
|
|
1322
|
+
const InteractionFeedback = ({ status = 'default', message, statusMessage, leftContent, rightAction, visible = true, className, t, locale, }) => {
|
|
1323
|
+
const { t: translate } = useFigmaUiI18n(locale, t);
|
|
830
1324
|
const feedbackClasses = clsx('designbase-figma-interaction', `designbase-figma-interaction--${status}`, {
|
|
831
1325
|
'designbase-figma-interaction--visible': visible,
|
|
832
1326
|
}, className);
|
|
@@ -835,38 +1329,28 @@ const InteractionFeedback = ({ status = 'default', message, statusMessage, leftC
|
|
|
835
1329
|
return (React.createElement("div", { className: feedbackClasses },
|
|
836
1330
|
React.createElement("div", { className: "designbase-figma-interaction__content" },
|
|
837
1331
|
React.createElement("div", { className: "designbase-figma-interaction__left" }, leftContent || (React.createElement("div", { className: "designbase-figma-interaction__message" },
|
|
838
|
-
status === 'loading' && (React.createElement(Spinner, { size: "s", type: "circular", label:
|
|
839
|
-
(message || status === 'loading') && (React.createElement("span", { className: "designbase-figma-interaction__text" }, resolveText(
|
|
840
|
-
statusMessage && (React.createElement("span", { className: "designbase-figma-interaction__status-text" }, resolveText(
|
|
1332
|
+
status === 'loading' && (React.createElement(Spinner, { size: "s", type: "circular", label: translate('loading'), showLabel: false })),
|
|
1333
|
+
(message || status === 'loading') && (React.createElement("span", { className: "designbase-figma-interaction__text" }, resolveText(translate, message || { key: 'loading' }))),
|
|
1334
|
+
statusMessage && (React.createElement("span", { className: "designbase-figma-interaction__status-text" }, resolveText(translate, statusMessage)))))),
|
|
841
1335
|
rightAction && (React.createElement("div", { className: "designbase-figma-interaction__right" }, rightAction)))));
|
|
842
1336
|
};
|
|
843
1337
|
InteractionFeedback.displayName = 'InteractionFeedback';
|
|
844
1338
|
|
|
845
|
-
let useTranslation = null;
|
|
846
|
-
try {
|
|
847
|
-
const reactI18next = require('react-i18next');
|
|
848
|
-
useTranslation = reactI18next.useTranslation;
|
|
849
|
-
}
|
|
850
|
-
catch (e) {
|
|
851
|
-
}
|
|
852
1339
|
const DEFAULT_LANGUAGES = [
|
|
853
1340
|
{ code: 'ko', label: 'KO' },
|
|
854
1341
|
{ code: 'en', label: 'EN' },
|
|
855
1342
|
];
|
|
856
1343
|
const LanguageSelector = ({ currentLanguage, languages = DEFAULT_LANGUAGES, onLanguageChange, size = 's', className, t }) => {
|
|
857
|
-
const
|
|
858
|
-
const activeLanguage = currentLanguage ||
|
|
1344
|
+
const { locale, t: translate } = useFigmaUiI18n(currentLanguage, t);
|
|
1345
|
+
const activeLanguage = currentLanguage || locale;
|
|
859
1346
|
const handleLanguageChange = (languageCode) => {
|
|
860
|
-
if (i18n) {
|
|
861
|
-
i18n.changeLanguage(languageCode);
|
|
862
|
-
}
|
|
863
1347
|
if (onLanguageChange) {
|
|
864
1348
|
onLanguageChange(languageCode);
|
|
865
1349
|
}
|
|
866
1350
|
};
|
|
867
1351
|
const segmentOptions = languages.map((language) => ({
|
|
868
1352
|
value: language.code,
|
|
869
|
-
label: resolveText(
|
|
1353
|
+
label: resolveText(translate, language.label, language.code.toUpperCase()),
|
|
870
1354
|
disabled: false,
|
|
871
1355
|
}));
|
|
872
1356
|
return (React.createElement("div", { className: `designbase-figma-language-selector ${className || ''}` },
|
|
@@ -898,25 +1382,26 @@ function openExternalUrl(url) {
|
|
|
898
1382
|
}
|
|
899
1383
|
}
|
|
900
1384
|
|
|
901
|
-
const LicenseOffer = ({ joinUrl, guideUrl, membershipUrl, t, className, }) => {
|
|
1385
|
+
const LicenseOffer = ({ joinUrl, guideUrl, membershipUrl, t, locale, className, }) => {
|
|
1386
|
+
const { t: translate } = useFigmaUiI18n(locale, t);
|
|
902
1387
|
const titleId = `designbase-license-offer-title-${useId().replace(/:/g, '')}`;
|
|
903
1388
|
const resolvedJoinUrl = joinUrl || membershipUrl;
|
|
904
1389
|
const steps = [
|
|
905
|
-
resolveText(
|
|
906
|
-
resolveText(
|
|
907
|
-
resolveText(
|
|
1390
|
+
resolveText(translate, { key: 'license.youtube_membership_step_1' }, 'Designbase YouTube 채널 멤버십에 가입합니다.'),
|
|
1391
|
+
resolveText(translate, { key: 'license.youtube_membership_step_2' }, '멤버 전용 커뮤니티에서 이번 달 키를 복사합니다.'),
|
|
1392
|
+
resolveText(translate, { key: 'license.youtube_membership_step_3' }, '아래 입력창에 키를 붙여 넣고 활성화합니다.'),
|
|
908
1393
|
];
|
|
909
1394
|
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(
|
|
1395
|
+
React.createElement("h3", { id: titleId, className: "designbase-figma-license-offer__title" }, resolveText(translate, { key: 'license.youtube_membership_howto' }, '이용 방법')),
|
|
911
1396
|
React.createElement("ol", { className: "designbase-figma-license-offer__steps" }, steps.map((step, index) => (React.createElement("li", { key: index },
|
|
912
1397
|
React.createElement("span", { className: "designbase-figma-license-offer__step-index", "aria-hidden": "true" },
|
|
913
1398
|
index + 1,
|
|
914
1399
|
"."),
|
|
915
1400
|
React.createElement("span", null, step))))),
|
|
916
1401
|
(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(
|
|
1402
|
+
resolvedJoinUrl && (React.createElement(Button, { onPress: () => openExternalUrl(resolvedJoinUrl), variant: "primary", size: "m", fullWidth: true }, resolveText(translate, { key: 'license.youtube_membership_join' }, '멤버십 가입'))),
|
|
918
1403
|
guideUrl && (React.createElement(Button, { onPress: () => openExternalUrl(guideUrl), variant: "secondary", size: "m", fullWidth: true },
|
|
919
|
-
React.createElement("span", null, resolveText(
|
|
1404
|
+
React.createElement("span", null, resolveText(translate, { key: 'license.youtube_membership_guide' }, '안내보기')),
|
|
920
1405
|
React.createElement(ExternalLinkIcon, { size: 16, "aria-hidden": "true" })))))));
|
|
921
1406
|
};
|
|
922
1407
|
LicenseOffer.displayName = 'LicenseOffer';
|
|
@@ -926,13 +1411,13 @@ function maskLicenseKey(value) {
|
|
|
926
1411
|
return '••••••••';
|
|
927
1412
|
return `${value.slice(0, 4)}••••${value.slice(-4)}`;
|
|
928
1413
|
}
|
|
929
|
-
function formatDate(value) {
|
|
1414
|
+
function formatDate(value, locale) {
|
|
930
1415
|
if (!value)
|
|
931
1416
|
return undefined;
|
|
932
1417
|
const date = new Date(value);
|
|
933
1418
|
if (Number.isNaN(date.getTime()))
|
|
934
1419
|
return value;
|
|
935
|
-
return new Intl.DateTimeFormat(
|
|
1420
|
+
return new Intl.DateTimeFormat(locale, { dateStyle: 'medium' }).format(date);
|
|
936
1421
|
}
|
|
937
1422
|
function formatScope(value, t) {
|
|
938
1423
|
if (!value)
|
|
@@ -953,43 +1438,44 @@ function formatSource(value, t) {
|
|
|
953
1438
|
}
|
|
954
1439
|
return value;
|
|
955
1440
|
}
|
|
956
|
-
const LicenseStatusCard = ({ status, licenseKey = '', licenseStatus, licenseSource, accessScope, expiresAt, lastVerifiedAt, validationMessage, allowRevealKey = false, t, className, }) => {
|
|
1441
|
+
const LicenseStatusCard = ({ status, licenseKey = '', licenseStatus, licenseSource, accessScope, expiresAt, lastVerifiedAt, validationMessage, allowRevealKey = false, t, locale, className, }) => {
|
|
1442
|
+
const { locale: resolvedLocale, t: translate } = useFigmaUiI18n(locale, t);
|
|
957
1443
|
const [isKeyRevealed, setIsKeyRevealed] = useState(false);
|
|
958
1444
|
const normalizedLicenseStatus = licenseStatus?.toLowerCase();
|
|
959
1445
|
const isInactiveStatus = ['expired', 'revoked', 'invalid', 'inactive'].includes(normalizedLicenseStatus || '');
|
|
960
1446
|
const isActive = status === 'PAID' && !isInactiveStatus;
|
|
961
1447
|
const statusText = isActive
|
|
962
|
-
? resolveText(
|
|
963
|
-
: resolveText(
|
|
1448
|
+
? resolveText(translate, { key: 'license.status_active' }, 'Active')
|
|
1449
|
+
: resolveText(translate, { key: 'license.status_inactive' }, 'License check failed');
|
|
964
1450
|
const statusClass = isActive ? 'active' : 'inactive';
|
|
965
1451
|
return (React.createElement("section", { className: `designbase-figma-license-status-card designbase-figma-license-status-card--${statusClass} ${className || ''}`, "aria-label": statusText },
|
|
966
1452
|
React.createElement("div", { className: "designbase-figma-license-status-card__header" },
|
|
967
1453
|
React.createElement("div", null,
|
|
968
1454
|
React.createElement("div", { className: "designbase-figma-license-status-card__eyebrow" }, isActive
|
|
969
|
-
? resolveText(
|
|
970
|
-
: resolveText(
|
|
1455
|
+
? resolveText(translate, { key: 'license.activated' }, '라이선스 활성화됨')
|
|
1456
|
+
: resolveText(translate, { key: 'license.status_label' }, '라이선스 상태')),
|
|
971
1457
|
React.createElement("h3", null, statusText)),
|
|
972
1458
|
React.createElement("span", { className: "designbase-figma-license-status-card__badge" }, licenseStatus || (isActive ? 'active' : 'inactive'))),
|
|
973
1459
|
validationMessage && !isActive && (React.createElement("p", { className: "designbase-figma-license-status-card__message" }, validationMessage)),
|
|
974
1460
|
licenseKey && (React.createElement("div", { className: "designbase-figma-license-status-card__key-row" },
|
|
975
|
-
React.createElement("span", null, resolveText(
|
|
1461
|
+
React.createElement("span", null, resolveText(translate, { key: 'license.key_label' }, '라이선스 키')),
|
|
976
1462
|
React.createElement("code", null, allowRevealKey && isKeyRevealed ? licenseKey : maskLicenseKey(licenseKey)),
|
|
977
1463
|
allowRevealKey && (React.createElement(Button, { type: "button", onPress: () => setIsKeyRevealed((value) => !value), variant: "tertiary", size: "s" }, isKeyRevealed
|
|
978
|
-
? resolveText(
|
|
979
|
-
: resolveText(
|
|
1464
|
+
? resolveText(translate, { key: 'license.hide_key' }, '숨기기')
|
|
1465
|
+
: resolveText(translate, { key: 'license.reveal_key' }, '보기'))))),
|
|
980
1466
|
(licenseSource || accessScope || expiresAt || lastVerifiedAt) && (React.createElement("dl", { className: "designbase-figma-license-status-card__meta" },
|
|
981
|
-
formatSource(licenseSource,
|
|
982
|
-
React.createElement("dt", null, resolveText(
|
|
983
|
-
React.createElement("dd", null, formatSource(licenseSource,
|
|
984
|
-
formatScope(accessScope,
|
|
985
|
-
React.createElement("dt", null, resolveText(
|
|
986
|
-
React.createElement("dd", null, formatScope(accessScope,
|
|
1467
|
+
formatSource(licenseSource, translate) && React.createElement(React.Fragment, null,
|
|
1468
|
+
React.createElement("dt", null, resolveText(translate, { key: 'license.source' }, '출처')),
|
|
1469
|
+
React.createElement("dd", null, formatSource(licenseSource, translate))),
|
|
1470
|
+
formatScope(accessScope, translate) && React.createElement(React.Fragment, null,
|
|
1471
|
+
React.createElement("dt", null, resolveText(translate, { key: 'license.access' }, '접근 범위')),
|
|
1472
|
+
React.createElement("dd", null, formatScope(accessScope, translate))),
|
|
987
1473
|
expiresAt && React.createElement(React.Fragment, null,
|
|
988
|
-
React.createElement("dt", null, resolveText(
|
|
989
|
-
React.createElement("dd", null, formatDate(expiresAt) || expiresAt)),
|
|
1474
|
+
React.createElement("dt", null, resolveText(translate, { key: 'license.expires' }, '만료일')),
|
|
1475
|
+
React.createElement("dd", null, formatDate(expiresAt, resolvedLocale) || expiresAt)),
|
|
990
1476
|
lastVerifiedAt && React.createElement(React.Fragment, null,
|
|
991
|
-
React.createElement("dt", null, resolveText(
|
|
992
|
-
React.createElement("dd", null, formatDate(lastVerifiedAt) || lastVerifiedAt))))));
|
|
1477
|
+
React.createElement("dt", null, resolveText(translate, { key: 'license.last_checked' }, '최근 확인')),
|
|
1478
|
+
React.createElement("dd", null, formatDate(lastVerifiedAt, resolvedLocale) || lastVerifiedAt))))));
|
|
993
1479
|
};
|
|
994
1480
|
LicenseStatusCard.displayName = 'LicenseStatusCard';
|
|
995
1481
|
|
|
@@ -1010,15 +1496,16 @@ function resolvePaymentFeedbackVariant(errorCode, explicitVariant) {
|
|
|
1010
1496
|
}
|
|
1011
1497
|
return 'error';
|
|
1012
1498
|
}
|
|
1013
|
-
const PaymentStatusSection = ({ activationLimit, activationUsage, licenseKey, onDeactivate, isDeactivating = false, showDetails = false, actionMessage, actionMessageVariant, errorCode, className, t, }) => {
|
|
1499
|
+
const PaymentStatusSection = ({ activationLimit, activationUsage, licenseKey, onDeactivate, isDeactivating = false, showDetails = false, actionMessage, actionMessageVariant, errorCode, className, t, locale, }) => {
|
|
1500
|
+
const { t: translate } = useFigmaUiI18n(locale, t);
|
|
1014
1501
|
const remainingActivations = Math.max(0, activationLimit - activationUsage);
|
|
1015
|
-
const resolvedActionMessage = actionMessage ? resolveText(
|
|
1502
|
+
const resolvedActionMessage = actionMessage ? resolveText(translate, actionMessage) : undefined;
|
|
1016
1503
|
const feedbackVariant = resolvedActionMessage
|
|
1017
1504
|
? resolvePaymentFeedbackVariant(errorCode, actionMessageVariant)
|
|
1018
1505
|
: undefined;
|
|
1019
1506
|
return (React.createElement("div", { className: clsx('designbase-figma-payment-status', className) },
|
|
1020
1507
|
React.createElement("div", { className: "designbase-figma-payment-status__license-key" },
|
|
1021
|
-
resolveText(
|
|
1508
|
+
resolveText(translate, { key: 'payment.license_key_label' }, '라이선스 키'),
|
|
1022
1509
|
": ",
|
|
1023
1510
|
React.createElement("span", null, licenseKey)),
|
|
1024
1511
|
showDetails && (React.createElement("div", { className: "designbase-figma-payment-status__details" },
|
|
@@ -1026,41 +1513,60 @@ const PaymentStatusSection = ({ activationLimit, activationUsage, licenseKey, on
|
|
|
1026
1513
|
React.createElement("div", { className: "designbase-figma-payment-status__remaining" },
|
|
1027
1514
|
remainingActivations,
|
|
1028
1515
|
' ',
|
|
1029
|
-
resolveText(
|
|
1516
|
+
resolveText(translate, { key: 'payment.activations_remaining' }, '활성'),
|
|
1030
1517
|
' ',
|
|
1031
|
-
resolveText(
|
|
1518
|
+
resolveText(translate, { key: 'payment.spots_remaining' }, '자리 남음')),
|
|
1032
1519
|
React.createElement("div", { className: "designbase-figma-payment-status__usage" },
|
|
1033
1520
|
activationUsage,
|
|
1034
1521
|
"/",
|
|
1035
1522
|
activationLimit)),
|
|
1036
1523
|
React.createElement("div", { className: "designbase-figma-payment-status__action" },
|
|
1037
1524
|
React.createElement(Button, { onPress: onDeactivate, variant: "secondary", size: "s", fullWidth: true, disabled: isDeactivating }, isDeactivating
|
|
1038
|
-
? resolveText(
|
|
1039
|
-
: resolveText(
|
|
1525
|
+
? resolveText(translate, { key: 'payment.deactivating' }, '비활성화중...')
|
|
1526
|
+
: resolveText(translate, { key: 'payment.deactivate_license' }, '라이선스 비활성화')),
|
|
1040
1527
|
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
1528
|
};
|
|
1042
1529
|
PaymentStatusSection.displayName = 'PaymentStatusSection';
|
|
1043
1530
|
|
|
1044
|
-
const
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1531
|
+
const defaultFeaturesByLocale = {
|
|
1532
|
+
ko: [
|
|
1533
|
+
{ name: '사용 제한', free: '일일 제한', pro: '무제한' },
|
|
1534
|
+
{ name: '모든 주제 사용', free: false, pro: true },
|
|
1535
|
+
{ name: '최대 선택 제한', free: '최대 5개', pro: '최대 30개' },
|
|
1536
|
+
{ name: '더미 개수', free: '1,400개', pro: '4,400개 이상' },
|
|
1537
|
+
{ name: '커스텀 숫자 사용', free: false, pro: true },
|
|
1538
|
+
],
|
|
1539
|
+
en: [
|
|
1540
|
+
{ name: 'Usage limit', free: 'Daily limit', pro: 'Unlimited' },
|
|
1541
|
+
{ name: 'All topics', free: false, pro: true },
|
|
1542
|
+
{ name: 'Selection limit', free: 'Up to 5', pro: 'Up to 30' },
|
|
1543
|
+
{ name: 'Dummy count', free: '1,400', pro: '4,400+' },
|
|
1544
|
+
{ name: 'Custom numbers', free: false, pro: true },
|
|
1545
|
+
],
|
|
1546
|
+
ja: [
|
|
1547
|
+
{ name: '使用制限', free: '1日の制限', pro: '無制限' },
|
|
1548
|
+
{ name: 'すべてのトピック', free: false, pro: true },
|
|
1549
|
+
{ name: '選択上限', free: '最大5個', pro: '最大30個' },
|
|
1550
|
+
{ name: 'ダミー数', free: '1,400個', pro: '4,400個以上' },
|
|
1551
|
+
{ name: 'カスタム数値', free: false, pro: true },
|
|
1552
|
+
],
|
|
1553
|
+
};
|
|
1051
1554
|
const defaultPricing = {
|
|
1052
1555
|
monthly: 2,
|
|
1053
1556
|
yearly: 21.6,
|
|
1054
1557
|
lifetime: 50,
|
|
1055
1558
|
};
|
|
1056
|
-
const PricingComparison = ({ features
|
|
1559
|
+
const PricingComparison = ({ features, pricing = defaultPricing, paymentType = 'subscription', t, locale, className, }) => {
|
|
1560
|
+
const { locale: resolvedLocale, t: translate } = useFigmaUiI18n(locale, t);
|
|
1057
1561
|
const [isYearly, setIsYearly] = useState(false);
|
|
1562
|
+
const resolvedFeatures = features ?? defaultFeaturesByLocale[resolvedLocale];
|
|
1563
|
+
const resolveFeatureValue = (value) => typeof value === 'boolean' ? (value ? '✓' : '✗') : value;
|
|
1058
1564
|
const getPrice = () => {
|
|
1059
1565
|
if (paymentType === 'lifetime') {
|
|
1060
|
-
return `$${pricing.lifetime?.toLocaleString()} - ${
|
|
1566
|
+
return `$${pricing.lifetime?.toLocaleString()} - ${translate('lifetimeAccess')}`;
|
|
1061
1567
|
}
|
|
1062
1568
|
const price = isYearly ? pricing.yearly : pricing.monthly;
|
|
1063
|
-
return `$${price?.toLocaleString()} / ${isYearly ?
|
|
1569
|
+
return `$${price?.toLocaleString()} / ${isYearly ? translate('perYear') : translate('perMonth')}`;
|
|
1064
1570
|
};
|
|
1065
1571
|
const getDiscountPercentage = () => {
|
|
1066
1572
|
if (paymentType === 'lifetime') {
|
|
@@ -1083,46 +1589,34 @@ const PricingComparison = ({ features = defaultFeatures, pricing = defaultPricin
|
|
|
1083
1589
|
}, options: [
|
|
1084
1590
|
{
|
|
1085
1591
|
value: 'monthly',
|
|
1086
|
-
label:
|
|
1592
|
+
label: translate('monthlyBilling'),
|
|
1087
1593
|
},
|
|
1088
1594
|
{
|
|
1089
1595
|
value: 'yearly',
|
|
1090
|
-
label: `${
|
|
1596
|
+
label: `${translate('yearlyBilling')} (${getDiscountPercentage()}% ${translate('pricing.discount_suffix')})`,
|
|
1091
1597
|
},
|
|
1092
1598
|
], size: "s" }))),
|
|
1093
1599
|
paymentType === 'lifetime' && (React.createElement("div", { className: "designbase-figma-pricing-comparison__lifetime-info" },
|
|
1094
|
-
React.createElement("p", { className: "designbase-figma-pricing-comparison__lifetime-text" },
|
|
1600
|
+
React.createElement("p", { className: "designbase-figma-pricing-comparison__lifetime-text" }, translate('oneTimePaymentLifetime')))),
|
|
1095
1601
|
React.createElement("table", { className: "designbase-figma-pricing-comparison__table" },
|
|
1096
1602
|
React.createElement("thead", null,
|
|
1097
1603
|
React.createElement("tr", null,
|
|
1098
|
-
React.createElement("th", null,
|
|
1604
|
+
React.createElement("th", null, translate('feature')),
|
|
1099
1605
|
React.createElement("th", null,
|
|
1100
|
-
React.createElement("div", { className: "designbase-figma-pricing-comparison__plan-name" },
|
|
1101
|
-
React.createElement("div", null,
|
|
1606
|
+
React.createElement("div", { className: "designbase-figma-pricing-comparison__plan-name" }, translate('free')),
|
|
1607
|
+
React.createElement("div", null, translate('freePrice'))),
|
|
1102
1608
|
React.createElement("th", { className: "designbase-figma-pricing-comparison__recommended" },
|
|
1103
1609
|
React.createElement("div", { className: "designbase-figma-pricing-comparison__plan-name" },
|
|
1104
|
-
|
|
1105
|
-
React.createElement("span", { className: "designbase-figma-pricing-comparison__recommended-badge" },
|
|
1610
|
+
translate('pro'),
|
|
1611
|
+
React.createElement("span", { className: "designbase-figma-pricing-comparison__recommended-badge" }, translate('recommended'))),
|
|
1106
1612
|
React.createElement("div", null, getPrice())))),
|
|
1107
|
-
React.createElement("tbody", null,
|
|
1613
|
+
React.createElement("tbody", null, resolvedFeatures.map((feature, index) => (React.createElement("tr", { key: index },
|
|
1108
1614
|
React.createElement("td", null, feature.name),
|
|
1109
|
-
React.createElement("td", null,
|
|
1110
|
-
|
|
1111
|
-
: feature.free),
|
|
1112
|
-
React.createElement("td", null, typeof feature.pro === 'boolean'
|
|
1113
|
-
? (feature.pro ? '✓' : '✗')
|
|
1114
|
-
: feature.pro))))))));
|
|
1615
|
+
React.createElement("td", null, resolveFeatureValue(feature.free)),
|
|
1616
|
+
React.createElement("td", null, resolveFeatureValue(feature.pro)))))))));
|
|
1115
1617
|
};
|
|
1116
1618
|
PricingComparison.displayName = 'PricingComparison';
|
|
1117
1619
|
|
|
1118
|
-
const DEFAULT_LICENSE_MESSAGE_TYPES = {
|
|
1119
|
-
initialize: 'initialize',
|
|
1120
|
-
verifyLicense: 'verify-license',
|
|
1121
|
-
deactivateLicense: 'deactivate-license',
|
|
1122
|
-
updateStatus: 'update-plugin-status',
|
|
1123
|
-
verifyResult: 'license-verification-result',
|
|
1124
|
-
deactivateResult: 'license-deactivation-result',
|
|
1125
|
-
};
|
|
1126
1620
|
const START_DESIGNBASE_AUTH_MESSAGE = 'start-designbase-auth';
|
|
1127
1621
|
const INITIAL_LICENSE_PAGE_STATE = {
|
|
1128
1622
|
paymentStatus: 'FREE',
|
|
@@ -1277,6 +1771,9 @@ function useFigmaLicenseBridge(options = {}) {
|
|
|
1277
1771
|
applyPatch({ lastMessage: payload.message });
|
|
1278
1772
|
}
|
|
1279
1773
|
}
|
|
1774
|
+
if (type === messageTypes.featureAccessResult || type === messageTypes.usageConsumeResult) {
|
|
1775
|
+
applyPatch(parseLicenseMessage(payload));
|
|
1776
|
+
}
|
|
1280
1777
|
};
|
|
1281
1778
|
window.addEventListener('message', handleMessage);
|
|
1282
1779
|
postPluginMessage({ type: messageTypes.initialize });
|
|
@@ -1301,6 +1798,20 @@ function useFigmaLicenseBridge(options = {}) {
|
|
|
1301
1798
|
setIsStartingAuth(true);
|
|
1302
1799
|
postPluginMessage({ type: START_DESIGNBASE_AUTH_MESSAGE });
|
|
1303
1800
|
}, []);
|
|
1801
|
+
const checkFeatureAccess = useCallback((featureKey, amount = 1) => {
|
|
1802
|
+
postPluginMessage({
|
|
1803
|
+
type: messageTypes.checkFeatureAccess,
|
|
1804
|
+
...(featureKey ? { featureKey } : {}),
|
|
1805
|
+
amount,
|
|
1806
|
+
});
|
|
1807
|
+
}, [messageTypes.checkFeatureAccess]);
|
|
1808
|
+
const consumeUsage = useCallback((amount = 1, featureKey) => {
|
|
1809
|
+
postPluginMessage({
|
|
1810
|
+
type: messageTypes.consumeUsage,
|
|
1811
|
+
amount,
|
|
1812
|
+
...(featureKey ? { featureKey } : {}),
|
|
1813
|
+
});
|
|
1814
|
+
}, [messageTypes.consumeUsage]);
|
|
1304
1815
|
return {
|
|
1305
1816
|
state,
|
|
1306
1817
|
isVerifying,
|
|
@@ -1309,6 +1820,8 @@ function useFigmaLicenseBridge(options = {}) {
|
|
|
1309
1820
|
verifyLicense,
|
|
1310
1821
|
deactivateLicense,
|
|
1311
1822
|
startDesignbaseAuth,
|
|
1823
|
+
checkFeatureAccess,
|
|
1824
|
+
consumeUsage,
|
|
1312
1825
|
setLicenseKeyInput: (licenseKey) => applyPatch({ licenseKey }),
|
|
1313
1826
|
};
|
|
1314
1827
|
}
|
|
@@ -1322,17 +1835,23 @@ function formatUsageLabel(state, t) {
|
|
|
1322
1835
|
const remaining = remainingUsage ?? Math.max(0, limit - usageUsed);
|
|
1323
1836
|
if (usageReset === 'daily') {
|
|
1324
1837
|
const fallback = `오늘 남은 횟수 ${remaining}/${limit}`;
|
|
1325
|
-
const translated = resolveText(t, { key: 'license.usage_daily' }, fallback) ?? fallback;
|
|
1838
|
+
const translated = resolveText(t, { key: 'license.usage_daily', values: { remaining, limit } }, fallback) ?? fallback;
|
|
1326
1839
|
return translated.includes(String(remaining)) ? translated : fallback;
|
|
1327
1840
|
}
|
|
1328
1841
|
if (usageReset === 'monthly') {
|
|
1329
1842
|
const fallback = `이번 달 남은 횟수 ${remaining}/${limit}`;
|
|
1330
|
-
const translated = resolveText(t, { key: 'license.usage_monthly' }, fallback) ?? fallback;
|
|
1843
|
+
const translated = resolveText(t, { key: 'license.usage_monthly', values: { remaining, limit } }, fallback) ?? fallback;
|
|
1844
|
+
return translated.includes(String(remaining)) ? translated : fallback;
|
|
1845
|
+
}
|
|
1846
|
+
if (usageReset === 'weekly') {
|
|
1847
|
+
const fallback = `이번 주 남은 횟수 ${remaining}/${limit}`;
|
|
1848
|
+
const translated = resolveText(t, { key: 'license.usage_weekly', values: { remaining, limit } }, fallback) ?? fallback;
|
|
1331
1849
|
return translated.includes(String(remaining)) ? translated : fallback;
|
|
1332
1850
|
}
|
|
1333
|
-
return `남은 횟수 ${remaining}/${limit}`;
|
|
1851
|
+
return resolveText(t, { key: 'license.usage_generic', values: { remaining, limit } }, `남은 횟수 ${remaining}/${limit}`) ?? `남은 횟수 ${remaining}/${limit}`;
|
|
1334
1852
|
}
|
|
1335
|
-
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, className, }) => {
|
|
1853
|
+
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, }) => {
|
|
1854
|
+
const { t: translate } = useFigmaUiI18n(locale, t);
|
|
1336
1855
|
const links = useMemo(() => ({
|
|
1337
1856
|
purchaseUrl: wordpressLinks?.purchaseUrl ?? paymentPageUrl,
|
|
1338
1857
|
membershipUrl: membershipUrl ?? wordpressLinks?.membershipUrl,
|
|
@@ -1354,7 +1873,7 @@ const PageLicense = ({ status: initialStatus, onClose, licenseKey: initialLicens
|
|
|
1354
1873
|
const [licenseKeyDraft, setLicenseKeyDraft] = useState(initialLicenseKey);
|
|
1355
1874
|
const isPaid = state.isLicenseRegistered && state.paymentStatus === 'PAID';
|
|
1356
1875
|
const isMembershipMode = mode === 'youtube-membership';
|
|
1357
|
-
const usageLabel = formatUsageLabel(state,
|
|
1876
|
+
const usageLabel = formatUsageLabel(state, translate);
|
|
1358
1877
|
const handleLicenseSubmit = async (submittedLicenseKey) => {
|
|
1359
1878
|
if (onLicenseSubmit) {
|
|
1360
1879
|
await onLicenseSubmit(submittedLicenseKey);
|
|
@@ -1371,79 +1890,80 @@ const PageLicense = ({ status: initialStatus, onClose, licenseKey: initialLicens
|
|
|
1371
1890
|
React.createElement("div", { className: "designbase-figma-page-license__header" },
|
|
1372
1891
|
React.createElement("div", { className: "designbase-figma-page-license__title" },
|
|
1373
1892
|
React.createElement("h2", null, isMembershipMode
|
|
1374
|
-
? resolveText(
|
|
1893
|
+
? resolveText(translate, { key: 'license.youtube_membership' }, 'YouTube 멤버십')
|
|
1375
1894
|
: isPaid
|
|
1376
|
-
? resolveText(
|
|
1377
|
-
: resolveText(
|
|
1895
|
+
? resolveText(translate, { key: 'license.pro_account' }, '프로 계정')
|
|
1896
|
+
: resolveText(translate, { key: 'license.upgrade_to_pro' }, '프로로 업그레이드')),
|
|
1378
1897
|
state.planTier && state.planTier !== 'FREE' && (React.createElement("span", { className: "designbase-figma-page-license__tier" }, state.planTier))),
|
|
1379
1898
|
React.createElement("p", { className: "designbase-figma-page-license__description" }, isPaid
|
|
1380
|
-
? resolveText(
|
|
1899
|
+
? resolveText(translate, { key: 'license.all_features_available' }, '모든 기능을 사용할 수 있습니다.')
|
|
1381
1900
|
: isMembershipMode
|
|
1382
|
-
? resolveText(
|
|
1383
|
-
: resolveText(
|
|
1901
|
+
? resolveText(translate, { key: 'license.youtube_membership_description' }, 'YouTube 멤버십에 가입하면 Designbase 플러그인을 무제한으로 사용할 수 있습니다.')
|
|
1902
|
+
: resolveText(translate, { key: 'license.purchase_for_unlimited' }, '디자인베이스 멤버십 또는 라이선스로 무제한 이용이 가능합니다.')),
|
|
1384
1903
|
!isPaid && !isMembershipMode && (React.createElement("p", { className: "designbase-figma-page-license__usage" }, usageLabel))),
|
|
1385
|
-
!isMembershipMode && (React.createElement(PricingComparison, { features: pricingFeatures, pricing: pricing, paymentType: paymentType, t:
|
|
1904
|
+
!isMembershipMode && (React.createElement(PricingComparison, { features: pricingFeatures, pricing: pricing, paymentType: paymentType, t: translate })),
|
|
1386
1905
|
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)),
|
|
1387
1906
|
isPaid && (React.createElement("div", { className: "designbase-figma-page-license__section" },
|
|
1388
1907
|
React.createElement("div", { className: "designbase-figma-page-license__section-header" },
|
|
1389
|
-
React.createElement("h3", null, resolveText(
|
|
1908
|
+
React.createElement("h3", null, resolveText(translate, { key: 'license.activated' }, '라이선스 활성화됨')),
|
|
1390
1909
|
React.createElement(Button, { onPress: () => setShowDetails(!showDetails), variant: "tertiary", size: "s", iconOnly: true },
|
|
1391
1910
|
React.createElement(MoreHorizontalIcon, { size: 16 }))),
|
|
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:
|
|
1911
|
+
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: translate }),
|
|
1393
1912
|
React.createElement("p", { className: "designbase-figma-page-license__usage" }, usageLabel),
|
|
1394
1913
|
showDetails && (React.createElement("div", { className: "designbase-figma-page-license__details" },
|
|
1395
1914
|
React.createElement("div", { className: "designbase-figma-page-license__activation-info" },
|
|
1396
1915
|
React.createElement("div", { className: "designbase-figma-page-license__remaining" },
|
|
1397
1916
|
Math.max(0, state.activationLimit - state.activationUsage),
|
|
1398
1917
|
' ',
|
|
1399
|
-
resolveText(
|
|
1918
|
+
resolveText(translate, { key: 'license.activations_remaining' }, '활성'),
|
|
1400
1919
|
' ',
|
|
1401
|
-
resolveText(
|
|
1920
|
+
resolveText(translate, { key: 'license.spots_remaining' }, '자리 남음')),
|
|
1402
1921
|
React.createElement("div", { className: "designbase-figma-page-license__usage-count" },
|
|
1403
1922
|
state.activationUsage,
|
|
1404
1923
|
"/",
|
|
1405
1924
|
state.activationLimit)),
|
|
1406
1925
|
React.createElement("div", { className: "designbase-figma-page-license__action" },
|
|
1407
1926
|
React.createElement(Button, { onPress: deactivateLicense, variant: "secondary", size: "s", fullWidth: true, disabled: isDeactivating }, isDeactivating
|
|
1408
|
-
? resolveText(
|
|
1409
|
-
: resolveText(
|
|
1927
|
+
? resolveText(translate, { key: 'license.deactivating' }, '비활성화 중...')
|
|
1928
|
+
: resolveText(translate, { key: 'license.deactivate' }, '라이선스 비활성화')),
|
|
1410
1929
|
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))))))),
|
|
1411
1930
|
!isPaid && (React.createElement(React.Fragment, null,
|
|
1412
|
-
isMembershipMode && (React.createElement(LicenseOffer, { joinUrl: links.membershipUrl || links.purchaseUrl, guideUrl: links.helpUrl || links.purchaseUrl, t:
|
|
1931
|
+
isMembershipMode && (React.createElement(LicenseOffer, { joinUrl: links.membershipUrl || links.purchaseUrl, guideUrl: links.helpUrl || links.purchaseUrl, t: translate })),
|
|
1413
1932
|
!isMembershipMode && (enableAccountAuth || links.purchaseUrl || links.accountUrl) && (React.createElement("div", { className: "designbase-figma-page-license__section" },
|
|
1414
1933
|
React.createElement("div", { className: "designbase-figma-page-license__section-header" },
|
|
1415
|
-
React.createElement("h3", null, resolveText(
|
|
1416
|
-
React.createElement("p", { className: "designbase-figma-page-license__hint" }, resolveText(
|
|
1934
|
+
React.createElement("h3", null, resolveText(translate, { key: 'license.designbase_account' }, '디자인베이스 계정'))),
|
|
1935
|
+
React.createElement("p", { className: "designbase-figma-page-license__hint" }, resolveText(translate, { key: 'license.designbase_account_hint' }, 'WordPress 계정으로 로그인하거나 멤버십을 구매하면 플러그인이 자동으로 활성화됩니다.')),
|
|
1417
1936
|
React.createElement("div", { className: "designbase-figma-page-license__actions" },
|
|
1418
1937
|
enableAccountAuth && (React.createElement(Button, { onPress: startDesignbaseAuth, variant: "primary", size: "m", disabled: isStartingAuth || isVerifying, fullWidth: true }, isStartingAuth
|
|
1419
|
-
? resolveText(
|
|
1420
|
-
: resolveText(
|
|
1938
|
+
? resolveText(translate, { key: 'license.authenticating' }, '인증 중...')
|
|
1939
|
+
: resolveText(translate, { key: 'license.login_with_designbase' }, '디자인베이스로 로그인'))),
|
|
1421
1940
|
links.purchaseUrl && (React.createElement(Button, { onPress: () => openExternalUrl(links.purchaseUrl), variant: "secondary", size: "m", fullWidth: true },
|
|
1422
|
-
React.createElement("span", null, resolveText(
|
|
1941
|
+
React.createElement("span", null, resolveText(translate, { key: 'license.purchase_membership' }, '멤버십 안내')),
|
|
1423
1942
|
React.createElement(ExternalLinkIcon, { size: 16 }))),
|
|
1424
1943
|
links.accountUrl && (React.createElement(Button, { onPress: () => openExternalUrl(links.accountUrl), variant: "tertiary", size: "s", fullWidth: true },
|
|
1425
|
-
React.createElement("span", null, resolveText(
|
|
1944
|
+
React.createElement("span", null, resolveText(translate, { key: 'license.manage_account' }, '계정·멤버십 관리')),
|
|
1426
1945
|
React.createElement(ExternalLinkIcon, { size: 16 })))))),
|
|
1427
1946
|
showLicenseKeyInput && (React.createElement("div", { className: isMembershipMode
|
|
1428
1947
|
? 'designbase-figma-page-license__key-form'
|
|
1429
1948
|
: 'designbase-figma-page-license__section' },
|
|
1430
1949
|
React.createElement("div", { className: "designbase-figma-page-license__section-header" },
|
|
1431
|
-
React.createElement("h3", null, resolveText(
|
|
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:
|
|
1950
|
+
React.createElement("h3", null, resolveText(translate, { key: 'license.enter_key' }, '라이선스 키 입력'))),
|
|
1951
|
+
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: translate }))))),
|
|
1433
1952
|
links.helpUrl && !isMembershipMode && (React.createElement("p", { className: "designbase-figma-page-license__disclaimer" },
|
|
1434
|
-
React.createElement("button", { type: "button", className: "designbase-figma-page-license__link", onClick: () => openExternalUrl(links.helpUrl) }, resolveText(
|
|
1435
|
-
React.createElement(Button, { className: "designbase-figma-page-license__close", onPress: onClose, variant: "tertiary", size: "s", iconOnly: true, "aria-label":
|
|
1953
|
+
React.createElement("button", { type: "button", className: "designbase-figma-page-license__link", onClick: () => openExternalUrl(links.helpUrl) }, resolveText(translate, { key: 'license.help_link' }, '라이선스·결제 도움말'))))),
|
|
1954
|
+
React.createElement(Button, { className: "designbase-figma-page-license__close", onPress: onClose, variant: "tertiary", size: "s", iconOnly: true, "aria-label": translate('close') },
|
|
1436
1955
|
React.createElement(CloseIcon, { size: 16 }))));
|
|
1437
1956
|
};
|
|
1438
1957
|
PageLicense.displayName = 'PageLicense';
|
|
1439
1958
|
|
|
1440
|
-
const PaymentBadge = ({ isActive, onClick, isLoading = false, text, t
|
|
1959
|
+
const PaymentBadge = ({ isActive, onClick, isLoading = false, text, t, locale, className, }) => {
|
|
1960
|
+
const { t: translate } = useFigmaUiI18n(locale, t);
|
|
1441
1961
|
const getBadgeText = () => {
|
|
1442
1962
|
if (text)
|
|
1443
1963
|
return text;
|
|
1444
1964
|
return isActive
|
|
1445
|
-
? (
|
|
1446
|
-
: (
|
|
1965
|
+
? translate('proUser')
|
|
1966
|
+
: translate('usingFree');
|
|
1447
1967
|
};
|
|
1448
1968
|
const getBadgeVariant = () => {
|
|
1449
1969
|
return isActive ? 'success' : 'secondary';
|
|
@@ -1452,7 +1972,8 @@ const PaymentBadge = ({ isActive, onClick, isLoading = false, text, t = (key) =>
|
|
|
1452
1972
|
};
|
|
1453
1973
|
PaymentBadge.displayName = 'PaymentBadge';
|
|
1454
1974
|
|
|
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 }) => {
|
|
1975
|
+
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, locale, autoComplete = true, autoCloseOnComplete = false }) => {
|
|
1976
|
+
const { t: translate } = useFigmaUiI18n(locale, t);
|
|
1456
1977
|
const [isCompleted, setIsCompleted] = useState(false);
|
|
1457
1978
|
const completeFiredRef = useRef(false);
|
|
1458
1979
|
const { processedNodes, totalNodes } = progress;
|
|
@@ -1482,11 +2003,11 @@ const ProgressModal = ({ isOpen, onClose, progress, title = { key: 'progress.wor
|
|
|
1482
2003
|
onClose();
|
|
1483
2004
|
};
|
|
1484
2005
|
const modalClasses = clsx('designbase-figma-progress-modal', className);
|
|
1485
|
-
const displayTitle = resolveText(
|
|
1486
|
-
const displayMessage = resolveText(
|
|
1487
|
-
const displayStopText = resolveText(
|
|
1488
|
-
const displayConfirmText = resolveText(
|
|
1489
|
-
const displayHelpText = resolveText(
|
|
2006
|
+
const displayTitle = resolveText(translate, isCompleted ? completedMessage : title, isCompleted ? '완료되었습니다' : '작업 중');
|
|
2007
|
+
const displayMessage = resolveText(translate, isCompleted ? completedMessage : processingMessage, isCompleted ? '완료되었습니다' : '처리 중...');
|
|
2008
|
+
const displayStopText = resolveText(translate, stopButtonText, '중지');
|
|
2009
|
+
const displayConfirmText = resolveText(translate, confirmButtonText, '확인');
|
|
2010
|
+
const displayHelpText = resolveText(translate, helpText);
|
|
1490
2011
|
return (React.createElement(Modal, { isOpen: isOpen, onClose: isCompleted ? onClose : undefined, title: displayTitle, size: "s", className: modalClasses, showCloseButton: false },
|
|
1491
2012
|
React.createElement("div", { className: "designbase-figma-progress-modal__content" },
|
|
1492
2013
|
React.createElement("div", { className: "designbase-figma-progress-modal__icon", "aria-hidden": "true" }, isCompleted ? (completeIcon || (React.createElement("div", { className: "designbase-figma-progress-modal__icon-success" },
|
|
@@ -1540,7 +2061,8 @@ const ResizablePlugin = ({ children, minWidth = 360, maxWidth = 460, minHeight =
|
|
|
1540
2061
|
};
|
|
1541
2062
|
ResizablePlugin.displayName = 'ResizablePlugin';
|
|
1542
2063
|
|
|
1543
|
-
const SettingsModal = ({ isOpen, onClose, categories: initialCategories, categoryGroups, onSave, onReset, title = { key: 'settings.modal_title' }, description = { key: 'settings.modal_description' }, className, t }) => {
|
|
2064
|
+
const SettingsModal = ({ isOpen, onClose, categories: initialCategories, categoryGroups, onSave, onReset, title = { key: 'settings.modal_title' }, description = { key: 'settings.modal_description' }, className, t, locale, }) => {
|
|
2065
|
+
const { t: translate } = useFigmaUiI18n(locale, t);
|
|
1544
2066
|
const [tempCategories, setTempCategories] = useState(initialCategories);
|
|
1545
2067
|
const [draggedIndex, setDraggedIndex] = useState(null);
|
|
1546
2068
|
useEffect(() => {
|
|
@@ -1633,8 +2155,8 @@ const SettingsModal = ({ isOpen, onClose, categories: initialCategories, categor
|
|
|
1633
2155
|
}
|
|
1634
2156
|
};
|
|
1635
2157
|
const modalClasses = clsx('designbase-figma-settings-modal', className);
|
|
1636
|
-
const displayTitle = resolveText(
|
|
1637
|
-
const displayDescription = resolveText(
|
|
2158
|
+
const displayTitle = resolveText(translate, title, '목록 변경');
|
|
2159
|
+
const displayDescription = resolveText(translate, description, '드래그하여 순서를 변경하거나, 토글하여 카테고리를 숨길 수 있습니다.');
|
|
1638
2160
|
return (React.createElement(Modal, { isOpen: isOpen, onClose: onClose, title: displayTitle, size: "m", className: modalClasses },
|
|
1639
2161
|
React.createElement("div", { className: "designbase-figma-settings-modal__content" },
|
|
1640
2162
|
displayDescription && (React.createElement("p", { className: "designbase-figma-settings-modal__description" }, displayDescription)),
|
|
@@ -1646,32 +2168,33 @@ const SettingsModal = ({ isOpen, onClose, categories: initialCategories, categor
|
|
|
1646
2168
|
React.createElement("span", { className: "designbase-figma-settings-modal__drag-handle" },
|
|
1647
2169
|
React.createElement(GripVerticalIcon, { size: 16 })),
|
|
1648
2170
|
category.icon && (React.createElement("span", { className: "designbase-figma-settings-modal__category-icon" }, category.icon)),
|
|
1649
|
-
React.createElement("span", { className: "designbase-figma-settings-modal__category-title" }, resolveText(
|
|
2171
|
+
React.createElement("span", { className: "designbase-figma-settings-modal__category-title" }, resolveText(translate, category.title))),
|
|
1650
2172
|
React.createElement(Toggle, { checked: category.enabled, onChange: () => handleToggle(category.id), size: "s" })))))),
|
|
1651
2173
|
React.createElement("div", { className: "designbase-figma-settings-modal__footer" },
|
|
1652
|
-
onReset && (React.createElement(Button, { onPress: handleReset, variant: "tertiary", size: "s" }, resolveText(
|
|
2174
|
+
onReset && (React.createElement(Button, { onPress: handleReset, variant: "tertiary", size: "s" }, resolveText(translate, { key: 'settings.reset' }, '초기화'))),
|
|
1653
2175
|
React.createElement("div", { className: "designbase-figma-settings-modal__footer-right" },
|
|
1654
|
-
React.createElement(Button, { onPress: handleCancel, variant: "tertiary", size: "s" }, resolveText(
|
|
1655
|
-
React.createElement(Button, { onPress: handleSave, variant: "primary", size: "s" }, resolveText(
|
|
2176
|
+
React.createElement(Button, { onPress: handleCancel, variant: "tertiary", size: "s" }, resolveText(translate, { key: 'settings.cancel' }, '취소')),
|
|
2177
|
+
React.createElement(Button, { onPress: handleSave, variant: "primary", size: "s" }, resolveText(translate, { key: 'settings.save' }, '저장'))))));
|
|
1656
2178
|
};
|
|
1657
2179
|
SettingsModal.displayName = 'SettingsModal';
|
|
1658
2180
|
|
|
1659
|
-
const TitleDescription = ({ iconSrc, iconAlt = 'Icon', title, description, button, features, className, t }) => {
|
|
2181
|
+
const TitleDescription = ({ iconSrc, iconAlt = 'Icon', title, description, button, features, className, t, locale, }) => {
|
|
2182
|
+
const { t: translate } = useFigmaUiI18n(locale, t);
|
|
1660
2183
|
const classes = [
|
|
1661
2184
|
'designbase-figma-title-description',
|
|
1662
2185
|
className,
|
|
1663
2186
|
]
|
|
1664
2187
|
.filter(Boolean)
|
|
1665
2188
|
.join(' ');
|
|
1666
|
-
const displayTitle = resolveText(
|
|
1667
|
-
const displayDescription = resolveText(
|
|
2189
|
+
const displayTitle = resolveText(translate, title);
|
|
2190
|
+
const displayDescription = resolveText(translate, description);
|
|
1668
2191
|
return (React.createElement("div", { className: classes },
|
|
1669
2192
|
iconSrc && (React.createElement("img", { className: "designbase-figma-title-description__icon", src: iconSrc, alt: iconAlt })),
|
|
1670
2193
|
React.createElement("h5", { className: "designbase-figma-title-description__title" }, displayTitle),
|
|
1671
2194
|
React.createElement("p", { className: "designbase-figma-title-description__description" }, displayDescription),
|
|
1672
2195
|
button && React.createElement("div", { className: "designbase-figma-title-description__button" }, button),
|
|
1673
2196
|
features && features.length > 0 && (React.createElement("ul", { className: "designbase-figma-title-description__features" }, features.map((feature, index) => {
|
|
1674
|
-
const displayFeature = resolveText(
|
|
2197
|
+
const displayFeature = resolveText(translate, feature);
|
|
1675
2198
|
return (React.createElement("li", { key: index, className: "designbase-figma-title-description__feature-item" },
|
|
1676
2199
|
React.createElement("span", { className: "designbase-figma-title-description__check-icon" },
|
|
1677
2200
|
React.createElement("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg" },
|
|
@@ -1682,10 +2205,11 @@ const TitleDescription = ({ iconSrc, iconAlt = 'Icon', title, description, butto
|
|
|
1682
2205
|
};
|
|
1683
2206
|
TitleDescription.displayName = 'TitleDescription';
|
|
1684
2207
|
|
|
1685
|
-
const UpgradeBanner = ({ onClick, variant = 'button', isLoading = false, title = { key: 'banner.upgrade_title' }, description = { key: 'banner.upgrade_description' }, buttonText = { key: 'banner.upgrade_button' }, useGradient = false, gradientScheme = 'primary', gradientTone = 'light', gradientAnimated = true, className, t }) => {
|
|
1686
|
-
const
|
|
1687
|
-
const
|
|
1688
|
-
const
|
|
2208
|
+
const UpgradeBanner = ({ onClick, variant = 'button', isLoading = false, title = { key: 'banner.upgrade_title' }, description = { key: 'banner.upgrade_description' }, buttonText = { key: 'banner.upgrade_button' }, useGradient = false, gradientScheme = 'primary', gradientTone = 'light', gradientAnimated = true, className, t, locale, }) => {
|
|
2209
|
+
const { t: translate } = useFigmaUiI18n(locale, t);
|
|
2210
|
+
const displayTitle = resolveText(translate, title, '프로로 업그레이드하세요');
|
|
2211
|
+
const displayDescription = resolveText(translate, description, '무제한 기능을 사용하고 더 많은 혜택을 누리세요.');
|
|
2212
|
+
const displayButtonText = resolveText(translate, buttonText, '지금 업그레이드');
|
|
1689
2213
|
const bannerContent = (React.createElement("div", { className: `designbase-figma-upgrade-banner__content designbase-figma-upgrade-banner__content--${variant}`, onClick: variant === 'clickable' ? onClick : undefined, role: variant === 'clickable' ? 'button' : undefined, tabIndex: variant === 'clickable' ? 0 : undefined, onKeyDown: variant === 'clickable' ? (e) => {
|
|
1690
2214
|
if (e.key === 'Enter' || e.key === ' ') {
|
|
1691
2215
|
e.preventDefault();
|
|
@@ -1701,5 +2225,5 @@ const UpgradeBanner = ({ onClick, variant = 'button', isLoading = false, title =
|
|
|
1701
2225
|
};
|
|
1702
2226
|
UpgradeBanner.displayName = 'UpgradeBanner';
|
|
1703
2227
|
|
|
1704
|
-
export {
|
|
2228
|
+
export { DonationBadge, FIGMA_UI_TRANSLATIONS, FigmaContainer, FigmaFooter, FigmaHeader, FigmaPageHead, FigmaSection, FigmaSidebar, FigmaToast, FigmaToastProvider, FigmaUiI18nProvider, FormWithSubmit, InteractionFeedback, LanguageSelector, LanguageSettingsModal, LicenseOffer, LicenseStatusCard, LogoDropdown, PageLicense, PaymentBadge, PaymentStatusSection, PricingComparison, ProgressModal, ResizablePlugin, START_DESIGNBASE_AUTH_MESSAGE, SettingsModal, TitleDescription, UpgradeBanner, UsageStatusPopover, createFigmaUiTranslator, getActiveSection, interpolateText, normalizeFigmaUiLocale, resolvePaymentFeedbackVariant, resolveText, scrollToSection, useFigmaLicenseBridge, useFigmaToast, useFigmaUiI18n, useToast };
|
|
1705
2229
|
//# sourceMappingURL=index.esm.js.map
|