@designbasekorea/figma-ui 0.1.7 → 0.1.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.esm.js CHANGED
@@ -5,7 +5,7 @@ export * from '@designbasekorea/ui';
5
5
 
6
6
  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}
7
7
 
8
- const DonationBadge$1 = ({ donationUrl = 'https://buymeacoffee.com/designbase', text = 'Buy me a coffee', iconType = 'heart', size = 'm', className, onClick, }) => {
8
+ const DonationBadge = ({ donationUrl = 'https://buymeacoffee.com/designbase', text = 'Buy me a coffee', iconType = 'heart', size = 'm', className, onClick, }) => {
9
9
  const handleClick = () => {
10
10
  if (onClick) {
11
11
  onClick();
@@ -20,11 +20,9 @@ const DonationBadge$1 = ({ donationUrl = 'https://buymeacoffee.com/designbase',
20
20
  React.createElement(Icon, { className: "designbase-figma-donation-badge__icon" }),
21
21
  React.createElement("span", { className: "designbase-figma-donation-badge__text" }, text)));
22
22
  };
23
- DonationBadge$1.displayName = 'DonationBadge';
23
+ DonationBadge.displayName = 'DonationBadge';
24
24
 
25
- DonationBadge;
26
-
27
- const LogoDropdown$1 = ({ logoSrc, logoAlt = 'DesignBase', logoType = 'designbase', logoSize = 'xs', links, position = 'top-left', className, t = (key) => key, }) => {
25
+ const LogoDropdown = ({ logoSrc, logoAlt = 'DesignBase', logoType = 'designbase', logoSize = 'xs', links, position = 'top-left', className, t = (key) => key, }) => {
28
26
  const [isOpen, setIsOpen] = useState(false);
29
27
  const toggleDropdown = () => setIsOpen(!isOpen);
30
28
  const handleLinkClick = (url) => {
@@ -51,9 +49,9 @@ const LogoDropdown$1 = ({ logoSrc, logoAlt = 'DesignBase', logoType = 'designbas
51
49
  React.createElement("span", { className: "designbase-figma-logo-dropdown__text" }, t(link.name)),
52
50
  React.createElement(ExternalLinkIcon, { size: 16, className: "designbase-figma-logo-dropdown__arrow" })))))))));
53
51
  };
54
- LogoDropdown$1.displayName = 'LogoDropdown';
52
+ LogoDropdown.displayName = 'LogoDropdown';
55
53
 
56
- const PaymentBadge$1 = ({ isActive, onClick, isLoading = false, text, t = (key) => key, className, }) => {
54
+ const PaymentBadge = ({ isActive, onClick, isLoading = false, text, t = (key) => key, className, }) => {
57
55
  const getBadgeText = () => {
58
56
  if (text)
59
57
  return text;
@@ -66,13 +64,13 @@ const PaymentBadge$1 = ({ isActive, onClick, isLoading = false, text, t = (key)
66
64
  };
67
65
  return (React.createElement("div", { className: `designbase-figma-payment-badge ${className || ''} ${isActive ? 'active' : 'free'}`, onClick: onClick, style: { cursor: onClick ? 'pointer' : 'default' } }, isLoading ? (React.createElement(Spinner, { size: "s" })) : (React.createElement(Badge, { variant: getBadgeVariant(), size: "s", className: "designbase-figma-payment-badge__badge" }, getBadgeText()))));
68
66
  };
69
- PaymentBadge$1.displayName = 'PaymentBadge';
67
+ PaymentBadge.displayName = 'PaymentBadge';
70
68
 
71
69
  const DEFAULT_LANGUAGES = [
72
70
  { code: 'ko', label: 'KO' },
73
71
  { code: 'en', label: 'EN' },
74
72
  ];
75
- const LanguageSelector$1 = ({ currentLanguage = 'ko', languages = DEFAULT_LANGUAGES, onLanguageChange, size = 's', className, }) => {
73
+ const LanguageSelector = ({ currentLanguage = 'ko', languages = DEFAULT_LANGUAGES, onLanguageChange, size = 's', className, }) => {
76
74
  const handleLanguageChange = (languageCode) => {
77
75
  if (onLanguageChange) {
78
76
  onLanguageChange(languageCode);
@@ -83,9 +81,9 @@ const LanguageSelector$1 = ({ currentLanguage = 'ko', languages = DEFAULT_LANGUA
83
81
  'designbase-figma-language-selector__button--active': currentLanguage === language.code,
84
82
  }), onClick: () => handleLanguageChange(language.code), "aria-label": `${language.label} 언어 선택`, "aria-pressed": currentLanguage === language.code }, language.label)))));
85
83
  };
