@rpg-engine/long-bow 0.3.34 → 0.3.36

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.
@@ -1,6 +1,6 @@
1
- import React, { Component, useState, useEffect, useRef } from 'react';
1
+ import React, { Component, useState, useEffect, useRef, useMemo, Fragment } from 'react';
2
2
  import styled from 'styled-components';
3
- import { GRID_WIDTH, GRID_HEIGHT, ItemSubType, ItemContainerType, ItemType, ItemSocketEventsDisplayLabels, ActionsForInventory, ActionsForEquipmentSet, ActionsForLoot, ActionsForMapContainer, getItemTextureKeyPath, ItemSlotType, getSPForLevel, PeriodOfDay } from '@rpg-engine/shared';
3
+ import { GRID_WIDTH, GRID_HEIGHT, ItemSubType, ItemContainerType, ItemType, ItemSocketEventsDisplayLabels, ActionsForInventory, ActionsForEquipmentSet, ActionsForLoot, ActionsForMapContainer, ItemRarities, getItemTextureKeyPath, ItemSlotType, getSPForLevel, PeriodOfDay } from '@rpg-engine/shared';
4
4
  import dayjs from 'dayjs';
5
5
  import { ErrorBoundary as ErrorBoundary$1 } from 'react-error-boundary';
6
6
  import { RxPaperPlane } from 'react-icons/rx';
@@ -33779,6 +33779,7 @@ var ItemSlot = /*#__PURE__*/observer(function (_ref) {
33779
33779
  }
33780
33780
  };
33781
33781
  return React.createElement(Container$a, {
33782
+ item: item,
33782
33783
  className: "rpgui-icon empty-slot",
33783
33784
  onMouseUp: function onMouseUp() {
33784
33785
  var data = item ? item : null;
@@ -33886,10 +33887,33 @@ var ItemSlot = /*#__PURE__*/observer(function (_ref) {
33886
33887
  }
33887
33888
  }));
33888
33889
  });