86
- LanguageSelector$1.displayName = 'LanguageSelector';
84
+ LanguageSelector.displayName = 'LanguageSelector';
87
85
 
88
- const Footer$1 = ({ logoSrc, logoAlt = 'DesignBase', logoType = 'designbase', logoSize = 'xs', logoLinks = [], onLicensePageClick, paymentStatus = 'FREE', usageCount = 0, isLoading = false, showPaymentStatus = true, maxDailyUsage = 20, showDonation = false, donationUrl = 'https://buymeacoffee.com/designbase', donationText = 'Buy me a coffee', showLanguageSelector = false, currentLanguage = 'ko', languages, onLanguageChange, className, t = (key) => key, children, }) => {
86
+ const Footer = ({ logoSrc, logoAlt = 'DesignBase', logoType = 'designbase', logoSize = 'xs', logoLinks = [], onLicensePageClick, paymentStatus = 'FREE', usageCount = 0, isLoading = false, showPaymentStatus = true, maxDailyUsage = 20, showDonation = false, donationUrl = 'https://buymeacoffee.com/designbase', donationText = 'Buy me a coffee', showLanguageSelector = false, currentLanguage = 'ko', languages, onLanguageChange, className, t = (key) => key, children, }) => {
89
87
  const isActive = paymentStatus === 'PAID';
90
88
  const hasChildren = React.Children.count(children) > 0;
91
89
  const classes = clsx('designbase-figma-footer', {
@@ -94,8 +92,8 @@ const Footer$1 = ({ logoSrc, logoAlt = 'DesignBase', logoType = 'designbase', lo
94
92
  return (React.createElement("footer", { className: classes },
95
93
  React.createElement("div", { className: "designbase-figma-footer__wrap" },
96
94
  React.createElement("div", { className: "designbase-figma-footer__left" },
97
- React.createElement(LogoDropdown$1, { logoSrc: logoSrc, logoAlt: logoAlt, logoType: logoType, logoSize: logoSize, links: logoLinks, position: "top-left", t: t }),
98
- showLanguageSelector && (React.createElement(LanguageSelector$1, { currentLanguage: currentLanguage, languages: languages, onLanguageChange: onLanguageChange, size: "s" }))),
95
+ React.createElement(LogoDropdown, { logoSrc: logoSrc, logoAlt: logoAlt, logoType: logoType, logoSize: logoSize, links: logoLinks, position: "top-left", t: t }),
96
+ showLanguageSelector && (React.createElement(LanguageSelector, { currentLanguage: currentLanguage, languages: languages, onLanguageChange: onLanguageChange, size: "s" }))),
99
97
  showPaymentStatus && (React.createElement("div", { className: "designbase-figma-footer__payment-states" },
100
98
  !isLoading && (React.createElement("div", { className: "designbase-figma-footer__usage-info" }, isActive ? (React.createElement("span", { className: "designbase-figma-footer__unlimited-usage" }, t('unlimitedUsage'))) : (React.createElement(React.Fragment, null,
101
99
  React.createElement("span", { className: "designbase-figma-footer__usage-count" }, usageCount),
@@ -105,16 +103,14 @@ const Footer$1 = ({ logoSrc, logoAlt = 'DesignBase', logoType = 'designbase', lo
105
103
  " ",
106
104
  t('perDay')),
107
105
  React.createElement("span", { className: "designbase-figma-footer__reset-info" }, t('resetsDaily')))))),
108
- React.createElement(PaymentBadge$1, { isActive: isActive, onClick: onLicensePageClick, isLoading: isLoading, t: t }))),
106
+ React.createElement(PaymentBadge, { isActive: isActive, onClick: onLicensePageClick, isLoading: isLoading, t: t }))),
109
107
  showDonation && (React.createElement("div", { className: "designbase-figma-footer__donation" },
110
- React.createElement(DonationBadge$1, { donationUrl: donationUrl, text: donationText, iconType: "heart", size: "s" })))),
108
+ React.createElement(DonationBadge, { donationUrl: donationUrl, text: donationText, iconType: "heart", size: "s" })))),
111
109
  children));
112
110
  };
113
- Footer$1.displayName = 'Footer';
114
-
115
- Footer;
111
+ Footer.displayName = 'Footer';
116
112
 
117
- const FormWithSubmit$1 = ({ onLicenseSubmit, disabled = false, isSubmitting = false, value = '', onValueChange, label = 'License Key', placeholder = 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx', submitText = 'Submit', submittingText = 'Verifying...', className, }) => {
113
+ const FormWithSubmit = ({ onLicenseSubmit, disabled = false, isSubmitting = false, value = '', onValueChange, label = 'License Key', placeholder = 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx', submitText = 'Submit', submittingText = 'Verifying...', className, }) => {
118
114
  const [inputValue, setInputValue] = useState(value);
119
115
  useEffect(() => {
120
116
  setInputValue(value);
@@ -134,11 +130,9 @@ const FormWithSubmit$1 = ({ onLicenseSubmit, disabled = false, isSubmitting = fa
134
130
  React.createElement(Input, { value: inputValue, onChange: handleChange, placeholder: placeholder, disabled: disabled || isSubmitting, size: "m" })),
135
131
  React.createElement(Button, { type: "submit", variant: "primary", size: "m", loading: isSubmitting, disabled: !inputValue.trim() || isSubmitting || disabled, fullWidth: true }, isSubmitting ? submittingText : submitText)));
136
132
  };
137
- FormWithSubmit$1.displayName = 'FormWithSubmit';
133
+ FormWithSubmit.displayName = 'FormWithSubmit';
138
134
 
139
- FormWithSubmit;
140
-
141
- const InteractionFeedback$1 = ({ status = 'default', message, statusMessage, leftContent, rightContent, visible = true, className, fixed = true, }) => {
135
+ const InteractionFeedback = ({ status = 'default', message, statusMessage, leftContent, rightContent, visible = true, className, fixed = true, }) => {
142
136
  const wrapperClasses = clsx('designbase-figma-interaction-wrapper', {
143
137
  'designbase-figma-interaction-wrapper--fixed': fixed,
144
138
  });
@@ -155,15 +149,9 @@ const InteractionFeedback$1 = ({ status = 'default', message, statusMessage, lef
155
149
  statusMessage && (React.createElement("span", { className: "designbase-figma-interaction__status-text" }, statusMessage))))),
156
150
  rightContent && (React.createElement("div", { className: "designbase-figma-interaction__right" }, rightContent))))));
157
151
  };
158
- InteractionFeedback$1.displayName = 'InteractionFeedback';
159
-
160
- InteractionFeedback;
152
+ InteractionFeedback.displayName = 'InteractionFeedback';
161
153
 
162
- LanguageSelector;
163
-
164
- LogoDropdown;
165
-
166
- const PaymentStatusSection$1 = ({ status, usageCount, activationLimit, activationUsage, licenseKey, onDeactivate, isDeactivating = false, showDetails = false, className, }) => {
154
+ const PaymentStatusSection = ({ status, usageCount, activationLimit, activationUsage, licenseKey, onDeactivate, isDeactivating = false, showDetails = false, className, }) => {
167
155
  const remainingActivations = activationLimit - activationUsage;
168
156
  return (React.createElement("div", { className: `designbase-figma-payment-status ${className || ''}` },
169
157
  React.createElement("div", { className: "designbase-figma-payment-status__license-key" },
@@ -182,7 +170,7 @@ const PaymentStatusSection$1 = ({ status, usageCount, activationLimit, activatio
182
170
  activationLimit)),
183
171
  React.createElement(Button, { onClick: onDeactivate, variant: "tertiary", size: "s", disabled: isDeactivating }, isDeactivating ? '비활성화중...' : '라이선스 비활성화')))));
184
172
  };