33890
+ var rarityColor = function rarityColor(item) {
33891
+ switch (item == null ? void 0 : item.rarity) {
33892
+ case ItemRarities.Uncommon:
33893
+ return 'rgba(13, 193, 13, 0.6)';
33894
+ case ItemRarities.Rare:
33895
+ return 'rgba(8, 104, 187, 0.6)';
33896
+ case ItemRarities.Epic:
33897
+ return 'rgba(191, 0, 255, 0.6)';
33898
+ case ItemRarities.Legendary:
33899
+ return 'rgba(255, 191, 0,0.6)';
33900
+ default:
33901
+ return 'unset';
33902
+ }
33903
+ };
33889
33904
  var Container$a = /*#__PURE__*/styled.div.withConfig({
33890
33905
  displayName: "ItemSlot__Container",
33891
33906
  componentId: "sc-l2j5ef-0"
33892
- })(["margin:0.1rem;.sprite-from-atlas-img{position:relative;top:1.5rem;left:1.5rem;}position:relative;"]);
33907
+ })(["margin:0.1rem;.sprite-from-atlas-img{position:relative;top:1.5rem;left:1.5rem;border-color:", ";box-shadow:", " inset,", ";}position:relative;"], function (_ref2) {
33908
+ var item = _ref2.item;
33909
+ return rarityColor(item);
33910
+ }, function (_ref3) {
33911
+ var item = _ref3.item;
33912
+ return "0 0 5px 2px " + rarityColor(item);
33913
+ }, function (_ref4) {
33914
+ var item = _ref4.item;
33915
+ return "0 0 4px 3px " + rarityColor(item);
33916
+ });
33893
33917
  var ItemContainer = /*#__PURE__*/styled.div.withConfig({
33894
33918
  displayName: "ItemSlot__ItemContainer",
33895
33919
  componentId: "sc-l2j5ef-1"
@@ -35425,6 +35449,240 @@ var CloseButton$3 = /*#__PURE__*/styled.div.withConfig({
35425
35449
  componentId: "sc-1g0c67q-2"
35426
35450
  })(["position:absolute;top:2px;right:2px;color:white;z-index:22;font-size:1.1rem;"]);
35427
35451
 
35452
+ var QuickSpells = function QuickSpells(_ref) {
35453
+ var quickSpells = _ref.quickSpells,
35454
+ onSpellCast = _ref.onSpellCast,
35455
+ mana = _ref.mana,
35456
+ _ref$isBlockedCasting = _ref.isBlockedCastingByKeyboard,
35457
+ isBlockedCastingByKeyboard = _ref$isBlockedCasting === void 0 ? false : _ref$isBlockedCasting;
35458
+ useEffect(function () {
35459
+ var handleKeyDown = function handleKeyDown(e) {
35460
+ if (isBlockedCastingByKeyboard) return;
35461
+ var shortcutIndex = Number(e.key) - 1;
35462
+ if (shortcutIndex >= 0 && shortcutIndex <= 3) {
35463
+ var shortcut = quickSpells[shortcutIndex];
35464
+ if (shortcut != null && shortcut.key && mana >= (shortcut == null ? void 0 : shortcut.manaCost)) {
35465
+ onSpellCast(shortcut.key);
35466
+ }
35467
+ }
35468
+ };
35469
+ window.addEventListener('keydown', handleKeyDown);
35470
+ return function () {
35471
+ window.removeEventListener('keydown', handleKeyDown);
35472
+ };
35473
+ }, [quickSpells, isBlockedCastingByKeyboard]);
35474
+ return React.createElement(List, null, Array.from({
35475
+ length: 4
35476
+ }).map(function (_, i) {
35477
+ var _quickSpells$i, _quickSpells$i2, _quickSpells$i3, _quickSpells$i4, _quickSpells$i5;
35478
+ return React.createElement(SpellShortcut, {
35479
+ key: i,
35480
+ onClick: onSpellCast.bind(null, (_quickSpells$i = quickSpells[i]) == null ? void 0 : _quickSpells$i.key),
35481
+ disabled: mana < ((_quickSpells$i2 = quickSpells[i]) == null ? void 0 : _quickSpells$i2.manaCost)
35482
+ }, React.createElement("span", {
35483
+ className: "mana"
35484
+ }, ((_quickSpells$i3 = quickSpells[i]) == null ? void 0 : _quickSpells$i3.key) && ((_quickSpells$i4 = quickSpells[i]) == null ? void 0 : _quickSpells$i4.manaCost)), React.createElement("span", {
35485
+ className: "magicWords"
35486
+ }, (_quickSpells$i5 = quickSpells[i]) == null ? void 0 : _quickSpells$i5.magicWords.split(' ').map(function (word) {
35487
+ return word[0];
35488
+ })), React.createElement("span", {
35489
+ className: "keyboard"
35490
+ }, i + 1));
35491
+ }));
35492
+ };
35493
+ var SpellShortcut = /*#__PURE__*/styled.button.withConfig({
35494
+ displayName: "QuickSpells__SpellShortcut",
35495
+ componentId: "sc-41yq7s-0"
35496
+ })(["width:3rem;height:3rem;background-color:", ";border:2px solid ", ";border-radius:50%;text-transform:uppercase;font-size:0.7rem;font-weight:bold;display:flex;align-items:center;justify-content:center;position:relative;.mana{position:absolute;top:-5px;right:0;font-size:0.65rem;color:", ";}.magicWords{margin-top:4px;}.keyboard{position:absolute;bottom:-5px;left:0;font-size:0.65rem;color:", ";}&:hover,&:focus{background-color:", ";}&:active{background-color:", ";}&:disabled{opacity:0.5;}"], uiColors.lightGray, uiColors.darkGray, uiColors.blue, uiColors.yellow, uiColors.darkGray, uiColors.gray);
35497
+ var List = /*#__PURE__*/styled.p.withConfig({
35498
+ displayName: "QuickSpells__List",
35499
+ componentId: "sc-41yq7s-1"
35500
+ })(["width:100%;display:flex;align-items:center;justify-content:center;gap:0.5rem;box-sizing:border-box;margin:0 !important;"]);
35501
+
35502
+ var Spell = function Spell(_ref) {
35503
+ var spellKey = _ref.spellKey,
35504
+ name = _ref.name,
35505
+ description = _ref.description,
35506
+ magicWords = _ref.magicWords,
35507
+ manaCost = _ref.manaCost,
35508
+ charMana = _ref.charMana,
35509
+ charMagicLevel = _ref.charMagicLevel,
35510
+ onClick = _ref.onClick,
35511
+ isSettingShortcut = _ref.isSettingShortcut,
35512
+ minMagicLevelRequired = _ref.minMagicLevelRequired;
35513
+ var disabled = isSettingShortcut ? charMagicLevel < minMagicLevelRequired : manaCost > charMana || charMagicLevel < minMagicLevelRequired;
35514
+ return React.createElement(Container$i, {
35515
+ disabled: disabled,
35516
+ onClick: onClick == null ? void 0 : onClick.bind(null, spellKey),
35517
+ isSettingShortcut: isSettingShortcut && !disabled,
35518
+ className: "spell"
35519
+ }, disabled && React.createElement(Overlay, null, charMagicLevel < minMagicLevelRequired ? 'Low magic level' : manaCost > charMana && 'No mana'), React.createElement(SpellImage, null, magicWords.split(' ').map(function (word) {
35520
+ return word[0];
35521
+ })), React.createElement(Info, null, React.createElement(Title$5, null, React.createElement("span", null, name), React.createElement("span", {
35522
+ className: "spell"
35523
+ }, "(", magicWords, ")")), React.createElement(Description, null, description)), React.createElement(Divider, null), React.createElement(Cost, null, React.createElement("span", null, "Mana cost:"), React.createElement("span", {
35524
+ className: "mana"
35525
+ }, manaCost)));
35526
+ };
35527
+ var Container$i = /*#__PURE__*/styled.button.withConfig({
35528
+ displayName: "Spell__Container",
35529
+ componentId: "sc-j96fa2-0"
35530
+ })(["display:block;background:none;border:2px solid transparent;border-radius:1rem;width:100%;display:flex;height:5rem;gap:1rem;align-items:center;padding:0 1rem;text-align:left;position:relative;animation:", ";@keyframes border-color-change{0%{border-color:", ";}50%{border-color:transparent;}100%{border-color:", ";}}&:hover,&:focus{background-color:", ";}&:active{background:none;}"], function (_ref2) {
35531
+ var isSettingShortcut = _ref2.isSettingShortcut;
35532
+ return isSettingShortcut ? 'border-color-change 1s infinite' : 'none';
35533
+ }, uiColors.yellow, uiColors.yellow, uiColors.darkGray);
35534
+ var SpellImage = /*#__PURE__*/styled.div.withConfig({
35535
+ displayName: "Spell__SpellImage",
35536
+ componentId: "sc-j96fa2-1"
35537
+ })(["width:4rem;height:4rem;font-size:", ";font-weight:bold;background-color:", ";color:", ";display:flex;justify-content:center;align-items:center;text-transform:uppercase;"], uiFonts.size.xLarge, uiColors.darkGray, uiColors.lightGray);
35538
+ var Info = /*#__PURE__*/styled.span.withConfig({
35539
+ displayName: "Spell__Info",
35540
+ componentId: "sc-j96fa2-2"
35541
+ })(["width:0;flex:1;"]);
35542
+ var Title$5 = /*#__PURE__*/styled.p.withConfig({
35543
+ displayName: "Spell__Title",
35544
+ componentId: "sc-j96fa2-3"
35545
+ })(["display:flex;flex-wrap:wrap;align-items:center;margin-bottom:5px;margin:0;span{font-size:", " !important;font-weight:bold !important;color:", " !important;margin-right:0.5rem;}.spell{font-size:", " !important;font-weight:normal !important;color:", " !important;}"], uiFonts.size.medium, uiColors.yellow, uiFonts.size.small, uiColors.lightGray);
35546
+ var Description = /*#__PURE__*/styled.div.withConfig({
35547
+ displayName: "Spell__Description",
35548
+ componentId: "sc-j96fa2-4"
35549
+ })(["font-size:", " !important;line-height:1.1 !important;"], uiFonts.size.small);
35550
+ var Divider = /*#__PURE__*/styled.div.withConfig({
35551
+ displayName: "Spell__Divider",
35552
+ componentId: "sc-j96fa2-5"
35553
+ })(["width:1px;height:100%;margin:0 1rem;background-color:", ";"], uiColors.lightGray);
35554
+ var Cost = /*#__PURE__*/styled.p.withConfig({
35555
+ displayName: "Spell__Cost",
35556
+ componentId: "sc-j96fa2-6"
35557
+ })(["display:flex;align-items:center;flex-direction:column;gap:0.5rem;div{z-index:1;}.mana{position:relative;font-size:", ";font-weight:bold;z-index:1;&::after{position:absolute;content:'';top:0;left:0;background-color:", ";width:100%;height:100%;border-radius:50%;transform:scale(1.8);filter:blur(10px);z-index:-1;}}"], uiFonts.size.medium, uiColors.blue);
35558
+ var Overlay = /*#__PURE__*/styled.p.withConfig({
35559
+ displayName: "Spell__Overlay",
35560
+ componentId: "sc-j96fa2-7"
35561
+ })(["margin:0 !important;position:absolute;top:0;left:0;width:100%;height:100%;border-radius:1rem;display:flex;justify-content:center;align-items:center;color:", ";font-size:", " !important;font-weight:bold;z-index:10;background-color:rgba(0 0 0 / 0.2);"], uiColors.yellow, uiFonts.size.large);
35562
+
35563
+ var SpellbookShortcuts = function SpellbookShortcuts(_ref) {
35564
+ var setSettingShortcutIndex = _ref.setSettingShortcutIndex,
35565
+ settingShortcutIndex = _ref.settingShortcutIndex,
35566
+ shortcuts = _ref.shortcuts,
35567
+ removeShortcut = _ref.removeShortcut;
35568
+ return React.createElement(List$1, {
35569
+ id: "shortcuts_list"
35570
+ }, "Spells shortcuts:", Array.from({
35571
+ length: 4
35572
+ }).map(function (_, i) {
35573
+ var _shortcuts$i2;
35574
+ return React.createElement(SpellShortcut$1, {
35575
+ key: i,
35576
+ onClick: function onClick() {
35577
+ var _shortcuts$i;
35578
+ removeShortcut(i);
35579
+ if (!((_shortcuts$i = shortcuts[i]) != null && _shortcuts$i.key)) setSettingShortcutIndex(i);
35580
+ },
35581
+ disabled: settingShortcutIndex !== -1 && settingShortcutIndex !== i,
35582
+ isBeingSet: settingShortcutIndex === i
35583
+ }, React.createElement("span", null, (_shortcuts$i2 = shortcuts[i]) == null ? void 0 : _shortcuts$i2.magicWords.split(' ').map(function (word) {
35584
+ return word[0];
35585
+ })));
35586
+ }));
35587
+ };
35588
+ var SpellShortcut$1 = /*#__PURE__*/styled.button.withConfig({
35589
+ displayName: "SpellbookShortcuts__SpellShortcut",
35590
+ componentId: "sc-fr4a0d-0"
35591
+ })(["width:2.6rem;height:2.6rem;background-color:", ";border:2px solid ", ";border-radius:50%;text-transform:uppercase;font-size:0.7rem;font-weight:bold;display:flex;align-items:center;justify-content:center;span{margin-top:4px;}&:hover,&:focus{background-color:", ";}&:active{background-color:", ";}&:disabled{opacity:0.5;}"], uiColors.lightGray, function (_ref2) {
35592
+ var isBeingSet = _ref2.isBeingSet;
35593
+ return isBeingSet ? uiColors.yellow : uiColors.darkGray;
35594
+ }, uiColors.darkGray, uiColors.gray);
35595
+ var List$1 = /*#__PURE__*/styled.p.withConfig({
35596
+ displayName: "SpellbookShortcuts__List",
35597
+ componentId: "sc-fr4a0d-1"
35598
+ })(["width:100%;display:flex;align-items:center;justify-content:flex-end;gap:0.5rem;padding:0.5rem;box-sizing:border-box;margin:0 !important;"]);
35599
+
35600
+ var Spellbook = function Spellbook(_ref) {
35601
+ var onClose = _ref.onClose,
35602
+ onInputFocus = _ref.onInputFocus,
35603
+ onInputBlur = _ref.onInputBlur,
35604
+ spells = _ref.spells,
35605
+ magicLevel = _ref.magicLevel,
35606
+ mana = _ref.mana,
35607
+ onSpellClick = _ref.onSpellClick,
35608
+ setSpellShortcut = _ref.setSpellShortcut,
35609
+ spellShortcuts = _ref.spellShortcuts,
35610
+ removeSpellShortcut = _ref.removeSpellShortcut;
35611
+ var _useState = useState(''),
35612
+ search = _useState[0],
35613
+ setSearch = _useState[1];
35614
+ var _useState2 = useState(-1),
35615
+ settingShortcutIndex = _useState2[0],
35616
+ setSettingShortcutIndex = _useState2[1];
35617
+ useEffect(function () {
35618
+ var handleEscapeClose = function handleEscapeClose(e) {
35619
+ if (e.key === 'Escape') {
35620
+ onClose == null ? void 0 : onClose();
35621
+ }
35622
+ };
35623
+ document.addEventListener('keydown', handleEscapeClose);
35624
+ return function () {
35625
+ document.removeEventListener('keydown', handleEscapeClose);
35626
+ };
35627
+ }, [onClose]);
35628
+ var spellsToDisplay = useMemo(function () {
35629
+ return spells.sort(function (a, b) {
35630
+ if (a.minMagicLevelRequired > b.minMagicLevelRequired) return 1;
35631
+ if (a.minMagicLevelRequired < b.minMagicLevelRequired) return -1;
35632
+ return 0;
35633
+ }).filter(function (spell) {
35634
+ return spell.name.toLocaleLowerCase().includes(search.toLocaleLowerCase()) || spell.magicWords.toLocaleLowerCase().includes(search.toLocaleLowerCase());
35635
+ });
35636
+ }, [search, spells]);
35637
+ var setShortcut = function setShortcut(spellKey) {
35638
+ setSpellShortcut == null ? void 0 : setSpellShortcut(spellKey, settingShortcutIndex);
35639
+ setSettingShortcutIndex(-1);
35640
+ };
35641
+ return React.createElement(DraggableContainer, {
35642
+ type: RPGUIContainerTypes.Framed,
35643
+ onCloseButton: onClose,
35644
+ width: "inherit",
35645
+ height: "inherit",
35646
+ cancelDrag: "#spellbook-search, #shortcuts_list, .spell"
35647
+ }, React.createElement(Container$j, null, React.createElement(Title$6, null, "Learned Spells"), React.createElement(SpellbookShortcuts, {
35648
+ setSettingShortcutIndex: setSettingShortcutIndex,
35649
+ settingShortcutIndex: settingShortcutIndex,
35650
+ shortcuts: spellShortcuts,
35651
+ removeShortcut: removeSpellShortcut
35652
+ }), React.createElement(Input, {
35653
+ placeholder: "Search for spell",
35654
+ value: search,
35655
+ onChange: function onChange(e) {
35656
+ return setSearch(e.target.value);
35657
+ },
35658
+ onFocus: onInputFocus,
35659
+ onBlur: onInputBlur,
35660
+ id: "spellbook-search"
35661
+ }), React.createElement(SpellList, null, spellsToDisplay.map(function (spell) {
35662
+ return React.createElement(Fragment, {
35663
+ key: spell.key
35664
+ }, React.createElement(Spell, Object.assign({
35665
+ charMana: mana,
35666
+ charMagicLevel: magicLevel,
35667
+ onClick: settingShortcutIndex !== -1 ? setShortcut : onSpellClick,
35668
+ spellKey: spell.key,
35669
+ isSettingShortcut: settingShortcutIndex !== -1
35670
+ }, spell)));
35671
+ }))));
35672
+ };
35673
+ var Title$6 = /*#__PURE__*/styled.h1.withConfig({
35674
+ displayName: "Spellbook__Title",
35675
+ componentId: "sc-r02nfq-0"
35676
+ })(["font-size:", " !important;margin-bottom:0 !important;"], uiFonts.size.large);
35677
+ var Container$j = /*#__PURE__*/styled.div.withConfig({
35678
+ displayName: "Spellbook__Container",
35679
+ componentId: "sc-r02nfq-1"
35680
+ })(["width:100%;height:100%;color:white;display:flex;flex-direction:column;"]);
35681
+ var SpellList = /*#__PURE__*/styled.div.withConfig({
35682
+ displayName: "Spellbook__SpellList",
35683
+ componentId: "sc-r02nfq-2"
35684
+ })(["width:100%;min-height:0;flex:1;overflow-y:auto;display:flex;flex-direction:column;gap:1.5rem;margin-top:1rem;"]);
35685
+
35428
35686
  var TextArea = function TextArea(_ref) {
35429
35687
  var props = _extends({}, (_objectDestructuringEmpty(_ref), _ref));
35430
35688
  return React.createElement("textarea", Object.assign({}, props));
@@ -35625,7 +35883,7 @@ var TradingMenu = function TradingMenu(_ref) {
35625
35883
  style: {
35626
35884
  width: '100%'
35627
35885
  }
35628
- }, React.createElement(Title$5, null, Capitalize(type), " Menu"), React.createElement("hr", {
35886
+ }, React.createElement(Title$7, null, Capitalize(type), " Menu"), React.createElement("hr", {
35629
35887
  className: "golden"
35630
35888
  })), React.createElement(TradingComponentScrollWrapper, null, traderItems.map(function (tradeItem, index) {
35631
35889
  var _qtyMap$get;
@@ -35651,7 +35909,7 @@ var TradingMenu = function TradingMenu(_ref) {
35651
35909
  }
35652
35910
  }, "Cancel"))));
35653
35911
  };