185
- PaymentStatusSection$1.displayName = 'PaymentStatusSection';
173
+ PaymentStatusSection.displayName = 'PaymentStatusSection';
186
174
 
187
175
  const defaultFeatures = [
188
176
  { name: '사용 제한', free: '일일 제한', pro: '무제한' },
@@ -195,7 +183,7 @@ const defaultPricing = {
195
183
  monthly: 2,
196
184
  yearly: 21.6,
197
185
  };
198
- const PricingComparison$1 = ({ features = defaultFeatures, pricing = defaultPricing, t = (key) => key, className, }) => {
186
+ const PricingComparison = ({ features = defaultFeatures, pricing = defaultPricing, t = (key) => key, className, }) => {
199
187
  const [isYearly, setIsYearly] = useState(false);
200
188
  const getPrice = () => {
201
189
  const price = isYearly ? pricing.yearly : pricing.monthly;
@@ -244,9 +232,9 @@ const PricingComparison$1 = ({ features = defaultFeatures, pricing = defaultPric
244
232
  ? (feature.pro ? '✓' : '✗')
245
233
  : feature.pro))))))));
246
234
  };
247
- PricingComparison$1.displayName = 'PricingComparison';
235
+ PricingComparison.displayName = 'PricingComparison';
248
236
 
249
- const PageLicense$1 = ({ status: initialStatus, onClose, usageCount: initialUsageCount = 0, onLicenseSubmit, licenseKey: initialLicenseKey = '', setPaymentStatus, setUsageCount, setShowLicensePage, paymentPageUrl, t = (key) => key, className, }) => {
237
+ const PageLicense = ({ status: initialStatus, onClose, usageCount: initialUsageCount = 0, onLicenseSubmit, licenseKey: initialLicenseKey = '', setPaymentStatus, setUsageCount, setShowLicensePage, paymentPageUrl, t = (key) => key, className, }) => {
250
238
  const [isSubmitting, setIsSubmitting] = useState(false);
251
239
  const [isDeactivating, setIsDeactivating] = useState(false);
252
240
  const [licenseKey, setLicenseKey] = useState(initialLicenseKey);
@@ -332,37 +320,29 @@ const PageLicense$1 = ({ status: initialStatus, onClose, usageCount: initialUsag
332
320
  React.createElement("p", { className: "designbase-figma-page-license__description" }, isPaid
333
321
  ? t('allFeaturesAvailable') || '모든 기능을 사용할 수 있습니다.'
334
322
  : t('purchaseForUnlimited') || '라이선스 구매 후 무제한 접근이 가능합니다.')),
335
- React.createElement(PricingComparison$1, { t: t }),
323
+ React.createElement(PricingComparison, { t: t }),
336
324
  isPaid && (React.createElement("div", { className: "designbase-figma-page-license__section" },
337
325
  React.createElement("div", { className: "designbase-figma-page-license__section-header" },
338
326
  React.createElement("h3", null, t('licenseActivated') || '라이선스 활성화됨'),
339
327
  React.createElement(Button, { onClick: () => setShowDetails(!showDetails), variant: "tertiary", size: "s", iconOnly: true },
340
328
  React.createElement(MoreHorizontalIcon, { size: 16 }))),
341
- React.createElement(PaymentStatusSection$1, { status: status, usageCount: usageCount, activationLimit: activationLimit, activationUsage: activationUsage, licenseKey: licenseKey, onDeactivate: handleDeactivateLicense, isDeactivating: isDeactivating, showDetails: showDetails }))),
329
+ React.createElement(PaymentStatusSection, { status: status, usageCount: usageCount, activationLimit: activationLimit, activationUsage: activationUsage, licenseKey: licenseKey, onDeactivate: handleDeactivateLicense, isDeactivating: isDeactivating, showDetails: showDetails }))),
342
330
  React.createElement("div", { className: "designbase-figma-page-license__section" },
343
331
  React.createElement("div", { className: "designbase-figma-page-license__section-header" },
344
332
  React.createElement("h3", null, t('enterLicenseKey') || '라이선스 키 입력'),
345
333
  !isPaid && paymentPageUrl && (React.createElement(Button, { onClick: () => window.open(paymentPageUrl, '_blank'), variant: "primary", size: "s" },
346
334
  React.createElement("span", null, t('purchaseLicense') || '라이선스 구매'),
347
335
  React.createElement(ExternalLinkIcon, { size: 16 })))),
348
- React.createElement(FormWithSubmit$1, { onLicenseSubmit: handleLicenseSubmit, disabled: false, isSubmitting: isSubmitting, value: licenseKey, onValueChange: setLicenseKey, label: t('licenseKey') || 'License Key', submitText: t('submit') || 'Submit', submittingText: t('verifying') || 'Verifying...' }),
336
+ React.createElement(FormWithSubmit, { onLicenseSubmit: handleLicenseSubmit, disabled: false, isSubmitting: isSubmitting, value: licenseKey, onValueChange: setLicenseKey, label: t('licenseKey') || 'License Key', submitText: t('submit') || 'Submit', submittingText: t('verifying') || 'Verifying...' }),
349
337
  React.createElement("p", { className: "designbase-figma-page-license__disclaimer" }, isPaid
350
338
  ? t('licenseActivatedSuccess') || '라이선스가 성공적으로 활성화되었습니다.'
351
339
  : t('enterLicenseFromEmail') || '구독 후 이메일로 받은 라이선스 키를 입력하세요.'))),
352
340
  React.createElement(Button, { className: "designbase-figma-page-license__close", onPress: onClose, variant: "tertiary", size: "s", "aria-label": "Close" },
353
341
  React.createElement(CloseIcon, { size: 20 }))));
354
342
  };
355
- PageLicense$1.displayName = 'PageLicense';
356
-
357
- PageLicense;
343
+ PageLicense.displayName = 'PageLicense';
358
344
 
359
- PaymentBadge;
360
-
361
- PaymentStatusSection;
362
-
363
- PricingComparison;
364
-
365
- const ProgressModal$1 = ({ isOpen, onClose, progress, title = '작업 중', completedMessage = '완료되었습니다', processingMessage = '처리 중...', stopButtonText = '중지', confirmButtonText = '확인', helpText, onStop, onComplete, loadingIcon, completeIcon, className, }) => {
345
+ const ProgressModal = ({ isOpen, onClose, progress, title = '작업 중', completedMessage = '완료되었습니다', processingMessage = '처리 중...', stopButtonText = '중지', confirmButtonText = '확인', helpText, onStop, onComplete, loadingIcon, completeIcon, className, }) => {
366
346
  const [isCompleted, setIsCompleted] = useState(false);
367
347
  const { processedNodes, totalNodes } = progress;
368
348
  const progressPercentage = totalNodes > 0 ? (processedNodes / totalNodes) * 100 : 0;
@@ -394,11 +374,9 @@ const ProgressModal$1 = ({ isOpen, onClose, progress, title = '작업 중', comp
394
374
  helpText && !isCompleted && (React.createElement("p", { className: "designbase-figma-progress-modal__help-text" }, helpText)),
395
375
  React.createElement("div", { className: "designbase-figma-progress-modal__actions" }, isCompleted ? (React.createElement(Button, { onPress: onClose, variant: "primary", size: "m", fullWidth: true }, confirmButtonText)) : (React.createElement(Button, { onPress: handleStop, variant: "secondary", size: "m", fullWidth: true }, stopButtonText))))));
396
376
  };
397
- ProgressModal$1.displayName = 'ProgressModal';
398
-
399
- ProgressModal;
377
+ ProgressModal.displayName = 'ProgressModal';
400
378
 
401
- const ResizablePlugin$1 = ({ children, minWidth = 360, maxWidth = 460, minHeight = 440, maxHeight = 700, icon, className, }) => {
379
+ const ResizablePlugin = ({ children, minWidth = 360, maxWidth = 460, minHeight = 440, maxHeight = 700, icon, className, }) => {
402
380
  const [isResizing, setIsResizing] = useState(false);
403
381
  const resizeHandleRef = useRef(null);
404
382
  useEffect(() => {
@@ -436,11 +414,9 @@ const ResizablePlugin$1 = ({ children, minWidth = 360, maxWidth = 460, minHeight
436
414
  setIsResizing(true);
437
415
  }, role: "button", tabIndex: 0, "aria-label": "Resize plugin" }, icon || React.createElement(ExpandIcon, { size: 12 }))));
438
416
  };