35654
- var Title$5 = /*#__PURE__*/styled.h1.withConfig({
35912
+ var Title$7 = /*#__PURE__*/styled.h1.withConfig({
35655
35913
  displayName: "TradingMenu__Title",
35656
35914
  componentId: "sc-1wjsz1l-0"
35657
35915
  })(["z-index:22;font-size:0.6rem;color:yellow !important;"]);
@@ -35685,16 +35943,16 @@ var Truncate = function Truncate(_ref) {
35685
35943
  var _ref$maxLines = _ref.maxLines,
35686
35944
  maxLines = _ref$maxLines === void 0 ? 1 : _ref$maxLines,
35687
35945
  children = _ref.children;
35688
- return React.createElement(Container$i, {
35946
+ return React.createElement(Container$k, {
35689
35947
  maxLines: maxLines
35690
35948
  }, children);
35691
35949
  };
35692
- var Container$i = /*#__PURE__*/styled.div.withConfig({
35950
+ var Container$k = /*#__PURE__*/styled.div.withConfig({
35693
35951
  displayName: "Truncate__Container",
35694
35952
  componentId: "sc-6x00qb-0"
35695
35953
  })(["display:-webkit-box;max-width:100%;max-height:100%;-webkit-line-clamp:", ";-webkit-box-orient:vertical;overflow:hidden;"], function (props) {
35696
35954
  return props.maxLines;
35697
35955
  });
35698
35956
 
35699
- export { Button, ButtonTypes, CharacterSelection, Chat, ChatDeprecated, CheckButton, CraftBook, DraggableContainer, Dropdown, DropdownSelectorContainer, DynamicText, EquipmentSet, ErrorBoundary, HistoryDialog, ImgSide, Input, InputRadio, ItemContainer$1 as ItemContainer, ItemSelector, ItemSlot, ListMenu, NPCDialog, NPCDialogType, NPCMultiDialog, ProgressBar, PropertySelect, QuestInfo, QuestList, QuestionDialog, RPGUIContainer, RPGUIContainerTypes, RPGUIRoot, RangeSlider, RangeSliderType, SkillProgressBar, SkillsContainer, SpriteFromAtlas, TextArea, TimeWidget, TradingMenu, Truncate, _RPGUI, useEventListener };
35957
+ export { Button, ButtonTypes, CharacterSelection, Chat, ChatDeprecated, CheckButton, CraftBook, DraggableContainer, Dropdown, DropdownSelectorContainer, DynamicText, EquipmentSet, ErrorBoundary, HistoryDialog, ImgSide, Input, InputRadio, ItemContainer$1 as ItemContainer, ItemSelector, ItemSlot, ListMenu, NPCDialog, NPCDialogType, NPCMultiDialog, ProgressBar, PropertySelect, QuestInfo, QuestList, QuestionDialog, QuickSpells, RPGUIContainer, RPGUIContainerTypes, RPGUIRoot, RangeSlider, RangeSliderType, SkillProgressBar, SkillsContainer, Spellbook, SpriteFromAtlas, TextArea, TimeWidget, TradingMenu, Truncate, _RPGUI, useEventListener };
35700
35958
  //# sourceMappingURL=long-bow.esm.js.map