439
- ResizablePlugin$1.displayName = 'ResizablePlugin';
417
+ ResizablePlugin.displayName = 'ResizablePlugin';
440
418
 
441
- ResizablePlugin;
442
-
443
- const SettingsModal$1 = ({ isOpen, onClose, categories: initialCategories, categoryGroups, onSave, onReset, title = '목록 변경', description = '드래그하여 순서를 변경하거나, 토글하여 카테고리를 숨길 수 있습니다.', className, }) => {
419
+ const SettingsModal = ({ isOpen, onClose, categories: initialCategories, categoryGroups, onSave, onReset, title = '목록 변경', description = '드래그하여 순서를 변경하거나, 토글하여 카테고리를 숨길 수 있습니다.', className, }) => {
444
420
  const [tempCategories, setTempCategories] = useState(initialCategories);
445
421
  const [draggedIndex, setDraggedIndex] = useState(null);
446
422
  useEffect(() => {
@@ -552,11 +528,9 @@ const SettingsModal$1 = ({ isOpen, onClose, categories: initialCategories, categ
552
528
  React.createElement(Button, { onPress: handleCancel, variant: "tertiary", size: "s" }, "\uCDE8\uC18C"),
553
529
  React.createElement(Button, { onPress: handleSave, variant: "primary", size: "s" }, "\uC800\uC7A5")))));
554
530
  };
555
- SettingsModal$1.displayName = 'SettingsModal';
556
-
557
- SettingsModal;
531
+ SettingsModal.displayName = 'SettingsModal';
558
532
 
559
- const UpgradeBanner$1 = ({ onClick, isLoading = false, title, description, buttonText, t = (key) => key, className, }) => {
533
+ const UpgradeBanner = ({ onClick, isLoading = false, title, description, buttonText, t = (key) => key, className, }) => {
560
534
  return (React.createElement("div", { className: `designbase-figma-upgrade-banner ${className || ''}` },
561
535
  React.createElement("div", { className: "designbase-figma-upgrade-banner__content" },
562
536
  React.createElement("div", { className: "designbase-figma-upgrade-banner__text-wrap" },
@@ -564,9 +538,7 @@ const UpgradeBanner$1 = ({ onClick, isLoading = false, title, description, butto
564
538
  React.createElement("p", { className: "designbase-figma-upgrade-banner__text" }, description || t('bannerText') || '무제한 기능을 사용하고 더 많은 혜택을 누리세요.')),
565
539
  React.createElement(Button, { onClick: onClick, variant: "primary", size: "m", disabled: isLoading }, buttonText || t('upgradeNow') || '지금 업그레이드'))));
566
540
  };
567
- UpgradeBanner$1.displayName = 'UpgradeBanner';
568
-
569
- UpgradeBanner;
541
+ UpgradeBanner.displayName = 'UpgradeBanner';
570
542
 
571
- export { DonationBadge$1 as DonationBadge, Footer$1 as Footer, FormWithSubmit$1 as FormWithSubmit, InteractionFeedback$1 as InteractionFeedback, LanguageSelector$1 as LanguageSelector, LogoDropdown$1 as LogoDropdown, PageLicense$1 as PageLicense, PaymentBadge$1 as PaymentBadge, PaymentStatusSection$1 as PaymentStatusSection, PricingComparison$1 as PricingComparison, ProgressModal$1 as ProgressModal, ResizablePlugin$1 as ResizablePlugin, SettingsModal$1 as SettingsModal, UpgradeBanner$1 as UpgradeBanner };
543
+ export { DonationBadge, Footer, FormWithSubmit, InteractionFeedback, LanguageSelector, LogoDropdown, PageLicense, PaymentBadge, PaymentStatusSection, PricingComparison, ProgressModal, ResizablePlugin, SettingsModal, UpgradeBanner };
572
544
  //# sourceMappingURL=index.esm.js.map