From eaa0a9b8153666984c5de2084e8adc425112fac0 Mon Sep 17 00:00:00 2001 From: Matthew Kalinin Date: Tue, 19 Mar 2024 13:16:48 +0300 Subject: [PATCH 01/48] Added getting information about icons from Figma --- tools/figma/figma-api.ts | 32 +++++++++++++++++ tools/figma/get-components.ts | 23 ++++++++++++ tools/figma/get-icon-descriptions.ts | 54 ++++++++++++++++++++++++++++ tools/sync-icons.ts | 15 ++++++++ 4 files changed, 124 insertions(+) create mode 100644 tools/figma/figma-api.ts create mode 100644 tools/figma/get-components.ts create mode 100644 tools/figma/get-icon-descriptions.ts create mode 100644 tools/sync-icons.ts diff --git a/tools/figma/figma-api.ts b/tools/figma/figma-api.ts new file mode 100644 index 0000000..ee20505 --- /dev/null +++ b/tools/figma/figma-api.ts @@ -0,0 +1,32 @@ +import {Api as FigmaApi} from 'figma-api'; +import {getComponents} from './get-components'; +import {getIconDescriptions} from './get-icon-descriptions'; + +/** The name of the canvas in the file from which the icons will be loaded */ +const CANVAS_NAME = 'glyphs'; +const AVAILABLE_SIZES = [14, 24]; + +export async function fetchFigmaIcons() { + const personalAccessToken: string | undefined = process.env.FIGMA_API_TOKEN; + const fileId: string | undefined = process.env.FIGMA_FILE_ID; + + if (!personalAccessToken) { + throw new Error('No Figma access token found in environment variable FIGMA_API_TOKEN'); + } + if (!fileId) { + throw new Error('No Figma file id found in environment variable FIGMA_FILE_ID'); + } + + const api = new FigmaApi({personalAccessToken}); + + const components = await getComponents({fileId, canvasName: CANVAS_NAME}, api); + console.info('Success get components'); + // components.forEach((c) => console.log(c.name)); + const iconDescriptions = getIconDescriptions(components, AVAILABLE_SIZES); + console.info('Success get icon descriptions'); + iconDescriptions.forEach((i) => console.log(i.name)); + + // const images = await getImages(iconDescriptions, fileId, api); + // console.info('Success load icon images'); + // console.log(localeMatchimages); +} diff --git a/tools/figma/get-components.ts b/tools/figma/get-components.ts new file mode 100644 index 0000000..63a397d --- /dev/null +++ b/tools/figma/get-components.ts @@ -0,0 +1,23 @@ +import {Api as FigmaApi, Node, isNodeType} from 'figma-api'; + +type ComponentOptions = { + fileId: string; + canvasName: string; +}; + +export const getComponents = async (options: ComponentOptions, api: FigmaApi): Promise[]> => { + const file = await api.getFile(options.fileId); + + const canvas = file.document.children.find((child) => child.name === options.canvasName) as Node<'CANVAS'>; + if (!canvas) { + throw new Error(`Canvas "${options.canvasName}" not found!`); + } + + return canvas.children.reduce((components, child) => { + if (!isNodeType(child, 'GROUP')) { + return components; + } + const newComponents = child.children.filter((child) => isNodeType(child, 'INSTANCE')) as Node<'COMPONENT'>[]; + return components.concat(newComponents); + }, [] as Node<'COMPONENT'>[]); +}; diff --git a/tools/figma/get-icon-descriptions.ts b/tools/figma/get-icon-descriptions.ts new file mode 100644 index 0000000..40c879a --- /dev/null +++ b/tools/figma/get-icon-descriptions.ts @@ -0,0 +1,54 @@ +import {ImageType, type Node} from 'figma-api'; + +const REGION_CODE_REGEXP = /_([a-z]{2})_/; +const SIZE_REGEXP = /_([0-9]{2})/; +const INTL_REGION_CODES = new Intl.DisplayNames(['en', 'ru'], {type: 'region'}); + +export type IconDescription = { + componentId: string; + name: string; + exportFormat: 'png' | 'svg'; +}; + +export const getIconDescriptions = (components: Node<'COMPONENT'>[], availableSizes: number[]): IconDescription[] => { + return components + .filter((component) => { + // the component should not have a regional code + if (componentNameHasRegionCode(component.name)) { + return false; + } + // the component must be of allowed size and square + if (!componentAvailableSize(component.name, availableSizes)) { + return false; + } + const {height, width} = component.absoluteBoundingBox; + return height === width; + }) + .map((component) => { + const isPng = + component.exportSettings?.length && + component.exportSettings.every(({format}) => format === ImageType.PNG); + const exportFormat = isPng ? 'png' : 'svg'; + return {componentId: component.id, name: component.name, exportFormat}; + }); +}; + +const componentNameHasRegionCode = (componentName: string): boolean => { + const regionMatch = REGION_CODE_REGEXP.exec(componentName); + if (regionMatch === null) { + return false; + } + const [, regionCode] = regionMatch; + const uppercaseCode = regionCode.toUpperCase(); + return INTL_REGION_CODES.of(uppercaseCode) !== uppercaseCode; +}; + +const componentAvailableSize = (componentName: string, availableSizes: number[]): boolean => { + const sizeMatch = SIZE_REGEXP.exec(componentName); + if (sizeMatch === null) { + return false; + } + const [, rawSize] = sizeMatch; + const size = rawSize ? Number(rawSize) : undefined; + return availableSizes.includes(size as number); +}; diff --git a/tools/sync-icons.ts b/tools/sync-icons.ts new file mode 100644 index 0000000..ae238b6 --- /dev/null +++ b/tools/sync-icons.ts @@ -0,0 +1,15 @@ +import {fetchFigmaIcons} from './figma/figma-api'; + +async function main() { + try { + console.info('Start update figma icons'); + await fetchFigmaIcons(); + console.info('Update figma icons succeed!'); + } catch (error) { + console.error(error.message || error.toString()); + } +} + +main().catch(() => { + process.exit(1); +}); From b1fa646e99ecced9de95554e6ee3a20fdfd2ee87 Mon Sep 17 00:00:00 2001 From: Matthew Kalinin Date: Tue, 19 Mar 2024 14:22:54 +0300 Subject: [PATCH 02/48] Added getting links to icons --- tools/figma/{figma-api.ts => fetch-icons.ts} | 11 ++-- tools/figma/get-icon-descriptions.ts | 4 +- tools/figma/get-image-links.ts | 66 ++++++++++++++++++++ tools/make-chunks.ts | 10 +++ tools/sync-icons.ts | 2 +- 5 files changed, 86 insertions(+), 7 deletions(-) rename tools/figma/{figma-api.ts => fetch-icons.ts} (79%) create mode 100644 tools/figma/get-image-links.ts create mode 100644 tools/make-chunks.ts diff --git a/tools/figma/figma-api.ts b/tools/figma/fetch-icons.ts similarity index 79% rename from tools/figma/figma-api.ts rename to tools/figma/fetch-icons.ts index ee20505..cebac51 100644 --- a/tools/figma/figma-api.ts +++ b/tools/figma/fetch-icons.ts @@ -1,6 +1,7 @@ import {Api as FigmaApi} from 'figma-api'; import {getComponents} from './get-components'; import {getIconDescriptions} from './get-icon-descriptions'; +import {getImageLinks} from './get-image-links'; /** The name of the canvas in the file from which the icons will be loaded */ const CANVAS_NAME = 'glyphs'; @@ -21,12 +22,12 @@ export async function fetchFigmaIcons() { const components = await getComponents({fileId, canvasName: CANVAS_NAME}, api); console.info('Success get components'); - // components.forEach((c) => console.log(c.name)); + const iconDescriptions = getIconDescriptions(components, AVAILABLE_SIZES); console.info('Success get icon descriptions'); - iconDescriptions.forEach((i) => console.log(i.name)); - // const images = await getImages(iconDescriptions, fileId, api); - // console.info('Success load icon images'); - // console.log(localeMatchimages); + const imageLinks = await getImageLinks(iconDescriptions, fileId, api); + console.info('Success load icon images'); + + return imageLinks; } diff --git a/tools/figma/get-icon-descriptions.ts b/tools/figma/get-icon-descriptions.ts index 40c879a..8a8be10 100644 --- a/tools/figma/get-icon-descriptions.ts +++ b/tools/figma/get-icon-descriptions.ts @@ -4,10 +4,12 @@ const REGION_CODE_REGEXP = /_([a-z]{2})_/; const SIZE_REGEXP = /_([0-9]{2})/; const INTL_REGION_CODES = new Intl.DisplayNames(['en', 'ru'], {type: 'region'}); +export type ExportFormat = 'png' | 'svg'; + export type IconDescription = { componentId: string; name: string; - exportFormat: 'png' | 'svg'; + exportFormat: ExportFormat; }; export const getIconDescriptions = (components: Node<'COMPONENT'>[], availableSizes: number[]): IconDescription[] => { diff --git a/tools/figma/get-image-links.ts b/tools/figma/get-image-links.ts new file mode 100644 index 0000000..1283138 --- /dev/null +++ b/tools/figma/get-image-links.ts @@ -0,0 +1,66 @@ +import {Api as FigmaApi} from 'figma-api'; +import {GetImageResult} from 'figma-api/lib/api-types'; +import {ExportFormat, IconDescription} from './get-icon-descriptions'; +import {makeChunks} from '../make-chunks'; + +const IMAGE_SCALE = 1; +const ICONS_PER_CHUNK = 100; + +export type IconDescriptionWithLink = IconDescription & { + link: string; +}; +type SeparatedIcons = { + [key in ExportFormat]: IconDescription[]; +}; + +export const getImageLinks = async ( + descriptions: IconDescription[], + fileId: string, + api: FigmaApi +): Promise => { + const chunks = separateIntoChunks(descriptions); + const linkChunks = await Promise.all( + chunks.map((chunk) => + api.getImage(fileId, { + ids: chunk.map((icon) => icon.componentId).join(','), + scale: IMAGE_SCALE, + format: chunk[0].exportFormat + }) + ) + ); + const links = linkChunks.reduce( + (memo, chunk) => { + if (chunk.err) { + memo.err = chunk.err; + } + if (chunk.images) { + memo.images = {...memo.images, ...chunk.images}; + } + return memo; + }, + {images: {}} + ); + + if (links.err) { + throw new Error(`Error while loading links: ${links.err}`); + } + + return descriptions.reduce((descriptionsWithLink, description) => { + const link = links.images[description.componentId]; + if (link) { + return descriptionsWithLink.concat({...description, link}); + } + return descriptionsWithLink; + }, []); +}; + +const separateIntoChunks = (descriptions: IconDescription[]): IconDescription[][] => { + const separatedIcons = descriptions.reduce( + (result, icon) => { + result[icon.exportFormat].push(icon); + return result; + }, + {svg: [], png: []} + ); + return [...makeChunks(separatedIcons.svg, ICONS_PER_CHUNK), ...makeChunks(separatedIcons.png, ICONS_PER_CHUNK)]; +}; diff --git a/tools/make-chunks.ts b/tools/make-chunks.ts new file mode 100644 index 0000000..5debb13 --- /dev/null +++ b/tools/make-chunks.ts @@ -0,0 +1,10 @@ +export const makeChunks = (input: T[], perChunk: number): T[][] => { + return input.reduce((chunks, item, index) => { + const chunkIndex = Math.floor(index / perChunk); + if (!chunks[chunkIndex]) { + chunks[chunkIndex] = []; + } + chunks[chunkIndex].push(item); + return chunks; + }, []); +}; diff --git a/tools/sync-icons.ts b/tools/sync-icons.ts index ae238b6..8d95dfe 100644 --- a/tools/sync-icons.ts +++ b/tools/sync-icons.ts @@ -1,4 +1,4 @@ -import {fetchFigmaIcons} from './figma/figma-api'; +import {fetchFigmaIcons} from './figma/fetch-icons'; async function main() { try { From 65fffaa34e9fabe884c5f8b032d6c4cd24f501f7 Mon Sep 17 00:00:00 2001 From: Matthew Kalinin Date: Tue, 19 Mar 2024 14:27:17 +0300 Subject: [PATCH 03/48] restructuring inside the tools folder --- tools/figma/get-image-links.ts | 2 +- tools/sync-icons.ts | 15 --------------- tools/{ => utils}/make-chunks.ts | 0 3 files changed, 1 insertion(+), 16 deletions(-) delete mode 100644 tools/sync-icons.ts rename tools/{ => utils}/make-chunks.ts (100%) diff --git a/tools/figma/get-image-links.ts b/tools/figma/get-image-links.ts index 1283138..3cc2fdd 100644 --- a/tools/figma/get-image-links.ts +++ b/tools/figma/get-image-links.ts @@ -1,7 +1,7 @@ import {Api as FigmaApi} from 'figma-api'; import {GetImageResult} from 'figma-api/lib/api-types'; +import {makeChunks} from '../utils/make-chunks'; import {ExportFormat, IconDescription} from './get-icon-descriptions'; -import {makeChunks} from '../make-chunks'; const IMAGE_SCALE = 1; const ICONS_PER_CHUNK = 100; diff --git a/tools/sync-icons.ts b/tools/sync-icons.ts deleted file mode 100644 index 8d95dfe..0000000 --- a/tools/sync-icons.ts +++ /dev/null @@ -1,15 +0,0 @@ -import {fetchFigmaIcons} from './figma/fetch-icons'; - -async function main() { - try { - console.info('Start update figma icons'); - await fetchFigmaIcons(); - console.info('Update figma icons succeed!'); - } catch (error) { - console.error(error.message || error.toString()); - } -} - -main().catch(() => { - process.exit(1); -}); diff --git a/tools/make-chunks.ts b/tools/utils/make-chunks.ts similarity index 100% rename from tools/make-chunks.ts rename to tools/utils/make-chunks.ts From 2fc239a01476b12f318e15aa8ce53a604ae70389 Mon Sep 17 00:00:00 2001 From: Matthew Kalinin Date: Tue, 19 Mar 2024 16:18:16 +0300 Subject: [PATCH 04/48] Fetching components only with export settings --- tools/figma/get-icon-descriptions.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tools/figma/get-icon-descriptions.ts b/tools/figma/get-icon-descriptions.ts index 8a8be10..850746d 100644 --- a/tools/figma/get-icon-descriptions.ts +++ b/tools/figma/get-icon-descriptions.ts @@ -15,6 +15,10 @@ export type IconDescription = { export const getIconDescriptions = (components: Node<'COMPONENT'>[], availableSizes: number[]): IconDescription[] => { return components .filter((component) => { + // the component must have export settings + if (component.exportSettings === undefined || component.exportSettings.length === 0) { + return false; + } // the component should not have a regional code if (componentNameHasRegionCode(component.name)) { return false; @@ -27,9 +31,7 @@ export const getIconDescriptions = (components: Node<'COMPONENT'>[], availableSi return height === width; }) .map((component) => { - const isPng = - component.exportSettings?.length && - component.exportSettings.every(({format}) => format === ImageType.PNG); + const isPng = component.exportSettings.every(({format}) => format === ImageType.PNG); const exportFormat = isPng ? 'png' : 'svg'; return {componentId: component.id, name: component.name, exportFormat}; }); From 72848f51bb4fab68472868156edf3dfe8f317293 Mon Sep 17 00:00:00 2001 From: Matthew Kalinin Date: Fri, 29 Mar 2024 18:42:09 +0300 Subject: [PATCH 05/48] load icon in local folder --- tools/figma/fetch-icons.ts | 11 ++------ tools/figma/get-image-files.ts | 46 ++++++++++++++++++++++++++++++++++ tools/figma/get-image-links.ts | 1 + tools/figma/local.ts | 45 +++++++++++++++++++++++++++++++++ tools/figma/update-files.ts | 17 +++++++++++++ 5 files changed, 111 insertions(+), 9 deletions(-) create mode 100644 tools/figma/get-image-files.ts create mode 100644 tools/figma/local.ts create mode 100644 tools/figma/update-files.ts diff --git a/tools/figma/fetch-icons.ts b/tools/figma/fetch-icons.ts index cebac51..f43a67d 100644 --- a/tools/figma/fetch-icons.ts +++ b/tools/figma/fetch-icons.ts @@ -1,13 +1,13 @@ import {Api as FigmaApi} from 'figma-api'; import {getComponents} from './get-components'; import {getIconDescriptions} from './get-icon-descriptions'; -import {getImageLinks} from './get-image-links'; +import {IconDescriptionWithLink, getImageLinks} from './get-image-links'; /** The name of the canvas in the file from which the icons will be loaded */ const CANVAS_NAME = 'glyphs'; const AVAILABLE_SIZES = [14, 24]; -export async function fetchFigmaIcons() { +export async function fetchFigmaIcons(): Promise { const personalAccessToken: string | undefined = process.env.FIGMA_API_TOKEN; const fileId: string | undefined = process.env.FIGMA_FILE_ID; @@ -19,15 +19,8 @@ export async function fetchFigmaIcons() { } const api = new FigmaApi({personalAccessToken}); - const components = await getComponents({fileId, canvasName: CANVAS_NAME}, api); - console.info('Success get components'); - const iconDescriptions = getIconDescriptions(components, AVAILABLE_SIZES); - console.info('Success get icon descriptions'); - const imageLinks = await getImageLinks(iconDescriptions, fileId, api); - console.info('Success load icon images'); - return imageLinks; } diff --git a/tools/figma/get-image-files.ts b/tools/figma/get-image-files.ts new file mode 100644 index 0000000..f95022d --- /dev/null +++ b/tools/figma/get-image-files.ts @@ -0,0 +1,46 @@ +import got from 'got'; +import {IconDescription} from './get-icon-descriptions'; +import {IconDescriptionWithLink} from './get-image-links'; + +const MAX_RETRIES = 20; +/** Our designer marks these icons with this color that do not need to be worked on yet. */ +const ERROR_COLOR_REGEXP = /fill="#C90D0D"/; +/** Default icon color from Figma */ +const FILL_COLOR_REGEXP = /fill="black"/g; + +export type IconDescriptionWithData = IconDescription & { + data: Buffer; +}; + +export const downloadAndTransform = async (icons: IconDescriptionWithLink[]): Promise => { + const iconsWithData = await getImageFiles(icons); + return iconsWithData + .filter((icon) => (icon.exportFormat !== 'svg' ? true : !icon.data.toString().match(ERROR_COLOR_REGEXP))) + .map((icon) => + icon.exportFormat !== 'svg' + ? icon + : {...icon, data: Buffer.from(icon.data.toString().replace(FILL_COLOR_REGEXP, 'fill="currentColor"'))} + ); +}; + +const getImageFiles = async (icons: IconDescriptionWithLink[]): Promise => { + return Promise.all( + icons.map(async (icon) => { + try { + const file = await fetchFile(icon.link); + return {...file, name: icon.name, exportFormat: icon.exportFormat, componentId: icon.componentId}; + } catch (e) { + e.message = `${icon.name}: ${e.message}`; + throw e; + } + }) + ); +}; + +const fetchFile = async (url: string) => { + const response = await got(url, {timeout: 60 * 1000, retry: MAX_RETRIES}); + if (!response.body) { + throw new Error('No response body.'); + } + return {data: response.body}; +}; diff --git a/tools/figma/get-image-links.ts b/tools/figma/get-image-links.ts index 3cc2fdd..cd7373a 100644 --- a/tools/figma/get-image-links.ts +++ b/tools/figma/get-image-links.ts @@ -7,6 +7,7 @@ const IMAGE_SCALE = 1; const ICONS_PER_CHUNK = 100; export type IconDescriptionWithLink = IconDescription & { + componentId: string; link: string; }; type SeparatedIcons = { diff --git a/tools/figma/local.ts b/tools/figma/local.ts new file mode 100644 index 0000000..24ff972 --- /dev/null +++ b/tools/figma/local.ts @@ -0,0 +1,45 @@ +import fs from 'fs/promises'; +import path from 'path'; +import {ExportFormat} from './get-icon-descriptions'; +import {IconDescriptionWithData} from './get-image-files'; + +const BASE_DIR = path.join(__dirname, '../../'); +const ICONS_PATH = path.join(BASE_DIR, 'static/icons'); +const TYPES_PATH = path.join(BASE_DIR, 'src/icons/types'); + +export type FileDescription = { + name: string; + data: Buffer; + exportFormat: ExportFormat; +}; + +export const getFiles = async (): Promise => { + console.info('Getting static resources dir'); + const currentFilenames = await fs.readdir(ICONS_PATH); + const descriptions: FileDescription[] = await Promise.all( + currentFilenames.map(async (filename) => { + const fileExtension = path.parse(filename).ext.slice(1); + if (fileExtension !== 'svg' && fileExtension !== 'png') { + throw new Error('Unknown file extension.'); + } + const cleanFilename = path.parse(filename).name; + const data = await fs.readFile(path.join(ICONS_PATH, filename)); + return { + name: cleanFilename, + data, + exportFormat: fileExtension + }; + }) + ); + console.info(`${descriptions.length} current icons files fetched`); + return descriptions; +}; + +export const updateLocalFiles = async (icons: IconDescriptionWithData[]) => { + await Promise.all( + icons.map((icon) => { + const filePath = path.join(ICONS_PATH, `${icon.name}.${icon.exportFormat}`); + return fs.writeFile(filePath, icon.data, icon.exportFormat === 'svg' ? 'utf-8' : null); + }) + ); +}; diff --git a/tools/figma/update-files.ts b/tools/figma/update-files.ts new file mode 100644 index 0000000..ec2a7ce --- /dev/null +++ b/tools/figma/update-files.ts @@ -0,0 +1,17 @@ +import {differenceBy, intersectionBy} from 'lodash'; +import {fetchFigmaIcons} from '../figma/fetch-icons'; +import {getFiles, updateLocalFiles} from '../figma/local'; +import {downloadAndTransform} from './get-image-files'; + +export const updateFiles = async () => { + const localIcons = await getFiles(); + + const figmaIcons = await fetchFigmaIcons(); + + const existingLocalIcons = intersectionBy(localIcons, figmaIcons, (d) => d.name); + const iconsToDelete = differenceBy(localIcons, existingLocalIcons, (d) => d.name); + + const iconsWithData = await downloadAndTransform(figmaIcons); + + await updateLocalFiles(iconsWithData); +}; From b2d08a0121950bc332bb4dd044b1a39c35e65df8 Mon Sep 17 00:00:00 2001 From: Matthew Kalinin Date: Mon, 1 Apr 2024 10:52:37 +0300 Subject: [PATCH 06/48] uploading icons from Figma to a local folder --- tools/figma/local.ts | 17 +++++++++++++---- tools/figma/update-files.ts | 17 ----------------- tools/figma/update-icons.ts | 17 +++++++++++++++++ 3 files changed, 30 insertions(+), 21 deletions(-) delete mode 100644 tools/figma/update-files.ts create mode 100644 tools/figma/update-icons.ts diff --git a/tools/figma/local.ts b/tools/figma/local.ts index 24ff972..f10e23f 100644 --- a/tools/figma/local.ts +++ b/tools/figma/local.ts @@ -5,18 +5,18 @@ import {IconDescriptionWithData} from './get-image-files'; const BASE_DIR = path.join(__dirname, '../../'); const ICONS_PATH = path.join(BASE_DIR, 'static/icons'); -const TYPES_PATH = path.join(BASE_DIR, 'src/icons/types'); +// const TYPES_PATH = path.join(BASE_DIR, 'src/icons/types'); -export type FileDescription = { +export type LocalIconDescription = { name: string; data: Buffer; exportFormat: ExportFormat; }; -export const getFiles = async (): Promise => { +export const getLocalIcons = async (): Promise => { console.info('Getting static resources dir'); const currentFilenames = await fs.readdir(ICONS_PATH); - const descriptions: FileDescription[] = await Promise.all( + const descriptions: LocalIconDescription[] = await Promise.all( currentFilenames.map(async (filename) => { const fileExtension = path.parse(filename).ext.slice(1); if (fileExtension !== 'svg' && fileExtension !== 'png') { @@ -43,3 +43,12 @@ export const updateLocalFiles = async (icons: IconDescriptionWithData[]) => { }) ); }; + +export const deleteLocalFiles = async (iconsToDelete: LocalIconDescription[]) => { + await Promise.all( + iconsToDelete.map((icon) => { + const filePath = path.join(ICONS_PATH, `${icon.name}.${icon.exportFormat}`); + return fs.rm(filePath); + }) + ); +}; diff --git a/tools/figma/update-files.ts b/tools/figma/update-files.ts deleted file mode 100644 index ec2a7ce..0000000 --- a/tools/figma/update-files.ts +++ /dev/null @@ -1,17 +0,0 @@ -import {differenceBy, intersectionBy} from 'lodash'; -import {fetchFigmaIcons} from '../figma/fetch-icons'; -import {getFiles, updateLocalFiles} from '../figma/local'; -import {downloadAndTransform} from './get-image-files'; - -export const updateFiles = async () => { - const localIcons = await getFiles(); - - const figmaIcons = await fetchFigmaIcons(); - - const existingLocalIcons = intersectionBy(localIcons, figmaIcons, (d) => d.name); - const iconsToDelete = differenceBy(localIcons, existingLocalIcons, (d) => d.name); - - const iconsWithData = await downloadAndTransform(figmaIcons); - - await updateLocalFiles(iconsWithData); -}; diff --git a/tools/figma/update-icons.ts b/tools/figma/update-icons.ts new file mode 100644 index 0000000..5612420 --- /dev/null +++ b/tools/figma/update-icons.ts @@ -0,0 +1,17 @@ +import {differenceBy, intersectionBy} from 'lodash'; +import {fetchFigmaIcons} from './fetch-icons'; +import {deleteLocalFiles, getLocalIcons, updateLocalFiles} from './local'; +import {downloadAndTransform} from './get-image-files'; + +export const updateIcons = async () => { + const [localIcons, figmaIcons] = await Promise.all([getLocalIcons(), fetchFigmaIcons()]); + + const iconsWithData = await downloadAndTransform(figmaIcons); + await updateLocalFiles(iconsWithData); + + const existingLocalIcons = intersectionBy(localIcons, iconsWithData, (d) => d.name); + const iconsToDelete = differenceBy(localIcons, existingLocalIcons, (d) => d.name); + await deleteLocalFiles(iconsToDelete); + + return iconsWithData; +}; From 67864b5d3b0282a2b022fb2624ec4d56071f4079 Mon Sep 17 00:00:00 2001 From: Matthew Kalinin Date: Mon, 1 Apr 2024 11:49:02 +0300 Subject: [PATCH 07/48] export only svg files --- tools/figma/get-icon-descriptions.ts | 11 ++--------- tools/figma/get-image-files.ts | 13 ++++++------- tools/figma/get-image-links.ts | 20 +++----------------- tools/figma/local.ts | 9 +++------ 4 files changed, 14 insertions(+), 39 deletions(-) diff --git a/tools/figma/get-icon-descriptions.ts b/tools/figma/get-icon-descriptions.ts index 850746d..52ac393 100644 --- a/tools/figma/get-icon-descriptions.ts +++ b/tools/figma/get-icon-descriptions.ts @@ -1,15 +1,12 @@ -import {ImageType, type Node} from 'figma-api'; +import {type Node} from 'figma-api'; const REGION_CODE_REGEXP = /_([a-z]{2})_/; const SIZE_REGEXP = /_([0-9]{2})/; const INTL_REGION_CODES = new Intl.DisplayNames(['en', 'ru'], {type: 'region'}); -export type ExportFormat = 'png' | 'svg'; - export type IconDescription = { componentId: string; name: string; - exportFormat: ExportFormat; }; export const getIconDescriptions = (components: Node<'COMPONENT'>[], availableSizes: number[]): IconDescription[] => { @@ -30,11 +27,7 @@ export const getIconDescriptions = (components: Node<'COMPONENT'>[], availableSi const {height, width} = component.absoluteBoundingBox; return height === width; }) - .map((component) => { - const isPng = component.exportSettings.every(({format}) => format === ImageType.PNG); - const exportFormat = isPng ? 'png' : 'svg'; - return {componentId: component.id, name: component.name, exportFormat}; - }); + .map((component) => ({componentId: component.id, name: component.name})); }; const componentNameHasRegionCode = (componentName: string): boolean => { diff --git a/tools/figma/get-image-files.ts b/tools/figma/get-image-files.ts index f95022d..2e34f6b 100644 --- a/tools/figma/get-image-files.ts +++ b/tools/figma/get-image-files.ts @@ -15,12 +15,11 @@ export type IconDescriptionWithData = IconDescription & { export const downloadAndTransform = async (icons: IconDescriptionWithLink[]): Promise => { const iconsWithData = await getImageFiles(icons); return iconsWithData - .filter((icon) => (icon.exportFormat !== 'svg' ? true : !icon.data.toString().match(ERROR_COLOR_REGEXP))) - .map((icon) => - icon.exportFormat !== 'svg' - ? icon - : {...icon, data: Buffer.from(icon.data.toString().replace(FILL_COLOR_REGEXP, 'fill="currentColor"'))} - ); + .filter((icon) => !icon.data.toString().match(ERROR_COLOR_REGEXP)) + .map((icon) => ({ + ...icon, + data: Buffer.from(icon.data.toString().replace(FILL_COLOR_REGEXP, 'fill="currentColor"')) + })); }; const getImageFiles = async (icons: IconDescriptionWithLink[]): Promise => { @@ -28,7 +27,7 @@ const getImageFiles = async (icons: IconDescriptionWithLink[]): Promise { try { const file = await fetchFile(icon.link); - return {...file, name: icon.name, exportFormat: icon.exportFormat, componentId: icon.componentId}; + return {...file, name: icon.name, componentId: icon.componentId}; } catch (e) { e.message = `${icon.name}: ${e.message}`; throw e; diff --git a/tools/figma/get-image-links.ts b/tools/figma/get-image-links.ts index cd7373a..31d6f64 100644 --- a/tools/figma/get-image-links.ts +++ b/tools/figma/get-image-links.ts @@ -1,7 +1,7 @@ import {Api as FigmaApi} from 'figma-api'; import {GetImageResult} from 'figma-api/lib/api-types'; import {makeChunks} from '../utils/make-chunks'; -import {ExportFormat, IconDescription} from './get-icon-descriptions'; +import {IconDescription} from './get-icon-descriptions'; const IMAGE_SCALE = 1; const ICONS_PER_CHUNK = 100; @@ -10,22 +10,19 @@ export type IconDescriptionWithLink = IconDescription & { componentId: string; link: string; }; -type SeparatedIcons = { - [key in ExportFormat]: IconDescription[]; -}; export const getImageLinks = async ( descriptions: IconDescription[], fileId: string, api: FigmaApi ): Promise => { - const chunks = separateIntoChunks(descriptions); + const chunks = makeChunks(descriptions, ICONS_PER_CHUNK); const linkChunks = await Promise.all( chunks.map((chunk) => api.getImage(fileId, { ids: chunk.map((icon) => icon.componentId).join(','), scale: IMAGE_SCALE, - format: chunk[0].exportFormat + format: 'svg' }) ) ); @@ -54,14 +51,3 @@ export const getImageLinks = async ( return descriptionsWithLink; }, []); }; - -const separateIntoChunks = (descriptions: IconDescription[]): IconDescription[][] => { - const separatedIcons = descriptions.reduce( - (result, icon) => { - result[icon.exportFormat].push(icon); - return result; - }, - {svg: [], png: []} - ); - return [...makeChunks(separatedIcons.svg, ICONS_PER_CHUNK), ...makeChunks(separatedIcons.png, ICONS_PER_CHUNK)]; -}; diff --git a/tools/figma/local.ts b/tools/figma/local.ts index f10e23f..604d836 100644 --- a/tools/figma/local.ts +++ b/tools/figma/local.ts @@ -1,6 +1,5 @@ import fs from 'fs/promises'; import path from 'path'; -import {ExportFormat} from './get-icon-descriptions'; import {IconDescriptionWithData} from './get-image-files'; const BASE_DIR = path.join(__dirname, '../../'); @@ -10,7 +9,6 @@ const ICONS_PATH = path.join(BASE_DIR, 'static/icons'); export type LocalIconDescription = { name: string; data: Buffer; - exportFormat: ExportFormat; }; export const getLocalIcons = async (): Promise => { @@ -31,15 +29,14 @@ export const getLocalIcons = async (): Promise => { }; }) ); - console.info(`${descriptions.length} current icons files fetched`); return descriptions; }; export const updateLocalFiles = async (icons: IconDescriptionWithData[]) => { await Promise.all( icons.map((icon) => { - const filePath = path.join(ICONS_PATH, `${icon.name}.${icon.exportFormat}`); - return fs.writeFile(filePath, icon.data, icon.exportFormat === 'svg' ? 'utf-8' : null); + const filePath = path.join(ICONS_PATH, `${icon.name}.svg`); + return fs.writeFile(filePath, icon.data, 'utf-8'); }) ); }; @@ -47,7 +44,7 @@ export const updateLocalFiles = async (icons: IconDescriptionWithData[]) => { export const deleteLocalFiles = async (iconsToDelete: LocalIconDescription[]) => { await Promise.all( iconsToDelete.map((icon) => { - const filePath = path.join(ICONS_PATH, `${icon.name}.${icon.exportFormat}`); + const filePath = path.join(ICONS_PATH, `${icon.name}.svg`); return fs.rm(filePath); }) ); From 60983d830c40701117310e0a9ad1723554a18f4b Mon Sep 17 00:00:00 2001 From: Matthew Kalinin Date: Mon, 1 Apr 2024 12:38:42 +0300 Subject: [PATCH 08/48] refactor --- tools/figma/fetch-icons.ts | 161 ++++++++++++++++++++++++++- tools/figma/get-components.ts | 23 ---- tools/figma/get-icon-descriptions.ts | 51 --------- tools/figma/get-image-files.ts | 19 +++- tools/figma/get-image-links.ts | 53 --------- 5 files changed, 173 insertions(+), 134 deletions(-) delete mode 100644 tools/figma/get-components.ts delete mode 100644 tools/figma/get-icon-descriptions.ts delete mode 100644 tools/figma/get-image-links.ts diff --git a/tools/figma/fetch-icons.ts b/tools/figma/fetch-icons.ts index f43a67d..aacb60b 100644 --- a/tools/figma/fetch-icons.ts +++ b/tools/figma/fetch-icons.ts @@ -1,12 +1,33 @@ -import {Api as FigmaApi} from 'figma-api'; -import {getComponents} from './get-components'; -import {getIconDescriptions} from './get-icon-descriptions'; -import {IconDescriptionWithLink, getImageLinks} from './get-image-links'; +import {Api as FigmaApi, isNodeType, Node} from 'figma-api'; +import {GetImageResult} from 'figma-api/lib/api-types'; +import {makeChunks} from '../utils/make-chunks'; + +export type IconDescription = { + componentId: string; + name: string; +}; + +export type IconDescriptionWithLink = IconDescription & { + link: string; +}; /** The name of the canvas in the file from which the icons will be loaded */ const CANVAS_NAME = 'glyphs'; +/** Available icon sizes specified in Figma */ const AVAILABLE_SIZES = [14, 24]; +/** RegExp for getting the region code from the component name */ +const REGION_CODE_REGEXP = /_([a-z]{2})_/; +/** RegExp for getting the size from the component name*/ +const SIZE_REGEXP = /_([0-9]{2})/; +const INTL_REGION_CODES = new Intl.DisplayNames(['en', 'ru'], {type: 'region'}); +/** A number between 0.01 and 4, the image scaling factor */ +const IMAGE_SCALE = 1; +const ICONS_PER_CHUNK = 100; +/** + * Gets a description and links to download icons from Figma + * @returns Description of the icons and links to download them, which are located in Figma. + */ export async function fetchFigmaIcons(): Promise { const personalAccessToken: string | undefined = process.env.FIGMA_API_TOKEN; const fileId: string | undefined = process.env.FIGMA_FILE_ID; @@ -19,8 +40,138 @@ export async function fetchFigmaIcons(): Promise { } const api = new FigmaApi({personalAccessToken}); - const components = await getComponents({fileId, canvasName: CANVAS_NAME}, api); + const components = await getComponents(fileId, CANVAS_NAME, api); const iconDescriptions = getIconDescriptions(components, AVAILABLE_SIZES); const imageLinks = await getImageLinks(iconDescriptions, fileId, api); return imageLinks; } + +/** + * Get components that are included in the canvas from Figma + * @param fileId - ID of the document where the canvas is located + * @param canvasName - The name of the canvas from which the components come + * @param api - Object for accessing the Figma API + * @returns Array of component nodes + */ +const getComponents = async (fileId: string, canvasName: string, api: FigmaApi): Promise[]> => { + const file = await api.getFile(fileId); + + const canvas = file.document.children.find((child) => child.name === canvasName) as Node<'CANVAS'>; + if (!canvas) { + throw new Error(`Canvas "${canvasName}" not found!`); + } + + return canvas.children.reduce((components, child) => { + if (!isNodeType(child, 'GROUP')) { + return components; + } + const newComponents = child.children.filter((child) => isNodeType(child, 'INSTANCE')) as Node<'COMPONENT'>[]; + return components.concat(newComponents); + }, [] as Node<'COMPONENT'>[]); +}; + +/** + * Gets a description of icons from the component nodes + * @param components - Array of component nodes + * @param availableSizes - Acceptable icon sizes + * @returns Array of objects with icon descriptions + */ +const getIconDescriptions = (components: Node<'COMPONENT'>[], availableSizes: number[]): IconDescription[] => { + return components + .filter((component) => { + // the component must have export settings + if (component.exportSettings === undefined || component.exportSettings.length === 0) { + return false; + } + // the component should not have a regional code + if (componentNameHasRegionCode(component.name)) { + return false; + } + // the component must be of allowed size and square + if (!componentAvailableSize(component.name, availableSizes)) { + return false; + } + const {height, width} = component.absoluteBoundingBox; + return height === width; + }) + .map((component) => ({componentId: component.id, name: component.name})); +}; + +/** + * Adds download links to the icon descriptions + * @param descriptions - Array of objects with icon descriptions + * @param fileId - ID of the document where the icons are located + * @param api - Object for accessing the Figma API + * @returns Array of objects with descriptions and download links for icons + */ +const getImageLinks = async ( + descriptions: IconDescription[], + fileId: string, + api: FigmaApi +): Promise => { + const chunks = makeChunks(descriptions, ICONS_PER_CHUNK); + const linkChunks = await Promise.all( + chunks.map((chunk) => + api.getImage(fileId, { + ids: chunk.map((icon) => icon.componentId).join(','), + scale: IMAGE_SCALE, + format: 'svg' + }) + ) + ); + const links = linkChunks.reduce( + (memo, chunk) => { + if (chunk.err) { + memo.err = chunk.err; + } + if (chunk.images) { + memo.images = {...memo.images, ...chunk.images}; + } + return memo; + }, + {images: {}} + ); + + if (links.err) { + throw new Error(`Error while loading links: ${links.err}`); + } + + return descriptions.reduce((descriptionsWithLink, description) => { + const link = links.images[description.componentId]; + if (link) { + return descriptionsWithLink.concat({...description, link}); + } + return descriptionsWithLink; + }, []); +}; + +/** + * Checks that the component name has a region code + * @param componentName - Component name + * @returns `true` if the component name contains the region code + */ +const componentNameHasRegionCode = (componentName: string): boolean => { + const regionMatch = REGION_CODE_REGEXP.exec(componentName); + if (regionMatch === null) { + return false; + } + const [, regionCode] = regionMatch; + const uppercaseCode = regionCode.toUpperCase(); + return INTL_REGION_CODES.of(uppercaseCode) !== uppercaseCode; +}; + +/** + * Checks that the allowed size is specified in the component name + * @param componentName - Component name + * @param availableSizes - Array with acceptable icon sizes + * @returns `true` if the component name contains a valid size + */ +const componentAvailableSize = (componentName: string, availableSizes: number[]): boolean => { + const sizeMatch = SIZE_REGEXP.exec(componentName); + if (sizeMatch === null) { + return false; + } + const [, rawSize] = sizeMatch; + const size = rawSize ? Number(rawSize) : undefined; + return availableSizes.includes(size as number); +}; diff --git a/tools/figma/get-components.ts b/tools/figma/get-components.ts deleted file mode 100644 index 63a397d..0000000 --- a/tools/figma/get-components.ts +++ /dev/null @@ -1,23 +0,0 @@ -import {Api as FigmaApi, Node, isNodeType} from 'figma-api'; - -type ComponentOptions = { - fileId: string; - canvasName: string; -}; - -export const getComponents = async (options: ComponentOptions, api: FigmaApi): Promise[]> => { - const file = await api.getFile(options.fileId); - - const canvas = file.document.children.find((child) => child.name === options.canvasName) as Node<'CANVAS'>; - if (!canvas) { - throw new Error(`Canvas "${options.canvasName}" not found!`); - } - - return canvas.children.reduce((components, child) => { - if (!isNodeType(child, 'GROUP')) { - return components; - } - const newComponents = child.children.filter((child) => isNodeType(child, 'INSTANCE')) as Node<'COMPONENT'>[]; - return components.concat(newComponents); - }, [] as Node<'COMPONENT'>[]); -}; diff --git a/tools/figma/get-icon-descriptions.ts b/tools/figma/get-icon-descriptions.ts deleted file mode 100644 index 52ac393..0000000 --- a/tools/figma/get-icon-descriptions.ts +++ /dev/null @@ -1,51 +0,0 @@ -import {type Node} from 'figma-api'; - -const REGION_CODE_REGEXP = /_([a-z]{2})_/; -const SIZE_REGEXP = /_([0-9]{2})/; -const INTL_REGION_CODES = new Intl.DisplayNames(['en', 'ru'], {type: 'region'}); - -export type IconDescription = { - componentId: string; - name: string; -}; - -export const getIconDescriptions = (components: Node<'COMPONENT'>[], availableSizes: number[]): IconDescription[] => { - return components - .filter((component) => { - // the component must have export settings - if (component.exportSettings === undefined || component.exportSettings.length === 0) { - return false; - } - // the component should not have a regional code - if (componentNameHasRegionCode(component.name)) { - return false; - } - // the component must be of allowed size and square - if (!componentAvailableSize(component.name, availableSizes)) { - return false; - } - const {height, width} = component.absoluteBoundingBox; - return height === width; - }) - .map((component) => ({componentId: component.id, name: component.name})); -}; - -const componentNameHasRegionCode = (componentName: string): boolean => { - const regionMatch = REGION_CODE_REGEXP.exec(componentName); - if (regionMatch === null) { - return false; - } - const [, regionCode] = regionMatch; - const uppercaseCode = regionCode.toUpperCase(); - return INTL_REGION_CODES.of(uppercaseCode) !== uppercaseCode; -}; - -const componentAvailableSize = (componentName: string, availableSizes: number[]): boolean => { - const sizeMatch = SIZE_REGEXP.exec(componentName); - if (sizeMatch === null) { - return false; - } - const [, rawSize] = sizeMatch; - const size = rawSize ? Number(rawSize) : undefined; - return availableSizes.includes(size as number); -}; diff --git a/tools/figma/get-image-files.ts b/tools/figma/get-image-files.ts index 2e34f6b..28b1e8a 100644 --- a/tools/figma/get-image-files.ts +++ b/tools/figma/get-image-files.ts @@ -1,7 +1,7 @@ import got from 'got'; -import {IconDescription} from './get-icon-descriptions'; -import {IconDescriptionWithLink} from './get-image-links'; +import {IconDescription, IconDescriptionWithLink} from './fetch-icons'; +/** Maximum number of retries when loading an icon */ const MAX_RETRIES = 20; /** Our designer marks these icons with this color that do not need to be worked on yet. */ const ERROR_COLOR_REGEXP = /fill="#C90D0D"/; @@ -12,6 +12,11 @@ export type IconDescriptionWithData = IconDescription & { data: Buffer; }; +/** + * Downloads icons from Figma using the link from the description and transforms them + * @param icons - Array of objects with icon descriptions + * @returns Array of objects with icon descriptions and transformed downloaded data + */ export const downloadAndTransform = async (icons: IconDescriptionWithLink[]): Promise => { const iconsWithData = await getImageFiles(icons); return iconsWithData @@ -22,6 +27,11 @@ export const downloadAndTransform = async (icons: IconDescriptionWithLink[]): Pr })); }; +/** + * Downloads icons from Figma using the link from the description + * @param icons - Array of objects with icon descriptions + * @returns Array of objects with icon descriptions and downloaded data + */ const getImageFiles = async (icons: IconDescriptionWithLink[]): Promise => { return Promise.all( icons.map(async (icon) => { @@ -36,6 +46,11 @@ const getImageFiles = async (icons: IconDescriptionWithLink[]): Promise { const response = await got(url, {timeout: 60 * 1000, retry: MAX_RETRIES}); if (!response.body) { diff --git a/tools/figma/get-image-links.ts b/tools/figma/get-image-links.ts deleted file mode 100644 index 31d6f64..0000000 --- a/tools/figma/get-image-links.ts +++ /dev/null @@ -1,53 +0,0 @@ -import {Api as FigmaApi} from 'figma-api'; -import {GetImageResult} from 'figma-api/lib/api-types'; -import {makeChunks} from '../utils/make-chunks'; -import {IconDescription} from './get-icon-descriptions'; - -const IMAGE_SCALE = 1; -const ICONS_PER_CHUNK = 100; - -export type IconDescriptionWithLink = IconDescription & { - componentId: string; - link: string; -}; - -export const getImageLinks = async ( - descriptions: IconDescription[], - fileId: string, - api: FigmaApi -): Promise => { - const chunks = makeChunks(descriptions, ICONS_PER_CHUNK); - const linkChunks = await Promise.all( - chunks.map((chunk) => - api.getImage(fileId, { - ids: chunk.map((icon) => icon.componentId).join(','), - scale: IMAGE_SCALE, - format: 'svg' - }) - ) - ); - const links = linkChunks.reduce( - (memo, chunk) => { - if (chunk.err) { - memo.err = chunk.err; - } - if (chunk.images) { - memo.images = {...memo.images, ...chunk.images}; - } - return memo; - }, - {images: {}} - ); - - if (links.err) { - throw new Error(`Error while loading links: ${links.err}`); - } - - return descriptions.reduce((descriptionsWithLink, description) => { - const link = links.images[description.componentId]; - if (link) { - return descriptionsWithLink.concat({...description, link}); - } - return descriptionsWithLink; - }, []); -}; From 4bb1291afa4174983dd7a1b6cab855a6176d784c Mon Sep 17 00:00:00 2001 From: Matthew Kalinin Date: Mon, 1 Apr 2024 12:55:55 +0300 Subject: [PATCH 09/48] optimize svg and filter fallback icons --- tools/figma/fetch-icons.ts | 177 --------------------------------- tools/figma/get-image-files.ts | 60 ----------- tools/figma/local.ts | 51 ---------- tools/figma/update-icons.ts | 17 ---- 4 files changed, 305 deletions(-) delete mode 100644 tools/figma/fetch-icons.ts delete mode 100644 tools/figma/get-image-files.ts delete mode 100644 tools/figma/local.ts delete mode 100644 tools/figma/update-icons.ts diff --git a/tools/figma/fetch-icons.ts b/tools/figma/fetch-icons.ts deleted file mode 100644 index aacb60b..0000000 --- a/tools/figma/fetch-icons.ts +++ /dev/null @@ -1,177 +0,0 @@ -import {Api as FigmaApi, isNodeType, Node} from 'figma-api'; -import {GetImageResult} from 'figma-api/lib/api-types'; -import {makeChunks} from '../utils/make-chunks'; - -export type IconDescription = { - componentId: string; - name: string; -}; - -export type IconDescriptionWithLink = IconDescription & { - link: string; -}; - -/** The name of the canvas in the file from which the icons will be loaded */ -const CANVAS_NAME = 'glyphs'; -/** Available icon sizes specified in Figma */ -const AVAILABLE_SIZES = [14, 24]; -/** RegExp for getting the region code from the component name */ -const REGION_CODE_REGEXP = /_([a-z]{2})_/; -/** RegExp for getting the size from the component name*/ -const SIZE_REGEXP = /_([0-9]{2})/; -const INTL_REGION_CODES = new Intl.DisplayNames(['en', 'ru'], {type: 'region'}); -/** A number between 0.01 and 4, the image scaling factor */ -const IMAGE_SCALE = 1; -const ICONS_PER_CHUNK = 100; - -/** - * Gets a description and links to download icons from Figma - * @returns Description of the icons and links to download them, which are located in Figma. - */ -export async function fetchFigmaIcons(): Promise { - const personalAccessToken: string | undefined = process.env.FIGMA_API_TOKEN; - const fileId: string | undefined = process.env.FIGMA_FILE_ID; - - if (!personalAccessToken) { - throw new Error('No Figma access token found in environment variable FIGMA_API_TOKEN'); - } - if (!fileId) { - throw new Error('No Figma file id found in environment variable FIGMA_FILE_ID'); - } - - const api = new FigmaApi({personalAccessToken}); - const components = await getComponents(fileId, CANVAS_NAME, api); - const iconDescriptions = getIconDescriptions(components, AVAILABLE_SIZES); - const imageLinks = await getImageLinks(iconDescriptions, fileId, api); - return imageLinks; -} - -/** - * Get components that are included in the canvas from Figma - * @param fileId - ID of the document where the canvas is located - * @param canvasName - The name of the canvas from which the components come - * @param api - Object for accessing the Figma API - * @returns Array of component nodes - */ -const getComponents = async (fileId: string, canvasName: string, api: FigmaApi): Promise[]> => { - const file = await api.getFile(fileId); - - const canvas = file.document.children.find((child) => child.name === canvasName) as Node<'CANVAS'>; - if (!canvas) { - throw new Error(`Canvas "${canvasName}" not found!`); - } - - return canvas.children.reduce((components, child) => { - if (!isNodeType(child, 'GROUP')) { - return components; - } - const newComponents = child.children.filter((child) => isNodeType(child, 'INSTANCE')) as Node<'COMPONENT'>[]; - return components.concat(newComponents); - }, [] as Node<'COMPONENT'>[]); -}; - -/** - * Gets a description of icons from the component nodes - * @param components - Array of component nodes - * @param availableSizes - Acceptable icon sizes - * @returns Array of objects with icon descriptions - */ -const getIconDescriptions = (components: Node<'COMPONENT'>[], availableSizes: number[]): IconDescription[] => { - return components - .filter((component) => { - // the component must have export settings - if (component.exportSettings === undefined || component.exportSettings.length === 0) { - return false; - } - // the component should not have a regional code - if (componentNameHasRegionCode(component.name)) { - return false; - } - // the component must be of allowed size and square - if (!componentAvailableSize(component.name, availableSizes)) { - return false; - } - const {height, width} = component.absoluteBoundingBox; - return height === width; - }) - .map((component) => ({componentId: component.id, name: component.name})); -}; - -/** - * Adds download links to the icon descriptions - * @param descriptions - Array of objects with icon descriptions - * @param fileId - ID of the document where the icons are located - * @param api - Object for accessing the Figma API - * @returns Array of objects with descriptions and download links for icons - */ -const getImageLinks = async ( - descriptions: IconDescription[], - fileId: string, - api: FigmaApi -): Promise => { - const chunks = makeChunks(descriptions, ICONS_PER_CHUNK); - const linkChunks = await Promise.all( - chunks.map((chunk) => - api.getImage(fileId, { - ids: chunk.map((icon) => icon.componentId).join(','), - scale: IMAGE_SCALE, - format: 'svg' - }) - ) - ); - const links = linkChunks.reduce( - (memo, chunk) => { - if (chunk.err) { - memo.err = chunk.err; - } - if (chunk.images) { - memo.images = {...memo.images, ...chunk.images}; - } - return memo; - }, - {images: {}} - ); - - if (links.err) { - throw new Error(`Error while loading links: ${links.err}`); - } - - return descriptions.reduce((descriptionsWithLink, description) => { - const link = links.images[description.componentId]; - if (link) { - return descriptionsWithLink.concat({...description, link}); - } - return descriptionsWithLink; - }, []); -}; - -/** - * Checks that the component name has a region code - * @param componentName - Component name - * @returns `true` if the component name contains the region code - */ -const componentNameHasRegionCode = (componentName: string): boolean => { - const regionMatch = REGION_CODE_REGEXP.exec(componentName); - if (regionMatch === null) { - return false; - } - const [, regionCode] = regionMatch; - const uppercaseCode = regionCode.toUpperCase(); - return INTL_REGION_CODES.of(uppercaseCode) !== uppercaseCode; -}; - -/** - * Checks that the allowed size is specified in the component name - * @param componentName - Component name - * @param availableSizes - Array with acceptable icon sizes - * @returns `true` if the component name contains a valid size - */ -const componentAvailableSize = (componentName: string, availableSizes: number[]): boolean => { - const sizeMatch = SIZE_REGEXP.exec(componentName); - if (sizeMatch === null) { - return false; - } - const [, rawSize] = sizeMatch; - const size = rawSize ? Number(rawSize) : undefined; - return availableSizes.includes(size as number); -}; diff --git a/tools/figma/get-image-files.ts b/tools/figma/get-image-files.ts deleted file mode 100644 index 28b1e8a..0000000 --- a/tools/figma/get-image-files.ts +++ /dev/null @@ -1,60 +0,0 @@ -import got from 'got'; -import {IconDescription, IconDescriptionWithLink} from './fetch-icons'; - -/** Maximum number of retries when loading an icon */ -const MAX_RETRIES = 20; -/** Our designer marks these icons with this color that do not need to be worked on yet. */ -const ERROR_COLOR_REGEXP = /fill="#C90D0D"/; -/** Default icon color from Figma */ -const FILL_COLOR_REGEXP = /fill="black"/g; - -export type IconDescriptionWithData = IconDescription & { - data: Buffer; -}; - -/** - * Downloads icons from Figma using the link from the description and transforms them - * @param icons - Array of objects with icon descriptions - * @returns Array of objects with icon descriptions and transformed downloaded data - */ -export const downloadAndTransform = async (icons: IconDescriptionWithLink[]): Promise => { - const iconsWithData = await getImageFiles(icons); - return iconsWithData - .filter((icon) => !icon.data.toString().match(ERROR_COLOR_REGEXP)) - .map((icon) => ({ - ...icon, - data: Buffer.from(icon.data.toString().replace(FILL_COLOR_REGEXP, 'fill="currentColor"')) - })); -}; - -/** - * Downloads icons from Figma using the link from the description - * @param icons - Array of objects with icon descriptions - * @returns Array of objects with icon descriptions and downloaded data - */ -const getImageFiles = async (icons: IconDescriptionWithLink[]): Promise => { - return Promise.all( - icons.map(async (icon) => { - try { - const file = await fetchFile(icon.link); - return {...file, name: icon.name, componentId: icon.componentId}; - } catch (e) { - e.message = `${icon.name}: ${e.message}`; - throw e; - } - }) - ); -}; - -/** - * Download the file from the specified URL - * @param url - URL where the file is located - * @returns Downloaded data as a buffer - */ -const fetchFile = async (url: string) => { - const response = await got(url, {timeout: 60 * 1000, retry: MAX_RETRIES}); - if (!response.body) { - throw new Error('No response body.'); - } - return {data: response.body}; -}; diff --git a/tools/figma/local.ts b/tools/figma/local.ts deleted file mode 100644 index 604d836..0000000 --- a/tools/figma/local.ts +++ /dev/null @@ -1,51 +0,0 @@ -import fs from 'fs/promises'; -import path from 'path'; -import {IconDescriptionWithData} from './get-image-files'; - -const BASE_DIR = path.join(__dirname, '../../'); -const ICONS_PATH = path.join(BASE_DIR, 'static/icons'); -// const TYPES_PATH = path.join(BASE_DIR, 'src/icons/types'); - -export type LocalIconDescription = { - name: string; - data: Buffer; -}; - -export const getLocalIcons = async (): Promise => { - console.info('Getting static resources dir'); - const currentFilenames = await fs.readdir(ICONS_PATH); - const descriptions: LocalIconDescription[] = await Promise.all( - currentFilenames.map(async (filename) => { - const fileExtension = path.parse(filename).ext.slice(1); - if (fileExtension !== 'svg' && fileExtension !== 'png') { - throw new Error('Unknown file extension.'); - } - const cleanFilename = path.parse(filename).name; - const data = await fs.readFile(path.join(ICONS_PATH, filename)); - return { - name: cleanFilename, - data, - exportFormat: fileExtension - }; - }) - ); - return descriptions; -}; - -export const updateLocalFiles = async (icons: IconDescriptionWithData[]) => { - await Promise.all( - icons.map((icon) => { - const filePath = path.join(ICONS_PATH, `${icon.name}.svg`); - return fs.writeFile(filePath, icon.data, 'utf-8'); - }) - ); -}; - -export const deleteLocalFiles = async (iconsToDelete: LocalIconDescription[]) => { - await Promise.all( - iconsToDelete.map((icon) => { - const filePath = path.join(ICONS_PATH, `${icon.name}.svg`); - return fs.rm(filePath); - }) - ); -}; diff --git a/tools/figma/update-icons.ts b/tools/figma/update-icons.ts deleted file mode 100644 index 5612420..0000000 --- a/tools/figma/update-icons.ts +++ /dev/null @@ -1,17 +0,0 @@ -import {differenceBy, intersectionBy} from 'lodash'; -import {fetchFigmaIcons} from './fetch-icons'; -import {deleteLocalFiles, getLocalIcons, updateLocalFiles} from './local'; -import {downloadAndTransform} from './get-image-files'; - -export const updateIcons = async () => { - const [localIcons, figmaIcons] = await Promise.all([getLocalIcons(), fetchFigmaIcons()]); - - const iconsWithData = await downloadAndTransform(figmaIcons); - await updateLocalFiles(iconsWithData); - - const existingLocalIcons = intersectionBy(localIcons, iconsWithData, (d) => d.name); - const iconsToDelete = differenceBy(localIcons, existingLocalIcons, (d) => d.name); - await deleteLocalFiles(iconsToDelete); - - return iconsWithData; -}; From 073ad56101ee29b64866d6177641724586349cad Mon Sep 17 00:00:00 2001 From: Matthew Kalinin Date: Mon, 1 Apr 2024 14:30:00 +0300 Subject: [PATCH 10/48] generate types for icons --- src/icons/types/rubrics.ts | 126 +++++++++++++++++++++++++++++++++++++ 1 file changed, 126 insertions(+) create mode 100644 src/icons/types/rubrics.ts diff --git a/src/icons/types/rubrics.ts b/src/icons/types/rubrics.ts new file mode 100644 index 0000000..6cf060e --- /dev/null +++ b/src/icons/types/rubrics.ts @@ -0,0 +1,126 @@ +/** Don't edit manually. Generated by script: ./tools/icons/generate-types.ts */ +type IconName = + | 'post_office' + | 'information' + | 'industrial_enterprise' + | 'science' + | 'police' + | 'police_post' + | 'college' + | 'library' + | 'fire_station' + | 'government' + | 'protestant_church' + | 'orthodox_church' + | 'mosque' + | 'synagogue' + | 'buddhism' + | 'cemetery' + | 'kindergarten' + | 'office' + | 'catholic_church' + | 'drugstores' + | 'hospital' + | 'medicine' + | 'dental' + | 'cinemas' + | 'film_studio' + | 'concert_hall' + | 'attraction' + | 'ticket_office' + | 'driving_school' + | 'racing' + | 'banks' + | 'currency_exchange' + | 'software' + | 'travel_agency' + | 'haulier' + | 'tailor' + | 'hairdressers' + | 'dry_cleaning' + | 'laundry' + | 'printing_services' + | 'bike_rent' + | 'mobile_phones' + | 'photo' + | 'hotels' + | 'gasstation' + | 'tire_fitting' + | 'fitness' + | 'sportcenter' + | 'sport_school' + | 'boat_station' + | 'office_service' + | 'spa' + | 'metro_bus' + | 'metro_light' + | 'railway' + | 'railway_station' + | 'port' + | 'airport' + | 'metro_cable' + | 'metro_funicular' + | 'metro_tram' + | 'metro' + | 'pier' + | 'bus' + | 'tram' + | 'metro_entrance' + | 'metro_monorail' + | 'taxi' + | 'hypermarket' + | 'petshop' + | 'bookstore' + | 'clothes_shop' + | 'furniture_store' + | 'flower_shop' + | 'baby_shop' + | 'auto' + | 'malls' + | 'sport' + | 'supermarket' + | 'skating_rink' + | 'swimming_pool' + | 'waterfall' + | 'spring' + | 'fountain' + | 'theatre' + | 'landmark' + | 'memorable_event' + | 'museum' + | 'beach' + | 'zoo' + | 'garden' + | 'park' + | 'forest' + | 'rezervation' + | 'equestrian' + | 'stadium' + | 'sanatorium' + | 'mountain' + | 'restaurants' + | 'cafe' + | 'fast_food' + | 'sushi' + | 'bars' + | 'confectionary' + | 'wc' + | 'trash' + | 'helicopter' + | 'building' + | 'bench' + | 'aviary' + | 'money_coin' + | 'recycling' + | 'barbeque' + | 'pavilion' + | 'picnic' + | 'monument' + | 'childrens_playground' + | 'pet_playground' + | 'playground' + | 'viewpoint' + | 'bike' + | 'car_park'; + +export {IconName}; From ea93e847800eeedaf6f6a71e306670f2528d58dc Mon Sep 17 00:00:00 2001 From: Matthew Kalinin Date: Mon, 1 Apr 2024 16:00:26 +0300 Subject: [PATCH 11/48] generate icons list in md file --- docs/icons.md | 128 ++++++++++++++ src/icons/types/{rubrics.ts => icons.ts} | 208 +++++++++++------------ 2 files changed, 232 insertions(+), 104 deletions(-) create mode 100644 docs/icons.md rename src/icons/types/{rubrics.ts => icons.ts} (98%) diff --git a/docs/icons.md b/docs/icons.md new file mode 100644 index 0000000..aa37f84 --- /dev/null +++ b/docs/icons.md @@ -0,0 +1,128 @@ + + +# List of supported icons + +| Name | Normal Size | Small Size | +| --------------------- | ------------------------------------------------------------------------- | ------------------------------------------------------------------------- | +| airport | ![airport](../../static/icons/airport_24.svg) | ![airport](../../static/icons/airport_14.svg) | +| attraction | ![attraction](../../static/icons/attraction_24.svg) | ![attraction](../../static/icons/attraction_14.svg) | +| auto | ![auto](../../static/icons/auto_24.svg) | ![auto](../../static/icons/auto_14.svg) | +| aviary | ![aviary](../../static/icons/aviary_24.svg) | none | +| baby_shop | ![baby_shop](../../static/icons/baby_shop_24.svg) | ![baby_shop](../../static/icons/baby_shop_14.svg) | +| banks | ![banks](../../static/icons/banks_24.svg) | ![banks](../../static/icons/banks_14.svg) | +| barbeque | ![barbeque](../../static/icons/barbeque_24.svg) | none | +| bars | ![bars](../../static/icons/bars_24.svg) | ![bars](../../static/icons/bars_14.svg) | +| beach | ![beach](../../static/icons/beach_24.svg) | ![beach](../../static/icons/beach_14.svg) | +| bench | ![bench](../../static/icons/bench_24.svg) | none | +| bike | ![bike](../../static/icons/bike_24.svg) | none | +| bike_rent | ![bike_rent](../../static/icons/bike_rent_24.svg) | ![bike_rent](../../static/icons/bike_rent_14.svg) | +| boat_station | ![boat_station](../../static/icons/boat_station_24.svg) | ![boat_station](../../static/icons/boat_station_14.svg) | +| bookstore | ![bookstore](../../static/icons/bookstore_24.svg) | ![bookstore](../../static/icons/bookstore_14.svg) | +| buddhism | ![buddhism](../../static/icons/buddhism_24.svg) | ![buddhism](../../static/icons/buddhism_14.svg) | +| building | ![building](../../static/icons/building_24.svg) | none | +| bus | ![bus](../../static/icons/bus_24.svg) | none | +| cafe | ![cafe](../../static/icons/cafe_24.svg) | ![cafe](../../static/icons/cafe_14.svg) | +| car_park | ![car_park](../../static/icons/car_park_24.svg) | none | +| catholic_church | ![catholic_church](../../static/icons/catholic_church_24.svg) | ![catholic_church](../../static/icons/catholic_church_14.svg) | +| cemetery | ![cemetery](../../static/icons/cemetery_24.svg) | ![cemetery](../../static/icons/cemetery_14.svg) | +| childrens_playground | ![childrens_playground](../../static/icons/childrens_playground_24.svg) | none | +| cinemas | ![cinemas](../../static/icons/cinemas_24.svg) | ![cinemas](../../static/icons/cinemas_14.svg) | +| clothes_shop | ![clothes_shop](../../static/icons/clothes_shop_24.svg) | ![clothes_shop](../../static/icons/clothes_shop_14.svg) | +| college | ![college](../../static/icons/college_24.svg) | ![college](../../static/icons/college_14.svg) | +| concert_hall | ![concert_hall](../../static/icons/concert_hall_24.svg) | ![concert_hall](../../static/icons/concert_hall_14.svg) | +| confectionary | ![confectionary](../../static/icons/confectionary_24.svg) | ![confectionary](../../static/icons/confectionary_14.svg) | +| currency_exchange | ![currency_exchange](../../static/icons/currency_exchange_24.svg) | ![currency_exchange](../../static/icons/currency_exchange_14.svg) | +| dental | ![dental](../../static/icons/dental_24.svg) | ![dental](../../static/icons/dental_14.svg) | +| driving_school | ![driving_school](../../static/icons/driving_school_24.svg) | ![driving_school](../../static/icons/driving_school_14.svg) | +| drugstores | ![drugstores](../../static/icons/drugstores_24.svg) | ![drugstores](../../static/icons/drugstores_14.svg) | +| dry_cleaning | ![dry_cleaning](../../static/icons/dry_cleaning_24.svg) | ![dry_cleaning](../../static/icons/dry_cleaning_14.svg) | +| equestrian | ![equestrian](../../static/icons/equestrian_24.svg) | ![equestrian](../../static/icons/equestrian_14.svg) | +| fast_food | ![fast_food](../../static/icons/fast_food_24.svg) | ![fast_food](../../static/icons/fast_food_14.svg) | +| film_studio | ![film_studio](../../static/icons/film_studio_24.svg) | ![film_studio](../../static/icons/film_studio_14.svg) | +| fire_station | ![fire_station](../../static/icons/fire_station_24.svg) | ![fire_station](../../static/icons/fire_station_14.svg) | +| fitness | ![fitness](../../static/icons/fitness_24.svg) | ![fitness](../../static/icons/fitness_14.svg) | +| flower_shop | ![flower_shop](../../static/icons/flower_shop_24.svg) | ![flower_shop](../../static/icons/flower_shop_14.svg) | +| forest | ![forest](../../static/icons/forest_24.svg) | ![forest](../../static/icons/forest_14.svg) | +| fountain | ![fountain](../../static/icons/fountain_24.svg) | none | +| furniture_store | ![furniture_store](../../static/icons/furniture_store_24.svg) | ![furniture_store](../../static/icons/furniture_store_14.svg) | +| garden | ![garden](../../static/icons/garden_24.svg) | ![garden](../../static/icons/garden_14.svg) | +| gasstation | ![gasstation](../../static/icons/gasstation_24.svg) | ![gasstation](../../static/icons/gasstation_14.svg) | +| government | ![government](../../static/icons/government_24.svg) | ![government](../../static/icons/government_14.svg) | +| hairdressers | ![hairdressers](../../static/icons/hairdressers_24.svg) | ![hairdressers](../../static/icons/hairdressers_14.svg) | +| haulier | ![haulier](../../static/icons/haulier_24.svg) | ![haulier](../../static/icons/haulier_14.svg) | +| helicopter | ![helicopter](../../static/icons/helicopter_24.svg) | none | +| hospital | ![hospital](../../static/icons/hospital_24.svg) | ![hospital](../../static/icons/hospital_14.svg) | +| hotels | ![hotels](../../static/icons/hotels_24.svg) | ![hotels](../../static/icons/hotels_14.svg) | +| hypermarket | ![hypermarket](../../static/icons/hypermarket_24.svg) | ![hypermarket](../../static/icons/hypermarket_14.svg) | +| industrial_enterprise | ![industrial_enterprise](../../static/icons/industrial_enterprise_24.svg) | ![industrial_enterprise](../../static/icons/industrial_enterprise_14.svg) | +| information | ![information](../../static/icons/information_24.svg) | ![information](../../static/icons/information_14.svg) | +| kindergarten | ![kindergarten](../../static/icons/kindergarten_24.svg) | ![kindergarten](../../static/icons/kindergarten_14.svg) | +| landmark | ![landmark](../../static/icons/landmark_24.svg) | ![landmark](../../static/icons/landmark_14.svg) | +| laundry | ![laundry](../../static/icons/laundry_24.svg) | ![laundry](../../static/icons/laundry_14.svg) | +| library | ![library](../../static/icons/library_24.svg) | ![library](../../static/icons/library_14.svg) | +| malls | ![malls](../../static/icons/malls_24.svg) | ![malls](../../static/icons/malls_14.svg) | +| medicine | ![medicine](../../static/icons/medicine_24.svg) | ![medicine](../../static/icons/medicine_14.svg) | +| memorable_event | ![memorable_event](../../static/icons/memorable_event_24.svg) | ![memorable_event](../../static/icons/memorable_event_14.svg) | +| metro | ![metro](../../static/icons/metro_24.svg) | ![metro](../../static/icons/metro_14.svg) | +| metro_bus | ![metro_bus](../../static/icons/metro_bus_24.svg) | ![metro_bus](../../static/icons/metro_bus_14.svg) | +| metro_cable | ![metro_cable](../../static/icons/metro_cable_24.svg) | ![metro_cable](../../static/icons/metro_cable_14.svg) | +| metro_entrance | ![metro_entrance](../../static/icons/metro_entrance_24.svg) | none | +| metro_funicular | ![metro_funicular](../../static/icons/metro_funicular_24.svg) | ![metro_funicular](../../static/icons/metro_funicular_14.svg) | +| metro_light | ![metro_light](../../static/icons/metro_light_24.svg) | ![metro_light](../../static/icons/metro_light_14.svg) | +| metro_monorail | ![metro_monorail](../../static/icons/metro_monorail_24.svg) | ![metro_monorail](../../static/icons/metro_monorail_14.svg) | +| metro_tram | ![metro_tram](../../static/icons/metro_tram_24.svg) | ![metro_tram](../../static/icons/metro_tram_14.svg) | +| mobile_phones | ![mobile_phones](../../static/icons/mobile_phones_24.svg) | ![mobile_phones](../../static/icons/mobile_phones_14.svg) | +| money_coin | ![money_coin](../../static/icons/money_coin_24.svg) | none | +| monument | ![monument](../../static/icons/monument_24.svg) | none | +| mosque | ![mosque](../../static/icons/mosque_24.svg) | ![mosque](../../static/icons/mosque_14.svg) | +| mountain | ![mountain](../../static/icons/mountain_24.svg) | none | +| museum | ![museum](../../static/icons/museum_24.svg) | ![museum](../../static/icons/museum_14.svg) | +| office | ![office](../../static/icons/office_24.svg) | ![office](../../static/icons/office_14.svg) | +| office_service | ![office_service](../../static/icons/office_service_24.svg) | ![office_service](../../static/icons/office_service_14.svg) | +| orthodox_church | ![orthodox_church](../../static/icons/orthodox_church_24.svg) | ![orthodox_church](../../static/icons/orthodox_church_14.svg) | +| park | ![park](../../static/icons/park_24.svg) | ![park](../../static/icons/park_14.svg) | +| pavilion | ![pavilion](../../static/icons/pavilion_24.svg) | none | +| pet_playground | ![pet_playground](../../static/icons/pet_playground_24.svg) | none | +| petshop | ![petshop](../../static/icons/petshop_24.svg) | ![petshop](../../static/icons/petshop_14.svg) | +| photo | ![photo](../../static/icons/photo_24.svg) | ![photo](../../static/icons/photo_14.svg) | +| picnic | ![picnic](../../static/icons/picnic_24.svg) | none | +| pier | ![pier](../../static/icons/pier_24.svg) | none | +| playground | ![playground](../../static/icons/playground_24.svg) | none | +| police | ![police](../../static/icons/police_24.svg) | ![police](../../static/icons/police_14.svg) | +| police_post | ![police_post](../../static/icons/police_post_24.svg) | ![police_post](../../static/icons/police_post_14.svg) | +| port | ![port](../../static/icons/port_24.svg) | ![port](../../static/icons/port_14.svg) | +| post_office | ![post_office](../../static/icons/post_office_24.svg) | ![post_office](../../static/icons/post_office_14.svg) | +| printing_services | ![printing_services](../../static/icons/printing_services_24.svg) | ![printing_services](../../static/icons/printing_services_14.svg) | +| protestant_church | ![protestant_church](../../static/icons/protestant_church_24.svg) | ![protestant_church](../../static/icons/protestant_church_14.svg) | +| racing | ![racing](../../static/icons/racing_24.svg) | ![racing](../../static/icons/racing_14.svg) | +| railway | ![railway](../../static/icons/railway_24.svg) | ![railway](../../static/icons/railway_14.svg) | +| railway_station | ![railway_station](../../static/icons/railway_station_24.svg) | ![railway_station](../../static/icons/railway_station_14.svg) | +| recycling | ![recycling](../../static/icons/recycling_24.svg) | none | +| restaurants | ![restaurants](../../static/icons/restaurants_24.svg) | ![restaurants](../../static/icons/restaurants_14.svg) | +| rezervation | ![rezervation](../../static/icons/rezervation_24.svg) | ![rezervation](../../static/icons/rezervation_14.svg) | +| sanatorium | ![sanatorium](../../static/icons/sanatorium_24.svg) | ![sanatorium](../../static/icons/sanatorium_14.svg) | +| science | ![science](../../static/icons/science_24.svg) | ![science](../../static/icons/science_14.svg) | +| skating_rink | ![skating_rink](../../static/icons/skating_rink_24.svg) | ![skating_rink](../../static/icons/skating_rink_14.svg) | +| software | ![software](../../static/icons/software_24.svg) | ![software](../../static/icons/software_14.svg) | +| spa | ![spa](../../static/icons/spa_24.svg) | ![spa](../../static/icons/spa_14.svg) | +| sport | ![sport](../../static/icons/sport_24.svg) | none | +| sport_school | ![sport_school](../../static/icons/sport_school_24.svg) | none | +| sportcenter | ![sportcenter](../../static/icons/sportcenter_24.svg) | ![sportcenter](../../static/icons/sportcenter_14.svg) | +| spring | ![spring](../../static/icons/spring_24.svg) | none | +| stadium | ![stadium](../../static/icons/stadium_24.svg) | ![stadium](../../static/icons/stadium_14.svg) | +| supermarket | ![supermarket](../../static/icons/supermarket_24.svg) | ![supermarket](../../static/icons/supermarket_14.svg) | +| sushi | ![sushi](../../static/icons/sushi_24.svg) | ![sushi](../../static/icons/sushi_14.svg) | +| swimming_pool | ![swimming_pool](../../static/icons/swimming_pool_24.svg) | ![swimming_pool](../../static/icons/swimming_pool_14.svg) | +| synagogue | ![synagogue](../../static/icons/synagogue_24.svg) | ![synagogue](../../static/icons/synagogue_14.svg) | +| tailor | ![tailor](../../static/icons/tailor_24.svg) | ![tailor](../../static/icons/tailor_14.svg) | +| taxi | ![taxi](../../static/icons/taxi_24.svg) | ![taxi](../../static/icons/taxi_14.svg) | +| theatre | ![theatre](../../static/icons/theatre_24.svg) | ![theatre](../../static/icons/theatre_14.svg) | +| ticket_office | ![ticket_office](../../static/icons/ticket_office_24.svg) | ![ticket_office](../../static/icons/ticket_office_14.svg) | +| tire_fitting | ![tire_fitting](../../static/icons/tire_fitting_24.svg) | ![tire_fitting](../../static/icons/tire_fitting_14.svg) | +| tram | ![tram](../../static/icons/tram_24.svg) | none | +| trash | ![trash](../../static/icons/trash_24.svg) | none | +| travel_agency | ![travel_agency](../../static/icons/travel_agency_24.svg) | ![travel_agency](../../static/icons/travel_agency_14.svg) | +| viewpoint | ![viewpoint](../../static/icons/viewpoint_24.svg) | none | +| waterfall | ![waterfall](../../static/icons/waterfall_24.svg) | none | +| wc | ![wc](../../static/icons/wc_24.svg) | none | +| zoo | ![zoo](../../static/icons/zoo_24.svg) | ![zoo](../../static/icons/zoo_14.svg) | diff --git a/src/icons/types/rubrics.ts b/src/icons/types/icons.ts similarity index 98% rename from src/icons/types/rubrics.ts rename to src/icons/types/icons.ts index 6cf060e..bb83046 100644 --- a/src/icons/types/rubrics.ts +++ b/src/icons/types/icons.ts @@ -1,126 +1,126 @@ /** Don't edit manually. Generated by script: ./tools/icons/generate-types.ts */ type IconName = - | 'post_office' - | 'information' - | 'industrial_enterprise' - | 'science' - | 'police' - | 'police_post' - | 'college' - | 'library' - | 'fire_station' - | 'government' - | 'protestant_church' - | 'orthodox_church' - | 'mosque' - | 'synagogue' + | 'airport' + | 'attraction' + | 'auto' + | 'aviary' + | 'baby_shop' + | 'banks' + | 'barbeque' + | 'bars' + | 'beach' + | 'bench' + | 'bike' + | 'bike_rent' + | 'boat_station' + | 'bookstore' | 'buddhism' - | 'cemetery' - | 'kindergarten' - | 'office' + | 'building' + | 'bus' + | 'cafe' + | 'car_park' | 'catholic_church' - | 'drugstores' - | 'hospital' - | 'medicine' - | 'dental' + | 'cemetery' + | 'childrens_playground' | 'cinemas' - | 'film_studio' + | 'clothes_shop' + | 'college' | 'concert_hall' - | 'attraction' - | 'ticket_office' - | 'driving_school' - | 'racing' - | 'banks' + | 'confectionary' | 'currency_exchange' - | 'software' - | 'travel_agency' - | 'haulier' - | 'tailor' - | 'hairdressers' + | 'dental' + | 'driving_school' + | 'drugstores' | 'dry_cleaning' - | 'laundry' - | 'printing_services' - | 'bike_rent' - | 'mobile_phones' - | 'photo' - | 'hotels' - | 'gasstation' - | 'tire_fitting' + | 'equestrian' + | 'fast_food' + | 'film_studio' + | 'fire_station' | 'fitness' - | 'sportcenter' - | 'sport_school' - | 'boat_station' - | 'office_service' - | 'spa' - | 'metro_bus' - | 'metro_light' - | 'railway' - | 'railway_station' - | 'port' - | 'airport' - | 'metro_cable' - | 'metro_funicular' - | 'metro_tram' - | 'metro' - | 'pier' - | 'bus' - | 'tram' - | 'metro_entrance' - | 'metro_monorail' - | 'taxi' - | 'hypermarket' - | 'petshop' - | 'bookstore' - | 'clothes_shop' - | 'furniture_store' | 'flower_shop' - | 'baby_shop' - | 'auto' - | 'malls' - | 'sport' - | 'supermarket' - | 'skating_rink' - | 'swimming_pool' - | 'waterfall' - | 'spring' + | 'forest' | 'fountain' - | 'theatre' + | 'furniture_store' + | 'garden' + | 'gasstation' + | 'government' + | 'hairdressers' + | 'haulier' + | 'helicopter' + | 'hospital' + | 'hotels' + | 'hypermarket' + | 'industrial_enterprise' + | 'information' + | 'kindergarten' | 'landmark' + | 'laundry' + | 'library' + | 'malls' + | 'medicine' | 'memorable_event' + | 'metro' + | 'metro_bus' + | 'metro_cable' + | 'metro_entrance' + | 'metro_funicular' + | 'metro_light' + | 'metro_monorail' + | 'metro_tram' + | 'mobile_phones' + | 'money_coin' + | 'monument' + | 'mosque' + | 'mountain' | 'museum' - | 'beach' - | 'zoo' - | 'garden' + | 'office' + | 'office_service' + | 'orthodox_church' | 'park' - | 'forest' + | 'pavilion' + | 'pet_playground' + | 'petshop' + | 'photo' + | 'picnic' + | 'pier' + | 'playground' + | 'police' + | 'police_post' + | 'port' + | 'post_office' + | 'printing_services' + | 'protestant_church' + | 'racing' + | 'railway' + | 'railway_station' + | 'recycling' + | 'restaurants' | 'rezervation' - | 'equestrian' - | 'stadium' | 'sanatorium' - | 'mountain' - | 'restaurants' - | 'cafe' - | 'fast_food' + | 'science' + | 'skating_rink' + | 'software' + | 'spa' + | 'sport' + | 'sport_school' + | 'sportcenter' + | 'spring' + | 'stadium' + | 'supermarket' | 'sushi' - | 'bars' - | 'confectionary' - | 'wc' + | 'swimming_pool' + | 'synagogue' + | 'tailor' + | 'taxi' + | 'theatre' + | 'ticket_office' + | 'tire_fitting' + | 'tram' | 'trash' - | 'helicopter' - | 'building' - | 'bench' - | 'aviary' - | 'money_coin' - | 'recycling' - | 'barbeque' - | 'pavilion' - | 'picnic' - | 'monument' - | 'childrens_playground' - | 'pet_playground' - | 'playground' + | 'travel_agency' | 'viewpoint' - | 'bike' - | 'car_park'; + | 'waterfall' + | 'wc' + | 'zoo'; export {IconName}; From 9a52d37abb5216b8a574eebc09d930d277fa62f4 Mon Sep 17 00:00:00 2001 From: Matthew Kalinin Date: Mon, 1 Apr 2024 16:30:54 +0300 Subject: [PATCH 12/48] filter fill-opacity icons --- docs/icons.md | 246 +++++++++++++++++++-------------------- src/icons/types/icons.ts | 2 - 2 files changed, 122 insertions(+), 126 deletions(-) diff --git a/docs/icons.md b/docs/icons.md index aa37f84..28f5b74 100644 --- a/docs/icons.md +++ b/docs/icons.md @@ -2,127 +2,125 @@ # List of supported icons -| Name | Normal Size | Small Size | -| --------------------- | ------------------------------------------------------------------------- | ------------------------------------------------------------------------- | -| airport | ![airport](../../static/icons/airport_24.svg) | ![airport](../../static/icons/airport_14.svg) | -| attraction | ![attraction](../../static/icons/attraction_24.svg) | ![attraction](../../static/icons/attraction_14.svg) | -| auto | ![auto](../../static/icons/auto_24.svg) | ![auto](../../static/icons/auto_14.svg) | -| aviary | ![aviary](../../static/icons/aviary_24.svg) | none | -| baby_shop | ![baby_shop](../../static/icons/baby_shop_24.svg) | ![baby_shop](../../static/icons/baby_shop_14.svg) | -| banks | ![banks](../../static/icons/banks_24.svg) | ![banks](../../static/icons/banks_14.svg) | -| barbeque | ![barbeque](../../static/icons/barbeque_24.svg) | none | -| bars | ![bars](../../static/icons/bars_24.svg) | ![bars](../../static/icons/bars_14.svg) | -| beach | ![beach](../../static/icons/beach_24.svg) | ![beach](../../static/icons/beach_14.svg) | -| bench | ![bench](../../static/icons/bench_24.svg) | none | -| bike | ![bike](../../static/icons/bike_24.svg) | none | -| bike_rent | ![bike_rent](../../static/icons/bike_rent_24.svg) | ![bike_rent](../../static/icons/bike_rent_14.svg) | -| boat_station | ![boat_station](../../static/icons/boat_station_24.svg) | ![boat_station](../../static/icons/boat_station_14.svg) | -| bookstore | ![bookstore](../../static/icons/bookstore_24.svg) | ![bookstore](../../static/icons/bookstore_14.svg) | -| buddhism | ![buddhism](../../static/icons/buddhism_24.svg) | ![buddhism](../../static/icons/buddhism_14.svg) | -| building | ![building](../../static/icons/building_24.svg) | none | -| bus | ![bus](../../static/icons/bus_24.svg) | none | -| cafe | ![cafe](../../static/icons/cafe_24.svg) | ![cafe](../../static/icons/cafe_14.svg) | -| car_park | ![car_park](../../static/icons/car_park_24.svg) | none | -| catholic_church | ![catholic_church](../../static/icons/catholic_church_24.svg) | ![catholic_church](../../static/icons/catholic_church_14.svg) | -| cemetery | ![cemetery](../../static/icons/cemetery_24.svg) | ![cemetery](../../static/icons/cemetery_14.svg) | -| childrens_playground | ![childrens_playground](../../static/icons/childrens_playground_24.svg) | none | -| cinemas | ![cinemas](../../static/icons/cinemas_24.svg) | ![cinemas](../../static/icons/cinemas_14.svg) | -| clothes_shop | ![clothes_shop](../../static/icons/clothes_shop_24.svg) | ![clothes_shop](../../static/icons/clothes_shop_14.svg) | -| college | ![college](../../static/icons/college_24.svg) | ![college](../../static/icons/college_14.svg) | -| concert_hall | ![concert_hall](../../static/icons/concert_hall_24.svg) | ![concert_hall](../../static/icons/concert_hall_14.svg) | -| confectionary | ![confectionary](../../static/icons/confectionary_24.svg) | ![confectionary](../../static/icons/confectionary_14.svg) | -| currency_exchange | ![currency_exchange](../../static/icons/currency_exchange_24.svg) | ![currency_exchange](../../static/icons/currency_exchange_14.svg) | -| dental | ![dental](../../static/icons/dental_24.svg) | ![dental](../../static/icons/dental_14.svg) | -| driving_school | ![driving_school](../../static/icons/driving_school_24.svg) | ![driving_school](../../static/icons/driving_school_14.svg) | -| drugstores | ![drugstores](../../static/icons/drugstores_24.svg) | ![drugstores](../../static/icons/drugstores_14.svg) | -| dry_cleaning | ![dry_cleaning](../../static/icons/dry_cleaning_24.svg) | ![dry_cleaning](../../static/icons/dry_cleaning_14.svg) | -| equestrian | ![equestrian](../../static/icons/equestrian_24.svg) | ![equestrian](../../static/icons/equestrian_14.svg) | -| fast_food | ![fast_food](../../static/icons/fast_food_24.svg) | ![fast_food](../../static/icons/fast_food_14.svg) | -| film_studio | ![film_studio](../../static/icons/film_studio_24.svg) | ![film_studio](../../static/icons/film_studio_14.svg) | -| fire_station | ![fire_station](../../static/icons/fire_station_24.svg) | ![fire_station](../../static/icons/fire_station_14.svg) | -| fitness | ![fitness](../../static/icons/fitness_24.svg) | ![fitness](../../static/icons/fitness_14.svg) | -| flower_shop | ![flower_shop](../../static/icons/flower_shop_24.svg) | ![flower_shop](../../static/icons/flower_shop_14.svg) | -| forest | ![forest](../../static/icons/forest_24.svg) | ![forest](../../static/icons/forest_14.svg) | -| fountain | ![fountain](../../static/icons/fountain_24.svg) | none | -| furniture_store | ![furniture_store](../../static/icons/furniture_store_24.svg) | ![furniture_store](../../static/icons/furniture_store_14.svg) | -| garden | ![garden](../../static/icons/garden_24.svg) | ![garden](../../static/icons/garden_14.svg) | -| gasstation | ![gasstation](../../static/icons/gasstation_24.svg) | ![gasstation](../../static/icons/gasstation_14.svg) | -| government | ![government](../../static/icons/government_24.svg) | ![government](../../static/icons/government_14.svg) | -| hairdressers | ![hairdressers](../../static/icons/hairdressers_24.svg) | ![hairdressers](../../static/icons/hairdressers_14.svg) | -| haulier | ![haulier](../../static/icons/haulier_24.svg) | ![haulier](../../static/icons/haulier_14.svg) | -| helicopter | ![helicopter](../../static/icons/helicopter_24.svg) | none | -| hospital | ![hospital](../../static/icons/hospital_24.svg) | ![hospital](../../static/icons/hospital_14.svg) | -| hotels | ![hotels](../../static/icons/hotels_24.svg) | ![hotels](../../static/icons/hotels_14.svg) | -| hypermarket | ![hypermarket](../../static/icons/hypermarket_24.svg) | ![hypermarket](../../static/icons/hypermarket_14.svg) | -| industrial_enterprise | ![industrial_enterprise](../../static/icons/industrial_enterprise_24.svg) | ![industrial_enterprise](../../static/icons/industrial_enterprise_14.svg) | -| information | ![information](../../static/icons/information_24.svg) | ![information](../../static/icons/information_14.svg) | -| kindergarten | ![kindergarten](../../static/icons/kindergarten_24.svg) | ![kindergarten](../../static/icons/kindergarten_14.svg) | -| landmark | ![landmark](../../static/icons/landmark_24.svg) | ![landmark](../../static/icons/landmark_14.svg) | -| laundry | ![laundry](../../static/icons/laundry_24.svg) | ![laundry](../../static/icons/laundry_14.svg) | -| library | ![library](../../static/icons/library_24.svg) | ![library](../../static/icons/library_14.svg) | -| malls | ![malls](../../static/icons/malls_24.svg) | ![malls](../../static/icons/malls_14.svg) | -| medicine | ![medicine](../../static/icons/medicine_24.svg) | ![medicine](../../static/icons/medicine_14.svg) | -| memorable_event | ![memorable_event](../../static/icons/memorable_event_24.svg) | ![memorable_event](../../static/icons/memorable_event_14.svg) | -| metro | ![metro](../../static/icons/metro_24.svg) | ![metro](../../static/icons/metro_14.svg) | -| metro_bus | ![metro_bus](../../static/icons/metro_bus_24.svg) | ![metro_bus](../../static/icons/metro_bus_14.svg) | -| metro_cable | ![metro_cable](../../static/icons/metro_cable_24.svg) | ![metro_cable](../../static/icons/metro_cable_14.svg) | -| metro_entrance | ![metro_entrance](../../static/icons/metro_entrance_24.svg) | none | -| metro_funicular | ![metro_funicular](../../static/icons/metro_funicular_24.svg) | ![metro_funicular](../../static/icons/metro_funicular_14.svg) | -| metro_light | ![metro_light](../../static/icons/metro_light_24.svg) | ![metro_light](../../static/icons/metro_light_14.svg) | -| metro_monorail | ![metro_monorail](../../static/icons/metro_monorail_24.svg) | ![metro_monorail](../../static/icons/metro_monorail_14.svg) | -| metro_tram | ![metro_tram](../../static/icons/metro_tram_24.svg) | ![metro_tram](../../static/icons/metro_tram_14.svg) | -| mobile_phones | ![mobile_phones](../../static/icons/mobile_phones_24.svg) | ![mobile_phones](../../static/icons/mobile_phones_14.svg) | -| money_coin | ![money_coin](../../static/icons/money_coin_24.svg) | none | -| monument | ![monument](../../static/icons/monument_24.svg) | none | -| mosque | ![mosque](../../static/icons/mosque_24.svg) | ![mosque](../../static/icons/mosque_14.svg) | -| mountain | ![mountain](../../static/icons/mountain_24.svg) | none | -| museum | ![museum](../../static/icons/museum_24.svg) | ![museum](../../static/icons/museum_14.svg) | -| office | ![office](../../static/icons/office_24.svg) | ![office](../../static/icons/office_14.svg) | -| office_service | ![office_service](../../static/icons/office_service_24.svg) | ![office_service](../../static/icons/office_service_14.svg) | -| orthodox_church | ![orthodox_church](../../static/icons/orthodox_church_24.svg) | ![orthodox_church](../../static/icons/orthodox_church_14.svg) | -| park | ![park](../../static/icons/park_24.svg) | ![park](../../static/icons/park_14.svg) | -| pavilion | ![pavilion](../../static/icons/pavilion_24.svg) | none | -| pet_playground | ![pet_playground](../../static/icons/pet_playground_24.svg) | none | -| petshop | ![petshop](../../static/icons/petshop_24.svg) | ![petshop](../../static/icons/petshop_14.svg) | -| photo | ![photo](../../static/icons/photo_24.svg) | ![photo](../../static/icons/photo_14.svg) | -| picnic | ![picnic](../../static/icons/picnic_24.svg) | none | -| pier | ![pier](../../static/icons/pier_24.svg) | none | -| playground | ![playground](../../static/icons/playground_24.svg) | none | -| police | ![police](../../static/icons/police_24.svg) | ![police](../../static/icons/police_14.svg) | -| police_post | ![police_post](../../static/icons/police_post_24.svg) | ![police_post](../../static/icons/police_post_14.svg) | -| port | ![port](../../static/icons/port_24.svg) | ![port](../../static/icons/port_14.svg) | -| post_office | ![post_office](../../static/icons/post_office_24.svg) | ![post_office](../../static/icons/post_office_14.svg) | -| printing_services | ![printing_services](../../static/icons/printing_services_24.svg) | ![printing_services](../../static/icons/printing_services_14.svg) | -| protestant_church | ![protestant_church](../../static/icons/protestant_church_24.svg) | ![protestant_church](../../static/icons/protestant_church_14.svg) | -| racing | ![racing](../../static/icons/racing_24.svg) | ![racing](../../static/icons/racing_14.svg) | -| railway | ![railway](../../static/icons/railway_24.svg) | ![railway](../../static/icons/railway_14.svg) | -| railway_station | ![railway_station](../../static/icons/railway_station_24.svg) | ![railway_station](../../static/icons/railway_station_14.svg) | -| recycling | ![recycling](../../static/icons/recycling_24.svg) | none | -| restaurants | ![restaurants](../../static/icons/restaurants_24.svg) | ![restaurants](../../static/icons/restaurants_14.svg) | -| rezervation | ![rezervation](../../static/icons/rezervation_24.svg) | ![rezervation](../../static/icons/rezervation_14.svg) | -| sanatorium | ![sanatorium](../../static/icons/sanatorium_24.svg) | ![sanatorium](../../static/icons/sanatorium_14.svg) | -| science | ![science](../../static/icons/science_24.svg) | ![science](../../static/icons/science_14.svg) | -| skating_rink | ![skating_rink](../../static/icons/skating_rink_24.svg) | ![skating_rink](../../static/icons/skating_rink_14.svg) | -| software | ![software](../../static/icons/software_24.svg) | ![software](../../static/icons/software_14.svg) | -| spa | ![spa](../../static/icons/spa_24.svg) | ![spa](../../static/icons/spa_14.svg) | -| sport | ![sport](../../static/icons/sport_24.svg) | none | -| sport_school | ![sport_school](../../static/icons/sport_school_24.svg) | none | -| sportcenter | ![sportcenter](../../static/icons/sportcenter_24.svg) | ![sportcenter](../../static/icons/sportcenter_14.svg) | -| spring | ![spring](../../static/icons/spring_24.svg) | none | -| stadium | ![stadium](../../static/icons/stadium_24.svg) | ![stadium](../../static/icons/stadium_14.svg) | -| supermarket | ![supermarket](../../static/icons/supermarket_24.svg) | ![supermarket](../../static/icons/supermarket_14.svg) | -| sushi | ![sushi](../../static/icons/sushi_24.svg) | ![sushi](../../static/icons/sushi_14.svg) | -| swimming_pool | ![swimming_pool](../../static/icons/swimming_pool_24.svg) | ![swimming_pool](../../static/icons/swimming_pool_14.svg) | -| synagogue | ![synagogue](../../static/icons/synagogue_24.svg) | ![synagogue](../../static/icons/synagogue_14.svg) | -| tailor | ![tailor](../../static/icons/tailor_24.svg) | ![tailor](../../static/icons/tailor_14.svg) | -| taxi | ![taxi](../../static/icons/taxi_24.svg) | ![taxi](../../static/icons/taxi_14.svg) | -| theatre | ![theatre](../../static/icons/theatre_24.svg) | ![theatre](../../static/icons/theatre_14.svg) | -| ticket_office | ![ticket_office](../../static/icons/ticket_office_24.svg) | ![ticket_office](../../static/icons/ticket_office_14.svg) | -| tire_fitting | ![tire_fitting](../../static/icons/tire_fitting_24.svg) | ![tire_fitting](../../static/icons/tire_fitting_14.svg) | -| tram | ![tram](../../static/icons/tram_24.svg) | none | -| trash | ![trash](../../static/icons/trash_24.svg) | none | -| travel_agency | ![travel_agency](../../static/icons/travel_agency_24.svg) | ![travel_agency](../../static/icons/travel_agency_14.svg) | -| viewpoint | ![viewpoint](../../static/icons/viewpoint_24.svg) | none | -| waterfall | ![waterfall](../../static/icons/waterfall_24.svg) | none | -| wc | ![wc](../../static/icons/wc_24.svg) | none | -| zoo | ![zoo](../../static/icons/zoo_24.svg) | ![zoo](../../static/icons/zoo_14.svg) | +| Name | Normal Size | Small Size | +| --------------------- | ---------------------------------------------------------------------- | ---------------------------------------------------------------------- | +| airport | ![airport](../static/icons/airport_24.svg) | ![airport](../static/icons/airport_14.svg) | +| attraction | ![attraction](../static/icons/attraction_24.svg) | ![attraction](../static/icons/attraction_14.svg) | +| auto | ![auto](../static/icons/auto_24.svg) | ![auto](../static/icons/auto_14.svg) | +| aviary | ![aviary](../static/icons/aviary_24.svg) | none | +| baby_shop | ![baby_shop](../static/icons/baby_shop_24.svg) | ![baby_shop](../static/icons/baby_shop_14.svg) | +| banks | ![banks](../static/icons/banks_24.svg) | ![banks](../static/icons/banks_14.svg) | +| barbeque | ![barbeque](../static/icons/barbeque_24.svg) | none | +| bars | ![bars](../static/icons/bars_24.svg) | ![bars](../static/icons/bars_14.svg) | +| beach | ![beach](../static/icons/beach_24.svg) | ![beach](../static/icons/beach_14.svg) | +| bench | ![bench](../static/icons/bench_24.svg) | none | +| bike | ![bike](../static/icons/bike_24.svg) | none | +| bike_rent | ![bike_rent](../static/icons/bike_rent_24.svg) | ![bike_rent](../static/icons/bike_rent_14.svg) | +| boat_station | ![boat_station](../static/icons/boat_station_24.svg) | ![boat_station](../static/icons/boat_station_14.svg) | +| bookstore | ![bookstore](../static/icons/bookstore_24.svg) | ![bookstore](../static/icons/bookstore_14.svg) | +| buddhism | ![buddhism](../static/icons/buddhism_24.svg) | ![buddhism](../static/icons/buddhism_14.svg) | +| building | ![building](../static/icons/building_24.svg) | none | +| bus | ![bus](../static/icons/bus_24.svg) | none | +| cafe | ![cafe](../static/icons/cafe_24.svg) | ![cafe](../static/icons/cafe_14.svg) | +| car_park | ![car_park](../static/icons/car_park_24.svg) | none | +| catholic_church | ![catholic_church](../static/icons/catholic_church_24.svg) | ![catholic_church](../static/icons/catholic_church_14.svg) | +| cemetery | ![cemetery](../static/icons/cemetery_24.svg) | ![cemetery](../static/icons/cemetery_14.svg) | +| childrens_playground | ![childrens_playground](../static/icons/childrens_playground_24.svg) | none | +| cinemas | ![cinemas](../static/icons/cinemas_24.svg) | ![cinemas](../static/icons/cinemas_14.svg) | +| clothes_shop | ![clothes_shop](../static/icons/clothes_shop_24.svg) | ![clothes_shop](../static/icons/clothes_shop_14.svg) | +| college | ![college](../static/icons/college_24.svg) | ![college](../static/icons/college_14.svg) | +| concert_hall | ![concert_hall](../static/icons/concert_hall_24.svg) | ![concert_hall](../static/icons/concert_hall_14.svg) | +| confectionary | ![confectionary](../static/icons/confectionary_24.svg) | ![confectionary](../static/icons/confectionary_14.svg) | +| currency_exchange | ![currency_exchange](../static/icons/currency_exchange_24.svg) | ![currency_exchange](../static/icons/currency_exchange_14.svg) | +| dental | ![dental](../static/icons/dental_24.svg) | ![dental](../static/icons/dental_14.svg) | +| driving_school | ![driving_school](../static/icons/driving_school_24.svg) | ![driving_school](../static/icons/driving_school_14.svg) | +| drugstores | ![drugstores](../static/icons/drugstores_24.svg) | ![drugstores](../static/icons/drugstores_14.svg) | +| dry_cleaning | ![dry_cleaning](../static/icons/dry_cleaning_24.svg) | ![dry_cleaning](../static/icons/dry_cleaning_14.svg) | +| equestrian | ![equestrian](../static/icons/equestrian_24.svg) | ![equestrian](../static/icons/equestrian_14.svg) | +| fast_food | ![fast_food](../static/icons/fast_food_24.svg) | ![fast_food](../static/icons/fast_food_14.svg) | +| film_studio | ![film_studio](../static/icons/film_studio_24.svg) | ![film_studio](../static/icons/film_studio_14.svg) | +| fire_station | ![fire_station](../static/icons/fire_station_24.svg) | ![fire_station](../static/icons/fire_station_14.svg) | +| fitness | ![fitness](../static/icons/fitness_24.svg) | ![fitness](../static/icons/fitness_14.svg) | +| flower_shop | ![flower_shop](../static/icons/flower_shop_24.svg) | ![flower_shop](../static/icons/flower_shop_14.svg) | +| forest | ![forest](../static/icons/forest_24.svg) | ![forest](../static/icons/forest_14.svg) | +| fountain | ![fountain](../static/icons/fountain_24.svg) | none | +| furniture_store | ![furniture_store](../static/icons/furniture_store_24.svg) | ![furniture_store](../static/icons/furniture_store_14.svg) | +| garden | ![garden](../static/icons/garden_24.svg) | ![garden](../static/icons/garden_14.svg) | +| gasstation | ![gasstation](../static/icons/gasstation_24.svg) | ![gasstation](../static/icons/gasstation_14.svg) | +| government | ![government](../static/icons/government_24.svg) | ![government](../static/icons/government_14.svg) | +| hairdressers | ![hairdressers](../static/icons/hairdressers_24.svg) | ![hairdressers](../static/icons/hairdressers_14.svg) | +| haulier | ![haulier](../static/icons/haulier_24.svg) | ![haulier](../static/icons/haulier_14.svg) | +| helicopter | ![helicopter](../static/icons/helicopter_24.svg) | none | +| hospital | ![hospital](../static/icons/hospital_24.svg) | ![hospital](../static/icons/hospital_14.svg) | +| hotels | ![hotels](../static/icons/hotels_24.svg) | ![hotels](../static/icons/hotels_14.svg) | +| hypermarket | ![hypermarket](../static/icons/hypermarket_24.svg) | ![hypermarket](../static/icons/hypermarket_14.svg) | +| industrial_enterprise | ![industrial_enterprise](../static/icons/industrial_enterprise_24.svg) | ![industrial_enterprise](../static/icons/industrial_enterprise_14.svg) | +| information | ![information](../static/icons/information_24.svg) | ![information](../static/icons/information_14.svg) | +| kindergarten | ![kindergarten](../static/icons/kindergarten_24.svg) | ![kindergarten](../static/icons/kindergarten_14.svg) | +| landmark | ![landmark](../static/icons/landmark_24.svg) | ![landmark](../static/icons/landmark_14.svg) | +| laundry | ![laundry](../static/icons/laundry_24.svg) | ![laundry](../static/icons/laundry_14.svg) | +| library | ![library](../static/icons/library_24.svg) | ![library](../static/icons/library_14.svg) | +| malls | ![malls](../static/icons/malls_24.svg) | ![malls](../static/icons/malls_14.svg) | +| medicine | ![medicine](../static/icons/medicine_24.svg) | ![medicine](../static/icons/medicine_14.svg) | +| memorable_event | ![memorable_event](../static/icons/memorable_event_24.svg) | ![memorable_event](../static/icons/memorable_event_14.svg) | +| metro | ![metro](../static/icons/metro_24.svg) | ![metro](../static/icons/metro_14.svg) | +| metro_bus | ![metro_bus](../static/icons/metro_bus_24.svg) | ![metro_bus](../static/icons/metro_bus_14.svg) | +| metro_cable | ![metro_cable](../static/icons/metro_cable_24.svg) | ![metro_cable](../static/icons/metro_cable_14.svg) | +| metro_entrance | ![metro_entrance](../static/icons/metro_entrance_24.svg) | none | +| metro_funicular | ![metro_funicular](../static/icons/metro_funicular_24.svg) | ![metro_funicular](../static/icons/metro_funicular_14.svg) | +| metro_light | ![metro_light](../static/icons/metro_light_24.svg) | ![metro_light](../static/icons/metro_light_14.svg) | +| metro_monorail | ![metro_monorail](../static/icons/metro_monorail_24.svg) | ![metro_monorail](../static/icons/metro_monorail_14.svg) | +| metro_tram | ![metro_tram](../static/icons/metro_tram_24.svg) | ![metro_tram](../static/icons/metro_tram_14.svg) | +| mobile_phones | ![mobile_phones](../static/icons/mobile_phones_24.svg) | ![mobile_phones](../static/icons/mobile_phones_14.svg) | +| money_coin | ![money_coin](../static/icons/money_coin_24.svg) | none | +| monument | ![monument](../static/icons/monument_24.svg) | none | +| mosque | ![mosque](../static/icons/mosque_24.svg) | ![mosque](../static/icons/mosque_14.svg) | +| mountain | ![mountain](../static/icons/mountain_24.svg) | none | +| museum | ![museum](../static/icons/museum_24.svg) | ![museum](../static/icons/museum_14.svg) | +| office | ![office](../static/icons/office_24.svg) | ![office](../static/icons/office_14.svg) | +| office_service | ![office_service](../static/icons/office_service_24.svg) | ![office_service](../static/icons/office_service_14.svg) | +| orthodox_church | ![orthodox_church](../static/icons/orthodox_church_24.svg) | ![orthodox_church](../static/icons/orthodox_church_14.svg) | +| park | ![park](../static/icons/park_24.svg) | ![park](../static/icons/park_14.svg) | +| pavilion | ![pavilion](../static/icons/pavilion_24.svg) | none | +| pet_playground | ![pet_playground](../static/icons/pet_playground_24.svg) | none | +| petshop | ![petshop](../static/icons/petshop_24.svg) | ![petshop](../static/icons/petshop_14.svg) | +| photo | ![photo](../static/icons/photo_24.svg) | ![photo](../static/icons/photo_14.svg) | +| picnic | ![picnic](../static/icons/picnic_24.svg) | none | +| pier | ![pier](../static/icons/pier_24.svg) | none | +| playground | ![playground](../static/icons/playground_24.svg) | none | +| police | ![police](../static/icons/police_24.svg) | ![police](../static/icons/police_14.svg) | +| police_post | ![police_post](../static/icons/police_post_24.svg) | ![police_post](../static/icons/police_post_14.svg) | +| port | ![port](../static/icons/port_24.svg) | ![port](../static/icons/port_14.svg) | +| post_office | ![post_office](../static/icons/post_office_24.svg) | ![post_office](../static/icons/post_office_14.svg) | +| printing_services | ![printing_services](../static/icons/printing_services_24.svg) | ![printing_services](../static/icons/printing_services_14.svg) | +| protestant_church | ![protestant_church](../static/icons/protestant_church_24.svg) | ![protestant_church](../static/icons/protestant_church_14.svg) | +| racing | ![racing](../static/icons/racing_24.svg) | ![racing](../static/icons/racing_14.svg) | +| railway | ![railway](../static/icons/railway_24.svg) | ![railway](../static/icons/railway_14.svg) | +| railway_station | ![railway_station](../static/icons/railway_station_24.svg) | ![railway_station](../static/icons/railway_station_14.svg) | +| recycling | ![recycling](../static/icons/recycling_24.svg) | none | +| restaurants | ![restaurants](../static/icons/restaurants_24.svg) | ![restaurants](../static/icons/restaurants_14.svg) | +| rezervation | ![rezervation](../static/icons/rezervation_24.svg) | ![rezervation](../static/icons/rezervation_14.svg) | +| sanatorium | ![sanatorium](../static/icons/sanatorium_24.svg) | ![sanatorium](../static/icons/sanatorium_14.svg) | +| science | ![science](../static/icons/science_24.svg) | ![science](../static/icons/science_14.svg) | +| skating_rink | ![skating_rink](../static/icons/skating_rink_24.svg) | ![skating_rink](../static/icons/skating_rink_14.svg) | +| software | ![software](../static/icons/software_24.svg) | ![software](../static/icons/software_14.svg) | +| spa | ![spa](../static/icons/spa_24.svg) | ![spa](../static/icons/spa_14.svg) | +| sportcenter | ![sportcenter](../static/icons/sportcenter_24.svg) | ![sportcenter](../static/icons/sportcenter_14.svg) | +| spring | ![spring](../static/icons/spring_24.svg) | none | +| stadium | ![stadium](../static/icons/stadium_24.svg) | ![stadium](../static/icons/stadium_14.svg) | +| supermarket | ![supermarket](../static/icons/supermarket_24.svg) | ![supermarket](../static/icons/supermarket_14.svg) | +| sushi | ![sushi](../static/icons/sushi_24.svg) | ![sushi](../static/icons/sushi_14.svg) | +| swimming_pool | ![swimming_pool](../static/icons/swimming_pool_24.svg) | ![swimming_pool](../static/icons/swimming_pool_14.svg) | +| synagogue | ![synagogue](../static/icons/synagogue_24.svg) | ![synagogue](../static/icons/synagogue_14.svg) | +| tailor | ![tailor](../static/icons/tailor_24.svg) | ![tailor](../static/icons/tailor_14.svg) | +| taxi | ![taxi](../static/icons/taxi_24.svg) | ![taxi](../static/icons/taxi_14.svg) | +| theatre | ![theatre](../static/icons/theatre_24.svg) | ![theatre](../static/icons/theatre_14.svg) | +| ticket_office | ![ticket_office](../static/icons/ticket_office_24.svg) | ![ticket_office](../static/icons/ticket_office_14.svg) | +| tire_fitting | ![tire_fitting](../static/icons/tire_fitting_24.svg) | ![tire_fitting](../static/icons/tire_fitting_14.svg) | +| tram | ![tram](../static/icons/tram_24.svg) | none | +| trash | ![trash](../static/icons/trash_24.svg) | none | +| travel_agency | ![travel_agency](../static/icons/travel_agency_24.svg) | ![travel_agency](../static/icons/travel_agency_14.svg) | +| viewpoint | ![viewpoint](../static/icons/viewpoint_24.svg) | none | +| waterfall | ![waterfall](../static/icons/waterfall_24.svg) | none | +| wc | ![wc](../static/icons/wc_24.svg) | none | +| zoo | ![zoo](../static/icons/zoo_24.svg) | ![zoo](../static/icons/zoo_14.svg) | diff --git a/src/icons/types/icons.ts b/src/icons/types/icons.ts index bb83046..2ceb08d 100644 --- a/src/icons/types/icons.ts +++ b/src/icons/types/icons.ts @@ -101,8 +101,6 @@ type IconName = | 'skating_rink' | 'software' | 'spa' - | 'sport' - | 'sport_school' | 'sportcenter' | 'spring' | 'stadium' From 82e2749b0387a7a227429d8948c5770f40e14a45 Mon Sep 17 00:00:00 2001 From: Matthew Kalinin Date: Mon, 1 Apr 2024 18:33:18 +0300 Subject: [PATCH 13/48] default marker --- src/MMapDefaultMarker/index.css | 13 ++++++++++++ src/MMapDefaultMarker/index.ts | 36 +++++++++++++++++++++++++++++++++ src/MMapDefaultMarker/pin.svg | 1 + 3 files changed, 50 insertions(+) create mode 100644 src/MMapDefaultMarker/index.css create mode 100644 src/MMapDefaultMarker/index.ts create mode 100644 src/MMapDefaultMarker/pin.svg diff --git a/src/MMapDefaultMarker/index.css b/src/MMapDefaultMarker/index.css new file mode 100644 index 0000000..6e64195 --- /dev/null +++ b/src/MMapDefaultMarker/index.css @@ -0,0 +1,13 @@ +.mappable--point { + box-sizing: border-box; + height: 8px; + width: 8px; + border: 2px solid #f8f8f8; + position: absolute; + transform: translate(-50%, -50%); + border-radius: 50%; +} + +.mappable--point svg { + transform: translate(calc(-50% + 2px), calc(-100% + 2px)); +} diff --git a/src/MMapDefaultMarker/index.ts b/src/MMapDefaultMarker/index.ts new file mode 100644 index 0000000..e76a1ea --- /dev/null +++ b/src/MMapDefaultMarker/index.ts @@ -0,0 +1,36 @@ +import {MMapMarker, MMapMarkerProps} from '@mappable-world/mappable-types'; +import {IconName} from '../icons/types/icons'; +import pin from './pin.svg'; +import './index.css'; + +export type MMapDefaultMarkerProps = MMapMarkerProps & { + name: IconName; + color: string; +}; + +export class MMapDefaultMarker extends mappable.MMapComplexEntity { + private _marker: MMapMarker; + private _markerElement: HTMLElement; + + constructor(props: MMapDefaultMarkerProps) { + super(props); + } + + protected _onAttach(): void { + this._markerElement = document.createElement('mappable'); + this._markerElement.classList.add('mappable--point'); + this._markerElement.innerHTML = pin; + this._markerElement.style.color = this._props.color; + this._markerElement.style.backgroundColor = this._props.color; + + this._marker = new mappable.MMapMarker(this._props, this._markerElement); + this.addChild(this._marker); + } + + protected _onUpdate(propsDiff: Partial): void { + if (propsDiff.color !== undefined) { + this._markerElement.style.color = this._props.color; + } + this._marker.update(this._props); + } +} diff --git a/src/MMapDefaultMarker/pin.svg b/src/MMapDefaultMarker/pin.svg new file mode 100644 index 0000000..db159ac --- /dev/null +++ b/src/MMapDefaultMarker/pin.svg @@ -0,0 +1 @@ + \ No newline at end of file From eed39179fd77b5ff44ce2c9317c6e44a46edb335 Mon Sep 17 00:00:00 2001 From: Matthew Kalinin Date: Mon, 1 Apr 2024 19:18:28 +0300 Subject: [PATCH 14/48] add nanospinner in cli tool --- tools/scripts/sync-icons.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/scripts/sync-icons.ts b/tools/scripts/sync-icons.ts index def309d..fcbcdbf 100644 --- a/tools/scripts/sync-icons.ts +++ b/tools/scripts/sync-icons.ts @@ -4,6 +4,7 @@ import {generateImports} from '../icons/generate-imports'; import {generateIconsTypes} from '../icons/generate-types'; import {getUniqNames} from '../icons/get-uniq-names'; import {updateIcons} from '../icons/update-icons'; +import {createSpinner} from 'nanospinner'; async function main() { const spinner = createSpinner(); From 1476307e353991951e5c218e8643031830b81ebe Mon Sep 17 00:00:00 2001 From: Matthew Kalinin Date: Tue, 2 Apr 2024 12:37:18 +0300 Subject: [PATCH 15/48] path as common constants, nanospinner error in catch --- .gitignore | 2 ++ src/icons/{types/icons.ts => icon-name.ts} | 0 tools/scripts/sync-icons.ts | 1 - 3 files changed, 2 insertions(+), 1 deletion(-) rename src/icons/{types/icons.ts => icon-name.ts} (100%) diff --git a/.gitignore b/.gitignore index 3ba0c2c..7f96c5b 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,5 @@ dist node_modules .env .DS_Store +# FIXME: ignore static folder for a while +static \ No newline at end of file diff --git a/src/icons/types/icons.ts b/src/icons/icon-name.ts similarity index 100% rename from src/icons/types/icons.ts rename to src/icons/icon-name.ts diff --git a/tools/scripts/sync-icons.ts b/tools/scripts/sync-icons.ts index fcbcdbf..def309d 100644 --- a/tools/scripts/sync-icons.ts +++ b/tools/scripts/sync-icons.ts @@ -4,7 +4,6 @@ import {generateImports} from '../icons/generate-imports'; import {generateIconsTypes} from '../icons/generate-types'; import {getUniqNames} from '../icons/get-uniq-names'; import {updateIcons} from '../icons/update-icons'; -import {createSpinner} from 'nanospinner'; async function main() { const spinner = createSpinner(); From 07c1a4b0ff16dc055162e9fae19b5f898f5b8f68 Mon Sep 17 00:00:00 2001 From: Matthew Kalinin Date: Tue, 2 Apr 2024 15:33:15 +0300 Subject: [PATCH 16/48] show pin with icon --- src/MMapDefaultMarker/index.css | 11 +++++++++-- src/MMapDefaultMarker/index.ts | 22 ++++++++++++++++++++-- src/MMapDefaultMarker/pin.svg | 2 +- src/icons/icon-name.ts | 4 +--- src/icons/icon-size.ts | 1 + 5 files changed, 32 insertions(+), 8 deletions(-) create mode 100644 src/icons/icon-size.ts diff --git a/src/MMapDefaultMarker/index.css b/src/MMapDefaultMarker/index.css index 6e64195..510c02e 100644 --- a/src/MMapDefaultMarker/index.css +++ b/src/MMapDefaultMarker/index.css @@ -8,6 +8,13 @@ border-radius: 50%; } -.mappable--point svg { - transform: translate(calc(-50% + 2px), calc(-100% + 2px)); +.mappable--pin { + position: absolute; + transform: translate(calc(-50% + 2px), calc(-100%)); +} + +.mappable--icon { + color: #fffffffc; + position: absolute; + transform: translate(calc(-24px - 10px),10px); } diff --git a/src/MMapDefaultMarker/index.ts b/src/MMapDefaultMarker/index.ts index e76a1ea..b20bae8 100644 --- a/src/MMapDefaultMarker/index.ts +++ b/src/MMapDefaultMarker/index.ts @@ -1,5 +1,6 @@ import {MMapMarker, MMapMarkerProps} from '@mappable-world/mappable-types'; -import {IconName} from '../icons/types/icons'; +import {IconName} from '../icons/icon-name'; +import {icons} from '../icons'; import pin from './pin.svg'; import './index.css'; @@ -11,6 +12,8 @@ export type MMapDefaultMarkerProps = MMapMarkerProps & { export class MMapDefaultMarker extends mappable.MMapComplexEntity { private _marker: MMapMarker; private _markerElement: HTMLElement; + private _pin: HTMLElement; + private _icon: HTMLElement; constructor(props: MMapDefaultMarkerProps) { super(props); @@ -18,11 +21,22 @@ export class MMapDefaultMarker extends mappable.MMapComplexEntity): void { if (propsDiff.color !== undefined) { this._markerElement.style.color = this._props.color; + this._markerElement.style.backgroundColor = this._props.color; + } + if (propsDiff.name !== undefined) { + this._icon.innerHTML = icons[propsDiff.name].normal; } this._marker.update(this._props); } diff --git a/src/MMapDefaultMarker/pin.svg b/src/MMapDefaultMarker/pin.svg index db159ac..5d9a1b1 100644 --- a/src/MMapDefaultMarker/pin.svg +++ b/src/MMapDefaultMarker/pin.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/icons/icon-name.ts b/src/icons/icon-name.ts index 2ceb08d..2e1e72a 100644 --- a/src/icons/icon-name.ts +++ b/src/icons/icon-name.ts @@ -1,5 +1,5 @@ /** Don't edit manually. Generated by script: ./tools/icons/generate-types.ts */ -type IconName = +export type IconName = | 'airport' | 'attraction' | 'auto' @@ -120,5 +120,3 @@ type IconName = | 'waterfall' | 'wc' | 'zoo'; - -export {IconName}; diff --git a/src/icons/icon-size.ts b/src/icons/icon-size.ts new file mode 100644 index 0000000..4189049 --- /dev/null +++ b/src/icons/icon-size.ts @@ -0,0 +1 @@ +export type IconSize = 'normal' | 'small'; From 8ff8c5e8096c19e3a4936779a9aa3f5e768fa750 Mon Sep 17 00:00:00 2001 From: Matthew Kalinin Date: Wed, 3 Apr 2024 14:28:53 +0300 Subject: [PATCH 17/48] fetch colors from Figma --- src/MMapDefaultMarker/pin.svg | 2 +- src/icons/icon-colors.ts | 14 ++ src/icons/icons.ts | 339 ++++++++++++++++++++++++++++++++++ 3 files changed, 354 insertions(+), 1 deletion(-) create mode 100644 src/icons/icon-colors.ts create mode 100644 src/icons/icons.ts diff --git a/src/MMapDefaultMarker/pin.svg b/src/MMapDefaultMarker/pin.svg index 5d9a1b1..f0dcd75 100644 --- a/src/MMapDefaultMarker/pin.svg +++ b/src/MMapDefaultMarker/pin.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/icons/icon-colors.ts b/src/icons/icon-colors.ts new file mode 100644 index 0000000..5338910 --- /dev/null +++ b/src/icons/icon-colors.ts @@ -0,0 +1,14 @@ +/** Don't edit manually only color names. Generated by script: ./tools/icons/generate-colors.ts */ +export const iconColors = { + darkgray: {day: '#ada9a6ff', night: '#6f737aff'}, + pink: {day: '#ff8f96ff', night: '#b96066ff'}, + seawave: {day: '#62c0c6ff', night: '#468286ff'}, + orchid: {day: '#e096d0ff', night: '#916187ff'}, + steelblue: {day: '#498ea5ff', night: '#57a8c2ff'}, + bluebell: {day: '#9d9dc4ff', night: '#6767a3ff'}, + ceil: {day: '#88aecfff', night: '#537695ff'}, + green: {day: '#5ebd8cff', night: '#468c68ff'}, + darksalmon: {day: '#f09a75ff', night: '#ab6f55ff'} +} as const; +export const glyphColors = {day: '#ffffffff', night: '#303741ff'} as const; +export type IconColor = keyof typeof iconColors; diff --git a/src/icons/icons.ts b/src/icons/icons.ts new file mode 100644 index 0000000..1de3256 --- /dev/null +++ b/src/icons/icons.ts @@ -0,0 +1,339 @@ +/** Don't edit manually. Generated by script: ./tools/icons/generate-imports.ts */ +import airport_14 from '../../static/icons/airport_14.svg'; +import airport_24 from '../../static/icons/airport_24.svg'; +import attraction_14 from '../../static/icons/attraction_14.svg'; +import attraction_24 from '../../static/icons/attraction_24.svg'; +import auto_14 from '../../static/icons/auto_14.svg'; +import auto_24 from '../../static/icons/auto_24.svg'; +import aviary_24 from '../../static/icons/aviary_24.svg'; +import baby_shop_14 from '../../static/icons/baby_shop_14.svg'; +import baby_shop_24 from '../../static/icons/baby_shop_24.svg'; +import banks_14 from '../../static/icons/banks_14.svg'; +import banks_24 from '../../static/icons/banks_24.svg'; +import barbeque_24 from '../../static/icons/barbeque_24.svg'; +import bars_14 from '../../static/icons/bars_14.svg'; +import bars_24 from '../../static/icons/bars_24.svg'; +import beach_14 from '../../static/icons/beach_14.svg'; +import beach_24 from '../../static/icons/beach_24.svg'; +import bench_24 from '../../static/icons/bench_24.svg'; +import bike_24 from '../../static/icons/bike_24.svg'; +import bike_rent_14 from '../../static/icons/bike_rent_14.svg'; +import bike_rent_24 from '../../static/icons/bike_rent_24.svg'; +import boat_station_14 from '../../static/icons/boat_station_14.svg'; +import boat_station_24 from '../../static/icons/boat_station_24.svg'; +import bookstore_14 from '../../static/icons/bookstore_14.svg'; +import bookstore_24 from '../../static/icons/bookstore_24.svg'; +import buddhism_14 from '../../static/icons/buddhism_14.svg'; +import buddhism_24 from '../../static/icons/buddhism_24.svg'; +import building_24 from '../../static/icons/building_24.svg'; +import bus_24 from '../../static/icons/bus_24.svg'; +import cafe_14 from '../../static/icons/cafe_14.svg'; +import cafe_24 from '../../static/icons/cafe_24.svg'; +import car_park_24 from '../../static/icons/car_park_24.svg'; +import catholic_church_14 from '../../static/icons/catholic_church_14.svg'; +import catholic_church_24 from '../../static/icons/catholic_church_24.svg'; +import cemetery_14 from '../../static/icons/cemetery_14.svg'; +import cemetery_24 from '../../static/icons/cemetery_24.svg'; +import childrens_playground_24 from '../../static/icons/childrens_playground_24.svg'; +import cinemas_14 from '../../static/icons/cinemas_14.svg'; +import cinemas_24 from '../../static/icons/cinemas_24.svg'; +import clothes_shop_14 from '../../static/icons/clothes_shop_14.svg'; +import clothes_shop_24 from '../../static/icons/clothes_shop_24.svg'; +import college_14 from '../../static/icons/college_14.svg'; +import college_24 from '../../static/icons/college_24.svg'; +import concert_hall_14 from '../../static/icons/concert_hall_14.svg'; +import concert_hall_24 from '../../static/icons/concert_hall_24.svg'; +import confectionary_14 from '../../static/icons/confectionary_14.svg'; +import confectionary_24 from '../../static/icons/confectionary_24.svg'; +import currency_exchange_14 from '../../static/icons/currency_exchange_14.svg'; +import currency_exchange_24 from '../../static/icons/currency_exchange_24.svg'; +import dental_14 from '../../static/icons/dental_14.svg'; +import dental_24 from '../../static/icons/dental_24.svg'; +import driving_school_14 from '../../static/icons/driving_school_14.svg'; +import driving_school_24 from '../../static/icons/driving_school_24.svg'; +import drugstores_14 from '../../static/icons/drugstores_14.svg'; +import drugstores_24 from '../../static/icons/drugstores_24.svg'; +import dry_cleaning_14 from '../../static/icons/dry_cleaning_14.svg'; +import dry_cleaning_24 from '../../static/icons/dry_cleaning_24.svg'; +import equestrian_14 from '../../static/icons/equestrian_14.svg'; +import equestrian_24 from '../../static/icons/equestrian_24.svg'; +import fast_food_14 from '../../static/icons/fast_food_14.svg'; +import fast_food_24 from '../../static/icons/fast_food_24.svg'; +import film_studio_14 from '../../static/icons/film_studio_14.svg'; +import film_studio_24 from '../../static/icons/film_studio_24.svg'; +import fire_station_14 from '../../static/icons/fire_station_14.svg'; +import fire_station_24 from '../../static/icons/fire_station_24.svg'; +import fitness_14 from '../../static/icons/fitness_14.svg'; +import fitness_24 from '../../static/icons/fitness_24.svg'; +import flower_shop_14 from '../../static/icons/flower_shop_14.svg'; +import flower_shop_24 from '../../static/icons/flower_shop_24.svg'; +import forest_14 from '../../static/icons/forest_14.svg'; +import forest_24 from '../../static/icons/forest_24.svg'; +import fountain_24 from '../../static/icons/fountain_24.svg'; +import furniture_store_14 from '../../static/icons/furniture_store_14.svg'; +import furniture_store_24 from '../../static/icons/furniture_store_24.svg'; +import garden_14 from '../../static/icons/garden_14.svg'; +import garden_24 from '../../static/icons/garden_24.svg'; +import gasstation_14 from '../../static/icons/gasstation_14.svg'; +import gasstation_24 from '../../static/icons/gasstation_24.svg'; +import government_14 from '../../static/icons/government_14.svg'; +import government_24 from '../../static/icons/government_24.svg'; +import hairdressers_14 from '../../static/icons/hairdressers_14.svg'; +import hairdressers_24 from '../../static/icons/hairdressers_24.svg'; +import haulier_14 from '../../static/icons/haulier_14.svg'; +import haulier_24 from '../../static/icons/haulier_24.svg'; +import helicopter_24 from '../../static/icons/helicopter_24.svg'; +import hospital_14 from '../../static/icons/hospital_14.svg'; +import hospital_24 from '../../static/icons/hospital_24.svg'; +import hotels_14 from '../../static/icons/hotels_14.svg'; +import hotels_24 from '../../static/icons/hotels_24.svg'; +import hypermarket_14 from '../../static/icons/hypermarket_14.svg'; +import hypermarket_24 from '../../static/icons/hypermarket_24.svg'; +import industrial_enterprise_14 from '../../static/icons/industrial_enterprise_14.svg'; +import industrial_enterprise_24 from '../../static/icons/industrial_enterprise_24.svg'; +import information_14 from '../../static/icons/information_14.svg'; +import information_24 from '../../static/icons/information_24.svg'; +import kindergarten_14 from '../../static/icons/kindergarten_14.svg'; +import kindergarten_24 from '../../static/icons/kindergarten_24.svg'; +import landmark_14 from '../../static/icons/landmark_14.svg'; +import landmark_24 from '../../static/icons/landmark_24.svg'; +import laundry_14 from '../../static/icons/laundry_14.svg'; +import laundry_24 from '../../static/icons/laundry_24.svg'; +import library_14 from '../../static/icons/library_14.svg'; +import library_24 from '../../static/icons/library_24.svg'; +import malls_14 from '../../static/icons/malls_14.svg'; +import malls_24 from '../../static/icons/malls_24.svg'; +import medicine_14 from '../../static/icons/medicine_14.svg'; +import medicine_24 from '../../static/icons/medicine_24.svg'; +import memorable_event_14 from '../../static/icons/memorable_event_14.svg'; +import memorable_event_24 from '../../static/icons/memorable_event_24.svg'; +import metro_14 from '../../static/icons/metro_14.svg'; +import metro_24 from '../../static/icons/metro_24.svg'; +import metro_bus_14 from '../../static/icons/metro_bus_14.svg'; +import metro_bus_24 from '../../static/icons/metro_bus_24.svg'; +import metro_cable_14 from '../../static/icons/metro_cable_14.svg'; +import metro_cable_24 from '../../static/icons/metro_cable_24.svg'; +import metro_entrance_24 from '../../static/icons/metro_entrance_24.svg'; +import metro_funicular_14 from '../../static/icons/metro_funicular_14.svg'; +import metro_funicular_24 from '../../static/icons/metro_funicular_24.svg'; +import metro_light_14 from '../../static/icons/metro_light_14.svg'; +import metro_light_24 from '../../static/icons/metro_light_24.svg'; +import metro_monorail_14 from '../../static/icons/metro_monorail_14.svg'; +import metro_monorail_24 from '../../static/icons/metro_monorail_24.svg'; +import metro_tram_14 from '../../static/icons/metro_tram_14.svg'; +import metro_tram_24 from '../../static/icons/metro_tram_24.svg'; +import mobile_phones_14 from '../../static/icons/mobile_phones_14.svg'; +import mobile_phones_24 from '../../static/icons/mobile_phones_24.svg'; +import money_coin_24 from '../../static/icons/money_coin_24.svg'; +import monument_24 from '../../static/icons/monument_24.svg'; +import mosque_14 from '../../static/icons/mosque_14.svg'; +import mosque_24 from '../../static/icons/mosque_24.svg'; +import mountain_24 from '../../static/icons/mountain_24.svg'; +import museum_14 from '../../static/icons/museum_14.svg'; +import museum_24 from '../../static/icons/museum_24.svg'; +import office_14 from '../../static/icons/office_14.svg'; +import office_24 from '../../static/icons/office_24.svg'; +import office_service_14 from '../../static/icons/office_service_14.svg'; +import office_service_24 from '../../static/icons/office_service_24.svg'; +import orthodox_church_14 from '../../static/icons/orthodox_church_14.svg'; +import orthodox_church_24 from '../../static/icons/orthodox_church_24.svg'; +import park_14 from '../../static/icons/park_14.svg'; +import park_24 from '../../static/icons/park_24.svg'; +import pavilion_24 from '../../static/icons/pavilion_24.svg'; +import pet_playground_24 from '../../static/icons/pet_playground_24.svg'; +import petshop_14 from '../../static/icons/petshop_14.svg'; +import petshop_24 from '../../static/icons/petshop_24.svg'; +import photo_14 from '../../static/icons/photo_14.svg'; +import photo_24 from '../../static/icons/photo_24.svg'; +import picnic_24 from '../../static/icons/picnic_24.svg'; +import pier_24 from '../../static/icons/pier_24.svg'; +import playground_24 from '../../static/icons/playground_24.svg'; +import police_14 from '../../static/icons/police_14.svg'; +import police_24 from '../../static/icons/police_24.svg'; +import police_post_14 from '../../static/icons/police_post_14.svg'; +import police_post_24 from '../../static/icons/police_post_24.svg'; +import port_14 from '../../static/icons/port_14.svg'; +import port_24 from '../../static/icons/port_24.svg'; +import post_office_14 from '../../static/icons/post_office_14.svg'; +import post_office_24 from '../../static/icons/post_office_24.svg'; +import printing_services_14 from '../../static/icons/printing_services_14.svg'; +import printing_services_24 from '../../static/icons/printing_services_24.svg'; +import protestant_church_14 from '../../static/icons/protestant_church_14.svg'; +import protestant_church_24 from '../../static/icons/protestant_church_24.svg'; +import racing_14 from '../../static/icons/racing_14.svg'; +import racing_24 from '../../static/icons/racing_24.svg'; +import railway_14 from '../../static/icons/railway_14.svg'; +import railway_24 from '../../static/icons/railway_24.svg'; +import railway_station_14 from '../../static/icons/railway_station_14.svg'; +import railway_station_24 from '../../static/icons/railway_station_24.svg'; +import recycling_24 from '../../static/icons/recycling_24.svg'; +import restaurants_14 from '../../static/icons/restaurants_14.svg'; +import restaurants_24 from '../../static/icons/restaurants_24.svg'; +import rezervation_14 from '../../static/icons/rezervation_14.svg'; +import rezervation_24 from '../../static/icons/rezervation_24.svg'; +import sanatorium_14 from '../../static/icons/sanatorium_14.svg'; +import sanatorium_24 from '../../static/icons/sanatorium_24.svg'; +import science_14 from '../../static/icons/science_14.svg'; +import science_24 from '../../static/icons/science_24.svg'; +import skating_rink_14 from '../../static/icons/skating_rink_14.svg'; +import skating_rink_24 from '../../static/icons/skating_rink_24.svg'; +import software_14 from '../../static/icons/software_14.svg'; +import software_24 from '../../static/icons/software_24.svg'; +import spa_14 from '../../static/icons/spa_14.svg'; +import spa_24 from '../../static/icons/spa_24.svg'; +import sportcenter_14 from '../../static/icons/sportcenter_14.svg'; +import sportcenter_24 from '../../static/icons/sportcenter_24.svg'; +import spring_24 from '../../static/icons/spring_24.svg'; +import stadium_14 from '../../static/icons/stadium_14.svg'; +import stadium_24 from '../../static/icons/stadium_24.svg'; +import supermarket_14 from '../../static/icons/supermarket_14.svg'; +import supermarket_24 from '../../static/icons/supermarket_24.svg'; +import sushi_14 from '../../static/icons/sushi_14.svg'; +import sushi_24 from '../../static/icons/sushi_24.svg'; +import swimming_pool_14 from '../../static/icons/swimming_pool_14.svg'; +import swimming_pool_24 from '../../static/icons/swimming_pool_24.svg'; +import synagogue_14 from '../../static/icons/synagogue_14.svg'; +import synagogue_24 from '../../static/icons/synagogue_24.svg'; +import tailor_14 from '../../static/icons/tailor_14.svg'; +import tailor_24 from '../../static/icons/tailor_24.svg'; +import taxi_14 from '../../static/icons/taxi_14.svg'; +import taxi_24 from '../../static/icons/taxi_24.svg'; +import theatre_14 from '../../static/icons/theatre_14.svg'; +import theatre_24 from '../../static/icons/theatre_24.svg'; +import ticket_office_14 from '../../static/icons/ticket_office_14.svg'; +import ticket_office_24 from '../../static/icons/ticket_office_24.svg'; +import tire_fitting_14 from '../../static/icons/tire_fitting_14.svg'; +import tire_fitting_24 from '../../static/icons/tire_fitting_24.svg'; +import tram_24 from '../../static/icons/tram_24.svg'; +import trash_24 from '../../static/icons/trash_24.svg'; +import travel_agency_14 from '../../static/icons/travel_agency_14.svg'; +import travel_agency_24 from '../../static/icons/travel_agency_24.svg'; +import viewpoint_24 from '../../static/icons/viewpoint_24.svg'; +import waterfall_24 from '../../static/icons/waterfall_24.svg'; +import wc_24 from '../../static/icons/wc_24.svg'; +import zoo_14 from '../../static/icons/zoo_14.svg'; +import zoo_24 from '../../static/icons/zoo_24.svg'; + +import type {Icons} from './types'; +export const icons: Icons = { + airport: {normal: airport_24, small: airport_14}, + attraction: {normal: attraction_24, small: attraction_14}, + auto: {normal: auto_24, small: auto_14}, + aviary: {normal: aviary_24, small: null}, + baby_shop: {normal: baby_shop_24, small: baby_shop_14}, + banks: {normal: banks_24, small: banks_14}, + barbeque: {normal: barbeque_24, small: null}, + bars: {normal: bars_24, small: bars_14}, + beach: {normal: beach_24, small: beach_14}, + bench: {normal: bench_24, small: null}, + bike: {normal: bike_24, small: null}, + bike_rent: {normal: bike_rent_24, small: bike_rent_14}, + boat_station: {normal: boat_station_24, small: boat_station_14}, + bookstore: {normal: bookstore_24, small: bookstore_14}, + buddhism: {normal: buddhism_24, small: buddhism_14}, + building: {normal: building_24, small: null}, + bus: {normal: bus_24, small: null}, + cafe: {normal: cafe_24, small: cafe_14}, + car_park: {normal: car_park_24, small: null}, + catholic_church: {normal: catholic_church_24, small: catholic_church_14}, + cemetery: {normal: cemetery_24, small: cemetery_14}, + childrens_playground: {normal: childrens_playground_24, small: null}, + cinemas: {normal: cinemas_24, small: cinemas_14}, + clothes_shop: {normal: clothes_shop_24, small: clothes_shop_14}, + college: {normal: college_24, small: college_14}, + concert_hall: {normal: concert_hall_24, small: concert_hall_14}, + confectionary: {normal: confectionary_24, small: confectionary_14}, + currency_exchange: {normal: currency_exchange_24, small: currency_exchange_14}, + dental: {normal: dental_24, small: dental_14}, + driving_school: {normal: driving_school_24, small: driving_school_14}, + drugstores: {normal: drugstores_24, small: drugstores_14}, + dry_cleaning: {normal: dry_cleaning_24, small: dry_cleaning_14}, + equestrian: {normal: equestrian_24, small: equestrian_14}, + fast_food: {normal: fast_food_24, small: fast_food_14}, + film_studio: {normal: film_studio_24, small: film_studio_14}, + fire_station: {normal: fire_station_24, small: fire_station_14}, + fitness: {normal: fitness_24, small: fitness_14}, + flower_shop: {normal: flower_shop_24, small: flower_shop_14}, + forest: {normal: forest_24, small: forest_14}, + fountain: {normal: fountain_24, small: null}, + furniture_store: {normal: furniture_store_24, small: furniture_store_14}, + garden: {normal: garden_24, small: garden_14}, + gasstation: {normal: gasstation_24, small: gasstation_14}, + government: {normal: government_24, small: government_14}, + hairdressers: {normal: hairdressers_24, small: hairdressers_14}, + haulier: {normal: haulier_24, small: haulier_14}, + helicopter: {normal: helicopter_24, small: null}, + hospital: {normal: hospital_24, small: hospital_14}, + hotels: {normal: hotels_24, small: hotels_14}, + hypermarket: {normal: hypermarket_24, small: hypermarket_14}, + industrial_enterprise: {normal: industrial_enterprise_24, small: industrial_enterprise_14}, + information: {normal: information_24, small: information_14}, + kindergarten: {normal: kindergarten_24, small: kindergarten_14}, + landmark: {normal: landmark_24, small: landmark_14}, + laundry: {normal: laundry_24, small: laundry_14}, + library: {normal: library_24, small: library_14}, + malls: {normal: malls_24, small: malls_14}, + medicine: {normal: medicine_24, small: medicine_14}, + memorable_event: {normal: memorable_event_24, small: memorable_event_14}, + metro: {normal: metro_24, small: metro_14}, + metro_bus: {normal: metro_bus_24, small: metro_bus_14}, + metro_cable: {normal: metro_cable_24, small: metro_cable_14}, + metro_entrance: {normal: metro_entrance_24, small: null}, + metro_funicular: {normal: metro_funicular_24, small: metro_funicular_14}, + metro_light: {normal: metro_light_24, small: metro_light_14}, + metro_monorail: {normal: metro_monorail_24, small: metro_monorail_14}, + metro_tram: {normal: metro_tram_24, small: metro_tram_14}, + mobile_phones: {normal: mobile_phones_24, small: mobile_phones_14}, + money_coin: {normal: money_coin_24, small: null}, + monument: {normal: monument_24, small: null}, + mosque: {normal: mosque_24, small: mosque_14}, + mountain: {normal: mountain_24, small: null}, + museum: {normal: museum_24, small: museum_14}, + office: {normal: office_24, small: office_14}, + office_service: {normal: office_service_24, small: office_service_14}, + orthodox_church: {normal: orthodox_church_24, small: orthodox_church_14}, + park: {normal: park_24, small: park_14}, + pavilion: {normal: pavilion_24, small: null}, + pet_playground: {normal: pet_playground_24, small: null}, + petshop: {normal: petshop_24, small: petshop_14}, + photo: {normal: photo_24, small: photo_14}, + picnic: {normal: picnic_24, small: null}, + pier: {normal: pier_24, small: null}, + playground: {normal: playground_24, small: null}, + police: {normal: police_24, small: police_14}, + police_post: {normal: police_post_24, small: police_post_14}, + port: {normal: port_24, small: port_14}, + post_office: {normal: post_office_24, small: post_office_14}, + printing_services: {normal: printing_services_24, small: printing_services_14}, + protestant_church: {normal: protestant_church_24, small: protestant_church_14}, + racing: {normal: racing_24, small: racing_14}, + railway: {normal: railway_24, small: railway_14}, + railway_station: {normal: railway_station_24, small: railway_station_14}, + recycling: {normal: recycling_24, small: null}, + restaurants: {normal: restaurants_24, small: restaurants_14}, + rezervation: {normal: rezervation_24, small: rezervation_14}, + sanatorium: {normal: sanatorium_24, small: sanatorium_14}, + science: {normal: science_24, small: science_14}, + skating_rink: {normal: skating_rink_24, small: skating_rink_14}, + software: {normal: software_24, small: software_14}, + spa: {normal: spa_24, small: spa_14}, + sportcenter: {normal: sportcenter_24, small: sportcenter_14}, + spring: {normal: spring_24, small: null}, + stadium: {normal: stadium_24, small: stadium_14}, + supermarket: {normal: supermarket_24, small: supermarket_14}, + sushi: {normal: sushi_24, small: sushi_14}, + swimming_pool: {normal: swimming_pool_24, small: swimming_pool_14}, + synagogue: {normal: synagogue_24, small: synagogue_14}, + tailor: {normal: tailor_24, small: tailor_14}, + taxi: {normal: taxi_24, small: taxi_14}, + theatre: {normal: theatre_24, small: theatre_14}, + ticket_office: {normal: ticket_office_24, small: ticket_office_14}, + tire_fitting: {normal: tire_fitting_24, small: tire_fitting_14}, + tram: {normal: tram_24, small: null}, + trash: {normal: trash_24, small: null}, + travel_agency: {normal: travel_agency_24, small: travel_agency_14}, + viewpoint: {normal: viewpoint_24, small: null}, + waterfall: {normal: waterfall_24, small: null}, + wc: {normal: wc_24, small: null}, + zoo: {normal: zoo_24, small: zoo_14} +}; From bcc0b34bd986da3580cc2cecb9ab007fa7810d55 Mon Sep 17 00:00:00 2001 From: Matthew Kalinin Date: Wed, 3 Apr 2024 16:21:11 +0300 Subject: [PATCH 18/48] color props --- src/MMapDefaultMarker/index.css | 5 ++--- src/MMapDefaultMarker/index.ts | 25 ++++++++++++++++--------- src/MMapDefaultMarker/pin.svg | 2 +- 3 files changed, 19 insertions(+), 13 deletions(-) diff --git a/src/MMapDefaultMarker/index.css b/src/MMapDefaultMarker/index.css index 510c02e..915ada3 100644 --- a/src/MMapDefaultMarker/index.css +++ b/src/MMapDefaultMarker/index.css @@ -2,7 +2,7 @@ box-sizing: border-box; height: 8px; width: 8px; - border: 2px solid #f8f8f8; + border: 2px solid; position: absolute; transform: translate(-50%, -50%); border-radius: 50%; @@ -14,7 +14,6 @@ } .mappable--icon { - color: #fffffffc; position: absolute; - transform: translate(calc(-24px - 10px),10px); + transform: translate(calc(-24px - 10px), 10px); } diff --git a/src/MMapDefaultMarker/index.ts b/src/MMapDefaultMarker/index.ts index b20bae8..c4f0e80 100644 --- a/src/MMapDefaultMarker/index.ts +++ b/src/MMapDefaultMarker/index.ts @@ -1,12 +1,11 @@ import {MMapMarker, MMapMarkerProps} from '@mappable-world/mappable-types'; -import {IconName} from '../icons/icon-name'; -import {icons} from '../icons'; -import pin from './pin.svg'; +import {IconColor, IconName, iconColors, icons, glyphColors} from '../icons'; import './index.css'; +import pin from './pin.svg'; export type MMapDefaultMarkerProps = MMapMarkerProps & { name: IconName; - color: string; + color: IconColor; }; export class MMapDefaultMarker extends mappable.MMapComplexEntity { @@ -14,19 +13,19 @@ export class MMapDefaultMarker extends mappable.MMapComplexEntity): void { if (propsDiff.color !== undefined) { - this._markerElement.style.color = this._props.color; - this._markerElement.style.backgroundColor = this._props.color; + this._color = iconColors[this._props.color]; + this._updateTheme(); } if (propsDiff.name !== undefined) { this._icon.innerHTML = icons[propsDiff.name].normal; } this._marker.update(this._props); } + + private _updateTheme() { + this._markerElement.style.color = this._color.day; + this._markerElement.style.backgroundColor = this._color.day; + this._markerElement.style.borderColor = '#f8f8f8'; + this._icon.style.color = glyphColors.day; + } } diff --git a/src/MMapDefaultMarker/pin.svg b/src/MMapDefaultMarker/pin.svg index f0dcd75..5d9a1b1 100644 --- a/src/MMapDefaultMarker/pin.svg +++ b/src/MMapDefaultMarker/pin.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file From 445482412c096a2b79d2af1ee972201ab2722d8c Mon Sep 17 00:00:00 2001 From: Matthew Kalinin Date: Thu, 4 Apr 2024 12:50:50 +0300 Subject: [PATCH 19/48] optional props --- src/MMapDefaultMarker/index.ts | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/src/MMapDefaultMarker/index.ts b/src/MMapDefaultMarker/index.ts index c4f0e80..713f0fd 100644 --- a/src/MMapDefaultMarker/index.ts +++ b/src/MMapDefaultMarker/index.ts @@ -4,11 +4,16 @@ import './index.css'; import pin from './pin.svg'; export type MMapDefaultMarkerProps = MMapMarkerProps & { - name: IconName; - color: IconColor; + iconName?: IconName; + color?: IconColor; }; -export class MMapDefaultMarker extends mappable.MMapComplexEntity { +const defaultProps = Object.freeze({color: 'darkgray'}); +type DefaultProps = typeof defaultProps; + +export class MMapDefaultMarker extends mappable.MMapComplexEntity { + static defaultProps = defaultProps; + private _marker: MMapMarker; private _markerElement: HTMLElement; private _pin: HTMLElement; @@ -19,6 +24,10 @@ export class MMapDefaultMarker extends mappable.MMapComplexEntity Date: Thu, 4 Apr 2024 15:51:44 +0300 Subject: [PATCH 20/48] custom color --- src/MMapDefaultMarker/index.ts | 36 ++++++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/src/MMapDefaultMarker/index.ts b/src/MMapDefaultMarker/index.ts index 713f0fd..9a4273c 100644 --- a/src/MMapDefaultMarker/index.ts +++ b/src/MMapDefaultMarker/index.ts @@ -3,9 +3,12 @@ import {IconColor, IconName, iconColors, icons, glyphColors} from '../icons'; import './index.css'; import pin from './pin.svg'; +export type ThemesColor = {day: string; night: string}; +export type MarkerColorProps = IconColor | ThemesColor; + export type MMapDefaultMarkerProps = MMapMarkerProps & { iconName?: IconName; - color?: IconColor; + color?: MarkerColorProps; }; const defaultProps = Object.freeze({color: 'darkgray'}); @@ -18,7 +21,7 @@ export class MMapDefaultMarker extends mappable.MMapComplexEntity): void { if (propsDiff.color !== undefined) { - this._color = iconColors[this._props.color]; + this._color = this._getColor(); this._updateTheme(); } - this._icon.innerHTML = this._props.iconName !== undefined ? icons[this._props.iconName].normal : ''; + this._icon.innerHTML = this._getIcon(); this._marker.update(this._props); } @@ -69,4 +70,23 @@ export class MMapDefaultMarker extends mappable.MMapComplexEntity Date: Thu, 4 Apr 2024 15:59:20 +0300 Subject: [PATCH 21/48] add fallback icon --- docs/icons.md | 1 + src/icons/icon-name.ts | 1 + src/icons/icons.ts | 3 +++ 3 files changed, 5 insertions(+) diff --git a/docs/icons.md b/docs/icons.md index 28f5b74..5bfafd7 100644 --- a/docs/icons.md +++ b/docs/icons.md @@ -37,6 +37,7 @@ | drugstores | ![drugstores](../static/icons/drugstores_24.svg) | ![drugstores](../static/icons/drugstores_14.svg) | | dry_cleaning | ![dry_cleaning](../static/icons/dry_cleaning_24.svg) | ![dry_cleaning](../static/icons/dry_cleaning_14.svg) | | equestrian | ![equestrian](../static/icons/equestrian_24.svg) | ![equestrian](../static/icons/equestrian_14.svg) | +| fallback | ![fallback](../static/icons/fallback_24.svg) | ![fallback](../static/icons/fallback_14.svg) | | fast_food | ![fast_food](../static/icons/fast_food_24.svg) | ![fast_food](../static/icons/fast_food_14.svg) | | film_studio | ![film_studio](../static/icons/film_studio_24.svg) | ![film_studio](../static/icons/film_studio_14.svg) | | fire_station | ![fire_station](../static/icons/fire_station_24.svg) | ![fire_station](../static/icons/fire_station_14.svg) | diff --git a/src/icons/icon-name.ts b/src/icons/icon-name.ts index 2e1e72a..a69b724 100644 --- a/src/icons/icon-name.ts +++ b/src/icons/icon-name.ts @@ -33,6 +33,7 @@ export type IconName = | 'drugstores' | 'dry_cleaning' | 'equestrian' + | 'fallback' | 'fast_food' | 'film_studio' | 'fire_station' diff --git a/src/icons/icons.ts b/src/icons/icons.ts index 1de3256..510abe2 100644 --- a/src/icons/icons.ts +++ b/src/icons/icons.ts @@ -57,6 +57,8 @@ import dry_cleaning_14 from '../../static/icons/dry_cleaning_14.svg'; import dry_cleaning_24 from '../../static/icons/dry_cleaning_24.svg'; import equestrian_14 from '../../static/icons/equestrian_14.svg'; import equestrian_24 from '../../static/icons/equestrian_24.svg'; +import fallback_14 from '../../static/icons/fallback_14.svg'; +import fallback_24 from '../../static/icons/fallback_24.svg'; import fast_food_14 from '../../static/icons/fast_food_14.svg'; import fast_food_24 from '../../static/icons/fast_food_24.svg'; import film_studio_14 from '../../static/icons/film_studio_14.svg'; @@ -249,6 +251,7 @@ export const icons: Icons = { drugstores: {normal: drugstores_24, small: drugstores_14}, dry_cleaning: {normal: dry_cleaning_24, small: dry_cleaning_14}, equestrian: {normal: equestrian_24, small: equestrian_14}, + fallback: {normal: fallback_24, small: fallback_14}, fast_food: {normal: fast_food_24, small: fast_food_14}, film_studio: {normal: film_studio_24, small: film_studio_14}, fire_station: {normal: fire_station_24, small: fire_station_14}, From d7aadc4e0af1ec38a75a1b02c268eadf4f979cfc Mon Sep 17 00:00:00 2001 From: Matthew Kalinin Date: Mon, 8 Apr 2024 15:37:47 +0300 Subject: [PATCH 22/48] size props in marker --- .../backgrounds/micro-poi-stroke.svg | 1 + .../backgrounds/micro-poi.svg | 1 + .../{pin.svg => backgrounds/normal-pin.svg} | 2 +- .../backgrounds/small-poi-stroke.svg | 1 + .../backgrounds/small-poi.svg | 1 + src/MMapDefaultMarker/index.css | 63 ++++++-- src/MMapDefaultMarker/index.ts | 135 +++++++++++++++--- 7 files changed, 170 insertions(+), 34 deletions(-) create mode 100644 src/MMapDefaultMarker/backgrounds/micro-poi-stroke.svg create mode 100644 src/MMapDefaultMarker/backgrounds/micro-poi.svg rename src/MMapDefaultMarker/{pin.svg => backgrounds/normal-pin.svg} (89%) create mode 100644 src/MMapDefaultMarker/backgrounds/small-poi-stroke.svg create mode 100644 src/MMapDefaultMarker/backgrounds/small-poi.svg diff --git a/src/MMapDefaultMarker/backgrounds/micro-poi-stroke.svg b/src/MMapDefaultMarker/backgrounds/micro-poi-stroke.svg new file mode 100644 index 0000000..4d62815 --- /dev/null +++ b/src/MMapDefaultMarker/backgrounds/micro-poi-stroke.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/MMapDefaultMarker/backgrounds/micro-poi.svg b/src/MMapDefaultMarker/backgrounds/micro-poi.svg new file mode 100644 index 0000000..da465bb --- /dev/null +++ b/src/MMapDefaultMarker/backgrounds/micro-poi.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/MMapDefaultMarker/pin.svg b/src/MMapDefaultMarker/backgrounds/normal-pin.svg similarity index 89% rename from src/MMapDefaultMarker/pin.svg rename to src/MMapDefaultMarker/backgrounds/normal-pin.svg index 5d9a1b1..4fe7f68 100644 --- a/src/MMapDefaultMarker/pin.svg +++ b/src/MMapDefaultMarker/backgrounds/normal-pin.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/MMapDefaultMarker/backgrounds/small-poi-stroke.svg b/src/MMapDefaultMarker/backgrounds/small-poi-stroke.svg new file mode 100644 index 0000000..4a6bb40 --- /dev/null +++ b/src/MMapDefaultMarker/backgrounds/small-poi-stroke.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/MMapDefaultMarker/backgrounds/small-poi.svg b/src/MMapDefaultMarker/backgrounds/small-poi.svg new file mode 100644 index 0000000..3a1c7d7 --- /dev/null +++ b/src/MMapDefaultMarker/backgrounds/small-poi.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/MMapDefaultMarker/index.css b/src/MMapDefaultMarker/index.css index 915ada3..d26740a 100644 --- a/src/MMapDefaultMarker/index.css +++ b/src/MMapDefaultMarker/index.css @@ -1,19 +1,62 @@ -.mappable--point { - box-sizing: border-box; - height: 8px; - width: 8px; - border: 2px solid; +.mappable--default-marker-point { position: absolute; - transform: translate(-50%, -50%); + cursor: pointer; +} +.mappable--default-marker-point svg { + display: block; +} + +/* normal size */ +.mappable--normal-pin { + display: flex; + flex-direction: column; + align-items: center; + row-gap: 2px; + position: absolute; + transform: translate(-50%, calc(-100% + 4px)); + filter: drop-shadow(0px 2px 6px rgba(24, 27, 34, 0.4)); +} +.mappable--normal-icon { + position: absolute; + transform: translate(-50%, calc(-4px - 2px - 51px + 10px)); /* 51px - pin height, 10px - offset */ +} +.mappable--normal-pin_circle { + display: block; + width: 4px; + height: 4px; + border-width: 2px; + border-style: solid; border-radius: 50%; + box-shadow: 0px 1px 4px 0px rgba(24, 27, 34, 0.3); } -.mappable--pin { +/* small size */ +.mappable--small-poi { position: absolute; - transform: translate(calc(-50% + 2px), calc(-100%)); + transform: translate(-50%, -50%); +} +.mappable--small-poi_stroke { + position: absolute; + transform: translate(-50%, -50%); + z-index: -1; + -webkit-filter: drop-shadow(0px 4px 4px rgba(0, 0, 0, 0.1)); + filter: drop-shadow(0px 4px 4px rgba(0, 0, 0, 0.1)); +} +.mappable--small-icon { + position: absolute; + z-index: 1; + transform: translate(-50%, -50%); } -.mappable--icon { +/* micro size */ +.mappable--micro-poi { position: absolute; - transform: translate(calc(-24px - 10px), 10px); + transform: translate(-50%, -50%); +} +.mappable--micro-poi_stroke { + position: absolute; + transform: translate(-50%, -50%); + z-index: -1; + -webkit-filter: drop-shadow(0px 4px 4px rgba(0, 0, 0, 0.1)); + filter: drop-shadow(0px 4px 4px rgba(0, 0, 0, 0.1)); } diff --git a/src/MMapDefaultMarker/index.ts b/src/MMapDefaultMarker/index.ts index 9a4273c..8942be9 100644 --- a/src/MMapDefaultMarker/index.ts +++ b/src/MMapDefaultMarker/index.ts @@ -1,27 +1,36 @@ import {MMapMarker, MMapMarkerProps} from '@mappable-world/mappable-types'; -import {IconColor, IconName, iconColors, icons, glyphColors} from '../icons'; +import {IconColor, IconName, IconSize, glyphColors, iconColors, icons} from '../icons'; +import microPoiStrokeSVG from './backgrounds/micro-poi-stroke.svg'; +import microPoiSVG from './backgrounds/micro-poi.svg'; +import normalPinSVG from './backgrounds/normal-pin.svg'; +import smallPoiStrokeSVG from './backgrounds/small-poi-stroke.svg'; +import smallPoiSVG from './backgrounds/small-poi.svg'; import './index.css'; -import pin from './pin.svg'; export type ThemesColor = {day: string; night: string}; export type MarkerColorProps = IconColor | ThemesColor; +export type MarkerSizeProps = IconSize | 'micro'; export type MMapDefaultMarkerProps = MMapMarkerProps & { iconName?: IconName; color?: MarkerColorProps; + size?: MarkerSizeProps; }; -const defaultProps = Object.freeze({color: 'darkgray'}); +const defaultProps = Object.freeze({color: 'darkgray', size: 'small'}); type DefaultProps = typeof defaultProps; +type BackgroundAndIcon = {background: HTMLElement; stroke?: HTMLElement; icon?: HTMLElement}; + export class MMapDefaultMarker extends mappable.MMapComplexEntity { static defaultProps = defaultProps; private _marker: MMapMarker; private _markerElement: HTMLElement; - private _pin: HTMLElement; - private _icon: HTMLElement; private _color: ThemesColor; + private _background: HTMLElement; + private _stroke?: HTMLElement; + private _icon?: HTMLElement; constructor(props: MMapDefaultMarkerProps) { super(props); @@ -33,20 +42,38 @@ export class MMapDefaultMarker extends mappable.MMapComplexEntity('.mappable--normal-pin_circle'); + this._background.style.color = this._color.day; + circle.style.backgroundColor = this._color.day; + this._icon.style.color = glyphColors.day; + circle.style.borderColor = glyphColors.day; + break; + case 'small': + this._background.style.color = this._color.day; + this._stroke.style.color = glyphColors.day; + this._icon.style.color = glyphColors.day; + break; + case 'micro': + this._background.style.color = this._color.day; + this._stroke.style.color = glyphColors.day; + break; + } } private _getIcon(): string { - return this._props.iconName !== undefined ? icons[this._props.iconName].normal : ''; + if (this._props.size === 'micro') { + return ''; + } + return this._props.iconName !== undefined ? icons[this._props.iconName][this._props.size] : ''; } private _getColor(): ThemesColor { @@ -89,4 +131,51 @@ export class MMapDefaultMarker extends mappable.MMapComplexEntity Date: Mon, 8 Apr 2024 16:00:55 +0300 Subject: [PATCH 23/48] Downscale if no small icon --- src/MMapDefaultMarker/index.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/MMapDefaultMarker/index.ts b/src/MMapDefaultMarker/index.ts index 8942be9..b267052 100644 --- a/src/MMapDefaultMarker/index.ts +++ b/src/MMapDefaultMarker/index.ts @@ -111,10 +111,13 @@ export class MMapDefaultMarker extends mappable.MMapComplexEntity Date: Wed, 10 Apr 2024 19:37:14 +0300 Subject: [PATCH 24/48] upgrade markers --- docs/icons.md | 246 ++++++------- .../backgrounds/micro-poi-stroke.svg | 1 - .../backgrounds/micro-poi.svg | 1 - .../backgrounds/normal-pin.svg | 1 - .../backgrounds/small-poi-stroke.svg | 1 - .../backgrounds/small-poi.svg | 1 - src/MMapDefaultMarker/index.css | 62 ---- src/MMapDefaultMarker/index.ts | 184 ---------- src/icons/icon-colors.ts | 1 - src/icons/icon-size.ts | 1 - src/icons/icons.ts | 337 +++++++----------- 11 files changed, 244 insertions(+), 592 deletions(-) delete mode 100644 src/MMapDefaultMarker/backgrounds/micro-poi-stroke.svg delete mode 100644 src/MMapDefaultMarker/backgrounds/micro-poi.svg delete mode 100644 src/MMapDefaultMarker/backgrounds/normal-pin.svg delete mode 100644 src/MMapDefaultMarker/backgrounds/small-poi-stroke.svg delete mode 100644 src/MMapDefaultMarker/backgrounds/small-poi.svg delete mode 100644 src/MMapDefaultMarker/index.css delete mode 100644 src/MMapDefaultMarker/index.ts delete mode 100644 src/icons/icon-size.ts diff --git a/docs/icons.md b/docs/icons.md index 5bfafd7..fb76816 100644 --- a/docs/icons.md +++ b/docs/icons.md @@ -2,126 +2,126 @@ # List of supported icons -| Name | Normal Size | Small Size | -| --------------------- | ---------------------------------------------------------------------- | ---------------------------------------------------------------------- | -| airport | ![airport](../static/icons/airport_24.svg) | ![airport](../static/icons/airport_14.svg) | -| attraction | ![attraction](../static/icons/attraction_24.svg) | ![attraction](../static/icons/attraction_14.svg) | -| auto | ![auto](../static/icons/auto_24.svg) | ![auto](../static/icons/auto_14.svg) | -| aviary | ![aviary](../static/icons/aviary_24.svg) | none | -| baby_shop | ![baby_shop](../static/icons/baby_shop_24.svg) | ![baby_shop](../static/icons/baby_shop_14.svg) | -| banks | ![banks](../static/icons/banks_24.svg) | ![banks](../static/icons/banks_14.svg) | -| barbeque | ![barbeque](../static/icons/barbeque_24.svg) | none | -| bars | ![bars](../static/icons/bars_24.svg) | ![bars](../static/icons/bars_14.svg) | -| beach | ![beach](../static/icons/beach_24.svg) | ![beach](../static/icons/beach_14.svg) | -| bench | ![bench](../static/icons/bench_24.svg) | none | -| bike | ![bike](../static/icons/bike_24.svg) | none | -| bike_rent | ![bike_rent](../static/icons/bike_rent_24.svg) | ![bike_rent](../static/icons/bike_rent_14.svg) | -| boat_station | ![boat_station](../static/icons/boat_station_24.svg) | ![boat_station](../static/icons/boat_station_14.svg) | -| bookstore | ![bookstore](../static/icons/bookstore_24.svg) | ![bookstore](../static/icons/bookstore_14.svg) | -| buddhism | ![buddhism](../static/icons/buddhism_24.svg) | ![buddhism](../static/icons/buddhism_14.svg) | -| building | ![building](../static/icons/building_24.svg) | none | -| bus | ![bus](../static/icons/bus_24.svg) | none | -| cafe | ![cafe](../static/icons/cafe_24.svg) | ![cafe](../static/icons/cafe_14.svg) | -| car_park | ![car_park](../static/icons/car_park_24.svg) | none | -| catholic_church | ![catholic_church](../static/icons/catholic_church_24.svg) | ![catholic_church](../static/icons/catholic_church_14.svg) | -| cemetery | ![cemetery](../static/icons/cemetery_24.svg) | ![cemetery](../static/icons/cemetery_14.svg) | -| childrens_playground | ![childrens_playground](../static/icons/childrens_playground_24.svg) | none | -| cinemas | ![cinemas](../static/icons/cinemas_24.svg) | ![cinemas](../static/icons/cinemas_14.svg) | -| clothes_shop | ![clothes_shop](../static/icons/clothes_shop_24.svg) | ![clothes_shop](../static/icons/clothes_shop_14.svg) | -| college | ![college](../static/icons/college_24.svg) | ![college](../static/icons/college_14.svg) | -| concert_hall | ![concert_hall](../static/icons/concert_hall_24.svg) | ![concert_hall](../static/icons/concert_hall_14.svg) | -| confectionary | ![confectionary](../static/icons/confectionary_24.svg) | ![confectionary](../static/icons/confectionary_14.svg) | -| currency_exchange | ![currency_exchange](../static/icons/currency_exchange_24.svg) | ![currency_exchange](../static/icons/currency_exchange_14.svg) | -| dental | ![dental](../static/icons/dental_24.svg) | ![dental](../static/icons/dental_14.svg) | -| driving_school | ![driving_school](../static/icons/driving_school_24.svg) | ![driving_school](../static/icons/driving_school_14.svg) | -| drugstores | ![drugstores](../static/icons/drugstores_24.svg) | ![drugstores](../static/icons/drugstores_14.svg) | -| dry_cleaning | ![dry_cleaning](../static/icons/dry_cleaning_24.svg) | ![dry_cleaning](../static/icons/dry_cleaning_14.svg) | -| equestrian | ![equestrian](../static/icons/equestrian_24.svg) | ![equestrian](../static/icons/equestrian_14.svg) | -| fallback | ![fallback](../static/icons/fallback_24.svg) | ![fallback](../static/icons/fallback_14.svg) | -| fast_food | ![fast_food](../static/icons/fast_food_24.svg) | ![fast_food](../static/icons/fast_food_14.svg) | -| film_studio | ![film_studio](../static/icons/film_studio_24.svg) | ![film_studio](../static/icons/film_studio_14.svg) | -| fire_station | ![fire_station](../static/icons/fire_station_24.svg) | ![fire_station](../static/icons/fire_station_14.svg) | -| fitness | ![fitness](../static/icons/fitness_24.svg) | ![fitness](../static/icons/fitness_14.svg) | -| flower_shop | ![flower_shop](../static/icons/flower_shop_24.svg) | ![flower_shop](../static/icons/flower_shop_14.svg) | -| forest | ![forest](../static/icons/forest_24.svg) | ![forest](../static/icons/forest_14.svg) | -| fountain | ![fountain](../static/icons/fountain_24.svg) | none | -| furniture_store | ![furniture_store](../static/icons/furniture_store_24.svg) | ![furniture_store](../static/icons/furniture_store_14.svg) | -| garden | ![garden](../static/icons/garden_24.svg) | ![garden](../static/icons/garden_14.svg) | -| gasstation | ![gasstation](../static/icons/gasstation_24.svg) | ![gasstation](../static/icons/gasstation_14.svg) | -| government | ![government](../static/icons/government_24.svg) | ![government](../static/icons/government_14.svg) | -| hairdressers | ![hairdressers](../static/icons/hairdressers_24.svg) | ![hairdressers](../static/icons/hairdressers_14.svg) | -| haulier | ![haulier](../static/icons/haulier_24.svg) | ![haulier](../static/icons/haulier_14.svg) | -| helicopter | ![helicopter](../static/icons/helicopter_24.svg) | none | -| hospital | ![hospital](../static/icons/hospital_24.svg) | ![hospital](../static/icons/hospital_14.svg) | -| hotels | ![hotels](../static/icons/hotels_24.svg) | ![hotels](../static/icons/hotels_14.svg) | -| hypermarket | ![hypermarket](../static/icons/hypermarket_24.svg) | ![hypermarket](../static/icons/hypermarket_14.svg) | -| industrial_enterprise | ![industrial_enterprise](../static/icons/industrial_enterprise_24.svg) | ![industrial_enterprise](../static/icons/industrial_enterprise_14.svg) | -| information | ![information](../static/icons/information_24.svg) | ![information](../static/icons/information_14.svg) | -| kindergarten | ![kindergarten](../static/icons/kindergarten_24.svg) | ![kindergarten](../static/icons/kindergarten_14.svg) | -| landmark | ![landmark](../static/icons/landmark_24.svg) | ![landmark](../static/icons/landmark_14.svg) | -| laundry | ![laundry](../static/icons/laundry_24.svg) | ![laundry](../static/icons/laundry_14.svg) | -| library | ![library](../static/icons/library_24.svg) | ![library](../static/icons/library_14.svg) | -| malls | ![malls](../static/icons/malls_24.svg) | ![malls](../static/icons/malls_14.svg) | -| medicine | ![medicine](../static/icons/medicine_24.svg) | ![medicine](../static/icons/medicine_14.svg) | -| memorable_event | ![memorable_event](../static/icons/memorable_event_24.svg) | ![memorable_event](../static/icons/memorable_event_14.svg) | -| metro | ![metro](../static/icons/metro_24.svg) | ![metro](../static/icons/metro_14.svg) | -| metro_bus | ![metro_bus](../static/icons/metro_bus_24.svg) | ![metro_bus](../static/icons/metro_bus_14.svg) | -| metro_cable | ![metro_cable](../static/icons/metro_cable_24.svg) | ![metro_cable](../static/icons/metro_cable_14.svg) | -| metro_entrance | ![metro_entrance](../static/icons/metro_entrance_24.svg) | none | -| metro_funicular | ![metro_funicular](../static/icons/metro_funicular_24.svg) | ![metro_funicular](../static/icons/metro_funicular_14.svg) | -| metro_light | ![metro_light](../static/icons/metro_light_24.svg) | ![metro_light](../static/icons/metro_light_14.svg) | -| metro_monorail | ![metro_monorail](../static/icons/metro_monorail_24.svg) | ![metro_monorail](../static/icons/metro_monorail_14.svg) | -| metro_tram | ![metro_tram](../static/icons/metro_tram_24.svg) | ![metro_tram](../static/icons/metro_tram_14.svg) | -| mobile_phones | ![mobile_phones](../static/icons/mobile_phones_24.svg) | ![mobile_phones](../static/icons/mobile_phones_14.svg) | -| money_coin | ![money_coin](../static/icons/money_coin_24.svg) | none | -| monument | ![monument](../static/icons/monument_24.svg) | none | -| mosque | ![mosque](../static/icons/mosque_24.svg) | ![mosque](../static/icons/mosque_14.svg) | -| mountain | ![mountain](../static/icons/mountain_24.svg) | none | -| museum | ![museum](../static/icons/museum_24.svg) | ![museum](../static/icons/museum_14.svg) | -| office | ![office](../static/icons/office_24.svg) | ![office](../static/icons/office_14.svg) | -| office_service | ![office_service](../static/icons/office_service_24.svg) | ![office_service](../static/icons/office_service_14.svg) | -| orthodox_church | ![orthodox_church](../static/icons/orthodox_church_24.svg) | ![orthodox_church](../static/icons/orthodox_church_14.svg) | -| park | ![park](../static/icons/park_24.svg) | ![park](../static/icons/park_14.svg) | -| pavilion | ![pavilion](../static/icons/pavilion_24.svg) | none | -| pet_playground | ![pet_playground](../static/icons/pet_playground_24.svg) | none | -| petshop | ![petshop](../static/icons/petshop_24.svg) | ![petshop](../static/icons/petshop_14.svg) | -| photo | ![photo](../static/icons/photo_24.svg) | ![photo](../static/icons/photo_14.svg) | -| picnic | ![picnic](../static/icons/picnic_24.svg) | none | -| pier | ![pier](../static/icons/pier_24.svg) | none | -| playground | ![playground](../static/icons/playground_24.svg) | none | -| police | ![police](../static/icons/police_24.svg) | ![police](../static/icons/police_14.svg) | -| police_post | ![police_post](../static/icons/police_post_24.svg) | ![police_post](../static/icons/police_post_14.svg) | -| port | ![port](../static/icons/port_24.svg) | ![port](../static/icons/port_14.svg) | -| post_office | ![post_office](../static/icons/post_office_24.svg) | ![post_office](../static/icons/post_office_14.svg) | -| printing_services | ![printing_services](../static/icons/printing_services_24.svg) | ![printing_services](../static/icons/printing_services_14.svg) | -| protestant_church | ![protestant_church](../static/icons/protestant_church_24.svg) | ![protestant_church](../static/icons/protestant_church_14.svg) | -| racing | ![racing](../static/icons/racing_24.svg) | ![racing](../static/icons/racing_14.svg) | -| railway | ![railway](../static/icons/railway_24.svg) | ![railway](../static/icons/railway_14.svg) | -| railway_station | ![railway_station](../static/icons/railway_station_24.svg) | ![railway_station](../static/icons/railway_station_14.svg) | -| recycling | ![recycling](../static/icons/recycling_24.svg) | none | -| restaurants | ![restaurants](../static/icons/restaurants_24.svg) | ![restaurants](../static/icons/restaurants_14.svg) | -| rezervation | ![rezervation](../static/icons/rezervation_24.svg) | ![rezervation](../static/icons/rezervation_14.svg) | -| sanatorium | ![sanatorium](../static/icons/sanatorium_24.svg) | ![sanatorium](../static/icons/sanatorium_14.svg) | -| science | ![science](../static/icons/science_24.svg) | ![science](../static/icons/science_14.svg) | -| skating_rink | ![skating_rink](../static/icons/skating_rink_24.svg) | ![skating_rink](../static/icons/skating_rink_14.svg) | -| software | ![software](../static/icons/software_24.svg) | ![software](../static/icons/software_14.svg) | -| spa | ![spa](../static/icons/spa_24.svg) | ![spa](../static/icons/spa_14.svg) | -| sportcenter | ![sportcenter](../static/icons/sportcenter_24.svg) | ![sportcenter](../static/icons/sportcenter_14.svg) | -| spring | ![spring](../static/icons/spring_24.svg) | none | -| stadium | ![stadium](../static/icons/stadium_24.svg) | ![stadium](../static/icons/stadium_14.svg) | -| supermarket | ![supermarket](../static/icons/supermarket_24.svg) | ![supermarket](../static/icons/supermarket_14.svg) | -| sushi | ![sushi](../static/icons/sushi_24.svg) | ![sushi](../static/icons/sushi_14.svg) | -| swimming_pool | ![swimming_pool](../static/icons/swimming_pool_24.svg) | ![swimming_pool](../static/icons/swimming_pool_14.svg) | -| synagogue | ![synagogue](../static/icons/synagogue_24.svg) | ![synagogue](../static/icons/synagogue_14.svg) | -| tailor | ![tailor](../static/icons/tailor_24.svg) | ![tailor](../static/icons/tailor_14.svg) | -| taxi | ![taxi](../static/icons/taxi_24.svg) | ![taxi](../static/icons/taxi_14.svg) | -| theatre | ![theatre](../static/icons/theatre_24.svg) | ![theatre](../static/icons/theatre_14.svg) | -| ticket_office | ![ticket_office](../static/icons/ticket_office_24.svg) | ![ticket_office](../static/icons/ticket_office_14.svg) | -| tire_fitting | ![tire_fitting](../static/icons/tire_fitting_24.svg) | ![tire_fitting](../static/icons/tire_fitting_14.svg) | -| tram | ![tram](../static/icons/tram_24.svg) | none | -| trash | ![trash](../static/icons/trash_24.svg) | none | -| travel_agency | ![travel_agency](../static/icons/travel_agency_24.svg) | ![travel_agency](../static/icons/travel_agency_14.svg) | -| viewpoint | ![viewpoint](../static/icons/viewpoint_24.svg) | none | -| waterfall | ![waterfall](../static/icons/waterfall_24.svg) | none | -| wc | ![wc](../static/icons/wc_24.svg) | none | -| zoo | ![zoo](../static/icons/zoo_24.svg) | ![zoo](../static/icons/zoo_14.svg) | +| Name | Normal Size | +| --------------------- | ---------------------------------------------------------------------- | +| airport | ![airport](../static/icons/airport_24.svg) | +| attraction | ![attraction](../static/icons/attraction_24.svg) | +| auto | ![auto](../static/icons/auto_24.svg) | +| aviary | ![aviary](../static/icons/aviary_24.svg) | +| baby_shop | ![baby_shop](../static/icons/baby_shop_24.svg) | +| banks | ![banks](../static/icons/banks_24.svg) | +| barbeque | ![barbeque](../static/icons/barbeque_24.svg) | +| bars | ![bars](../static/icons/bars_24.svg) | +| beach | ![beach](../static/icons/beach_24.svg) | +| bench | ![bench](../static/icons/bench_24.svg) | +| bike | ![bike](../static/icons/bike_24.svg) | +| bike_rent | ![bike_rent](../static/icons/bike_rent_24.svg) | +| boat_station | ![boat_station](../static/icons/boat_station_24.svg) | +| bookstore | ![bookstore](../static/icons/bookstore_24.svg) | +| buddhism | ![buddhism](../static/icons/buddhism_24.svg) | +| building | ![building](../static/icons/building_24.svg) | +| bus | ![bus](../static/icons/bus_24.svg) | +| cafe | ![cafe](../static/icons/cafe_24.svg) | +| car_park | ![car_park](../static/icons/car_park_24.svg) | +| catholic_church | ![catholic_church](../static/icons/catholic_church_24.svg) | +| cemetery | ![cemetery](../static/icons/cemetery_24.svg) | +| childrens_playground | ![childrens_playground](../static/icons/childrens_playground_24.svg) | +| cinemas | ![cinemas](../static/icons/cinemas_24.svg) | +| clothes_shop | ![clothes_shop](../static/icons/clothes_shop_24.svg) | +| college | ![college](../static/icons/college_24.svg) | +| concert_hall | ![concert_hall](../static/icons/concert_hall_24.svg) | +| confectionary | ![confectionary](../static/icons/confectionary_24.svg) | +| currency_exchange | ![currency_exchange](../static/icons/currency_exchange_24.svg) | +| dental | ![dental](../static/icons/dental_24.svg) | +| driving_school | ![driving_school](../static/icons/driving_school_24.svg) | +| drugstores | ![drugstores](../static/icons/drugstores_24.svg) | +| dry_cleaning | ![dry_cleaning](../static/icons/dry_cleaning_24.svg) | +| equestrian | ![equestrian](../static/icons/equestrian_24.svg) | +| fallback | ![fallback](../static/icons/fallback_24.svg) | +| fast_food | ![fast_food](../static/icons/fast_food_24.svg) | +| film_studio | ![film_studio](../static/icons/film_studio_24.svg) | +| fire_station | ![fire_station](../static/icons/fire_station_24.svg) | +| fitness | ![fitness](../static/icons/fitness_24.svg) | +| flower_shop | ![flower_shop](../static/icons/flower_shop_24.svg) | +| forest | ![forest](../static/icons/forest_24.svg) | +| fountain | ![fountain](../static/icons/fountain_24.svg) | +| furniture_store | ![furniture_store](../static/icons/furniture_store_24.svg) | +| garden | ![garden](../static/icons/garden_24.svg) | +| gasstation | ![gasstation](../static/icons/gasstation_24.svg) | +| government | ![government](../static/icons/government_24.svg) | +| hairdressers | ![hairdressers](../static/icons/hairdressers_24.svg) | +| haulier | ![haulier](../static/icons/haulier_24.svg) | +| helicopter | ![helicopter](../static/icons/helicopter_24.svg) | +| hospital | ![hospital](../static/icons/hospital_24.svg) | +| hotels | ![hotels](../static/icons/hotels_24.svg) | +| hypermarket | ![hypermarket](../static/icons/hypermarket_24.svg) | +| industrial_enterprise | ![industrial_enterprise](../static/icons/industrial_enterprise_24.svg) | +| information | ![information](../static/icons/information_24.svg) | +| kindergarten | ![kindergarten](../static/icons/kindergarten_24.svg) | +| landmark | ![landmark](../static/icons/landmark_24.svg) | +| laundry | ![laundry](../static/icons/laundry_24.svg) | +| library | ![library](../static/icons/library_24.svg) | +| malls | ![malls](../static/icons/malls_24.svg) | +| medicine | ![medicine](../static/icons/medicine_24.svg) | +| memorable_event | ![memorable_event](../static/icons/memorable_event_24.svg) | +| metro | ![metro](../static/icons/metro_24.svg) | +| metro_bus | ![metro_bus](../static/icons/metro_bus_24.svg) | +| metro_cable | ![metro_cable](../static/icons/metro_cable_24.svg) | +| metro_entrance | ![metro_entrance](../static/icons/metro_entrance_24.svg) | +| metro_funicular | ![metro_funicular](../static/icons/metro_funicular_24.svg) | +| metro_light | ![metro_light](../static/icons/metro_light_24.svg) | +| metro_monorail | ![metro_monorail](../static/icons/metro_monorail_24.svg) | +| metro_tram | ![metro_tram](../static/icons/metro_tram_24.svg) | +| mobile_phones | ![mobile_phones](../static/icons/mobile_phones_24.svg) | +| money_coin | ![money_coin](../static/icons/money_coin_24.svg) | +| monument | ![monument](../static/icons/monument_24.svg) | +| mosque | ![mosque](../static/icons/mosque_24.svg) | +| mountain | ![mountain](../static/icons/mountain_24.svg) | +| museum | ![museum](../static/icons/museum_24.svg) | +| office | ![office](../static/icons/office_24.svg) | +| office_service | ![office_service](../static/icons/office_service_24.svg) | +| orthodox_church | ![orthodox_church](../static/icons/orthodox_church_24.svg) | +| park | ![park](../static/icons/park_24.svg) | +| pavilion | ![pavilion](../static/icons/pavilion_24.svg) | +| pet_playground | ![pet_playground](../static/icons/pet_playground_24.svg) | +| petshop | ![petshop](../static/icons/petshop_24.svg) | +| photo | ![photo](../static/icons/photo_24.svg) | +| picnic | ![picnic](../static/icons/picnic_24.svg) | +| pier | ![pier](../static/icons/pier_24.svg) | +| playground | ![playground](../static/icons/playground_24.svg) | +| police | ![police](../static/icons/police_24.svg) | +| police_post | ![police_post](../static/icons/police_post_24.svg) | +| port | ![port](../static/icons/port_24.svg) | +| post_office | ![post_office](../static/icons/post_office_24.svg) | +| printing_services | ![printing_services](../static/icons/printing_services_24.svg) | +| protestant_church | ![protestant_church](../static/icons/protestant_church_24.svg) | +| racing | ![racing](../static/icons/racing_24.svg) | +| railway | ![railway](../static/icons/railway_24.svg) | +| railway_station | ![railway_station](../static/icons/railway_station_24.svg) | +| recycling | ![recycling](../static/icons/recycling_24.svg) | +| restaurants | ![restaurants](../static/icons/restaurants_24.svg) | +| rezervation | ![rezervation](../static/icons/rezervation_24.svg) | +| sanatorium | ![sanatorium](../static/icons/sanatorium_24.svg) | +| science | ![science](../static/icons/science_24.svg) | +| skating_rink | ![skating_rink](../static/icons/skating_rink_24.svg) | +| software | ![software](../static/icons/software_24.svg) | +| spa | ![spa](../static/icons/spa_24.svg) | +| sportcenter | ![sportcenter](../static/icons/sportcenter_24.svg) | +| spring | ![spring](../static/icons/spring_24.svg) | +| stadium | ![stadium](../static/icons/stadium_24.svg) | +| supermarket | ![supermarket](../static/icons/supermarket_24.svg) | +| sushi | ![sushi](../static/icons/sushi_24.svg) | +| swimming_pool | ![swimming_pool](../static/icons/swimming_pool_24.svg) | +| synagogue | ![synagogue](../static/icons/synagogue_24.svg) | +| tailor | ![tailor](../static/icons/tailor_24.svg) | +| taxi | ![taxi](../static/icons/taxi_24.svg) | +| theatre | ![theatre](../static/icons/theatre_24.svg) | +| ticket_office | ![ticket_office](../static/icons/ticket_office_24.svg) | +| tire_fitting | ![tire_fitting](../static/icons/tire_fitting_24.svg) | +| tram | ![tram](../static/icons/tram_24.svg) | +| trash | ![trash](../static/icons/trash_24.svg) | +| travel_agency | ![travel_agency](../static/icons/travel_agency_24.svg) | +| viewpoint | ![viewpoint](../static/icons/viewpoint_24.svg) | +| waterfall | ![waterfall](../static/icons/waterfall_24.svg) | +| wc | ![wc](../static/icons/wc_24.svg) | +| zoo | ![zoo](../static/icons/zoo_24.svg) | diff --git a/src/MMapDefaultMarker/backgrounds/micro-poi-stroke.svg b/src/MMapDefaultMarker/backgrounds/micro-poi-stroke.svg deleted file mode 100644 index 4d62815..0000000 --- a/src/MMapDefaultMarker/backgrounds/micro-poi-stroke.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/MMapDefaultMarker/backgrounds/micro-poi.svg b/src/MMapDefaultMarker/backgrounds/micro-poi.svg deleted file mode 100644 index da465bb..0000000 --- a/src/MMapDefaultMarker/backgrounds/micro-poi.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/MMapDefaultMarker/backgrounds/normal-pin.svg b/src/MMapDefaultMarker/backgrounds/normal-pin.svg deleted file mode 100644 index 4fe7f68..0000000 --- a/src/MMapDefaultMarker/backgrounds/normal-pin.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/MMapDefaultMarker/backgrounds/small-poi-stroke.svg b/src/MMapDefaultMarker/backgrounds/small-poi-stroke.svg deleted file mode 100644 index 4a6bb40..0000000 --- a/src/MMapDefaultMarker/backgrounds/small-poi-stroke.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/MMapDefaultMarker/backgrounds/small-poi.svg b/src/MMapDefaultMarker/backgrounds/small-poi.svg deleted file mode 100644 index 3a1c7d7..0000000 --- a/src/MMapDefaultMarker/backgrounds/small-poi.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/MMapDefaultMarker/index.css b/src/MMapDefaultMarker/index.css deleted file mode 100644 index d26740a..0000000 --- a/src/MMapDefaultMarker/index.css +++ /dev/null @@ -1,62 +0,0 @@ -.mappable--default-marker-point { - position: absolute; - cursor: pointer; -} -.mappable--default-marker-point svg { - display: block; -} - -/* normal size */ -.mappable--normal-pin { - display: flex; - flex-direction: column; - align-items: center; - row-gap: 2px; - position: absolute; - transform: translate(-50%, calc(-100% + 4px)); - filter: drop-shadow(0px 2px 6px rgba(24, 27, 34, 0.4)); -} -.mappable--normal-icon { - position: absolute; - transform: translate(-50%, calc(-4px - 2px - 51px + 10px)); /* 51px - pin height, 10px - offset */ -} -.mappable--normal-pin_circle { - display: block; - width: 4px; - height: 4px; - border-width: 2px; - border-style: solid; - border-radius: 50%; - box-shadow: 0px 1px 4px 0px rgba(24, 27, 34, 0.3); -} - -/* small size */ -.mappable--small-poi { - position: absolute; - transform: translate(-50%, -50%); -} -.mappable--small-poi_stroke { - position: absolute; - transform: translate(-50%, -50%); - z-index: -1; - -webkit-filter: drop-shadow(0px 4px 4px rgba(0, 0, 0, 0.1)); - filter: drop-shadow(0px 4px 4px rgba(0, 0, 0, 0.1)); -} -.mappable--small-icon { - position: absolute; - z-index: 1; - transform: translate(-50%, -50%); -} - -/* micro size */ -.mappable--micro-poi { - position: absolute; - transform: translate(-50%, -50%); -} -.mappable--micro-poi_stroke { - position: absolute; - transform: translate(-50%, -50%); - z-index: -1; - -webkit-filter: drop-shadow(0px 4px 4px rgba(0, 0, 0, 0.1)); - filter: drop-shadow(0px 4px 4px rgba(0, 0, 0, 0.1)); -} diff --git a/src/MMapDefaultMarker/index.ts b/src/MMapDefaultMarker/index.ts deleted file mode 100644 index b267052..0000000 --- a/src/MMapDefaultMarker/index.ts +++ /dev/null @@ -1,184 +0,0 @@ -import {MMapMarker, MMapMarkerProps} from '@mappable-world/mappable-types'; -import {IconColor, IconName, IconSize, glyphColors, iconColors, icons} from '../icons'; -import microPoiStrokeSVG from './backgrounds/micro-poi-stroke.svg'; -import microPoiSVG from './backgrounds/micro-poi.svg'; -import normalPinSVG from './backgrounds/normal-pin.svg'; -import smallPoiStrokeSVG from './backgrounds/small-poi-stroke.svg'; -import smallPoiSVG from './backgrounds/small-poi.svg'; -import './index.css'; - -export type ThemesColor = {day: string; night: string}; -export type MarkerColorProps = IconColor | ThemesColor; -export type MarkerSizeProps = IconSize | 'micro'; - -export type MMapDefaultMarkerProps = MMapMarkerProps & { - iconName?: IconName; - color?: MarkerColorProps; - size?: MarkerSizeProps; -}; - -const defaultProps = Object.freeze({color: 'darkgray', size: 'small'}); -type DefaultProps = typeof defaultProps; - -type BackgroundAndIcon = {background: HTMLElement; stroke?: HTMLElement; icon?: HTMLElement}; - -export class MMapDefaultMarker extends mappable.MMapComplexEntity { - static defaultProps = defaultProps; - - private _marker: MMapMarker; - private _markerElement: HTMLElement; - private _color: ThemesColor; - private _background: HTMLElement; - private _stroke?: HTMLElement; - private _icon?: HTMLElement; - - constructor(props: MMapDefaultMarkerProps) { - super(props); - } - - protected __implGetDefaultProps(): DefaultProps { - return MMapDefaultMarker.defaultProps; - } - - protected _onAttach(): void { - this._color = this._getColor(); - - this._markerElement = document.createElement('mappable'); - this._markerElement.classList.add('mappable--default-marker-point'); - - switch (this._props.size) { - case 'normal': - const normal = this._createNormalPin(); - this._icon = normal.icon; - this._background = normal.background; - this._stroke = normal.stroke; - break; - case 'small': - const small = this._createSmallPoi(); - this._icon = small.icon; - this._background = small.background; - this._stroke = small.stroke; - break; - case 'micro': - const micro = this._createMicroPoi(); - this._stroke = micro.stroke; - this._background = micro.background; - break; - } - - this._markerElement.appendChild(this._background); - if (this._stroke) { - this._markerElement.appendChild(this._stroke); - } - if (this._icon) { - this._markerElement.appendChild(this._icon); - } - - this._marker = new mappable.MMapMarker(this._props, this._markerElement); - this.addChild(this._marker); - - this._updateTheme(); - } - - protected _onUpdate(propsDiff: Partial): void { - if (propsDiff.color !== undefined) { - this._color = this._getColor(); - this._updateTheme(); - } - - this._marker.update(this._props); - } - - private _updateTheme() { - switch (this._props.size) { - case 'normal': - const circle = this._background.querySelector('.mappable--normal-pin_circle'); - this._background.style.color = this._color.day; - circle.style.backgroundColor = this._color.day; - this._icon.style.color = glyphColors.day; - circle.style.borderColor = glyphColors.day; - break; - case 'small': - this._background.style.color = this._color.day; - this._stroke.style.color = glyphColors.day; - this._icon.style.color = glyphColors.day; - break; - case 'micro': - this._background.style.color = this._color.day; - this._stroke.style.color = glyphColors.day; - break; - } - } - - private _getIcon(): string { - if (this._props.size === 'micro' || this._props.iconName === undefined) { - return ''; - } - if (icons[this._props.iconName][this._props.size]) { - return icons[this._props.iconName][this._props.size]; - } - return icons[this._props.iconName].normal.replace(/ Date: Thu, 11 Apr 2024 17:43:40 +0300 Subject: [PATCH 25/48] download icons --- .gitignore | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index 7f96c5b..e0ddeaa 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,4 @@ dist node_modules .env -.DS_Store -# FIXME: ignore static folder for a while -static \ No newline at end of file +.DS_Store \ No newline at end of file From 530b4b6203774ad65cd60e2d047f2be2fccc78fd Mon Sep 17 00:00:00 2001 From: Matthew Kalinin Date: Thu, 11 Apr 2024 17:49:00 +0300 Subject: [PATCH 26/48] fix --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index e0ddeaa..3ba0c2c 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,4 @@ dist node_modules .env -.DS_Store \ No newline at end of file +.DS_Store From 074a182d061ba385ba28624641f5a3f4a66cb1d3 Mon Sep 17 00:00:00 2001 From: Matthew Kalinin Date: Thu, 18 Apr 2024 18:09:08 +0300 Subject: [PATCH 27/48] took chunk function from lodash --- tools/utils/make-chunks.ts | 10 ---------- 1 file changed, 10 deletions(-) delete mode 100644 tools/utils/make-chunks.ts diff --git a/tools/utils/make-chunks.ts b/tools/utils/make-chunks.ts deleted file mode 100644 index 5debb13..0000000 --- a/tools/utils/make-chunks.ts +++ /dev/null @@ -1,10 +0,0 @@ -export const makeChunks = (input: T[], perChunk: number): T[][] => { - return input.reduce((chunks, item, index) => { - const chunkIndex = Math.floor(index / perChunk); - if (!chunks[chunkIndex]) { - chunks[chunkIndex] = []; - } - chunks[chunkIndex].push(item); - return chunks; - }, []); -}; From c5b89e9e4ee3208e273fd52837114629ea85568c Mon Sep 17 00:00:00 2001 From: Matthew Kalinin Date: Thu, 18 Apr 2024 18:10:02 +0300 Subject: [PATCH 28/48] rename generated files --- docs/icons.md | 127 -------------------- src/icons/icon-colors.ts | 13 --- src/icons/icon-name.ts | 123 ------------------- src/icons/icons.ts | 247 --------------------------------------- 4 files changed, 510 deletions(-) delete mode 100644 docs/icons.md delete mode 100644 src/icons/icon-colors.ts delete mode 100644 src/icons/icon-name.ts delete mode 100644 src/icons/icons.ts diff --git a/docs/icons.md b/docs/icons.md deleted file mode 100644 index fb76816..0000000 --- a/docs/icons.md +++ /dev/null @@ -1,127 +0,0 @@ - - -# List of supported icons - -| Name | Normal Size | -| --------------------- | ---------------------------------------------------------------------- | -| airport | ![airport](../static/icons/airport_24.svg) | -| attraction | ![attraction](../static/icons/attraction_24.svg) | -| auto | ![auto](../static/icons/auto_24.svg) | -| aviary | ![aviary](../static/icons/aviary_24.svg) | -| baby_shop | ![baby_shop](../static/icons/baby_shop_24.svg) | -| banks | ![banks](../static/icons/banks_24.svg) | -| barbeque | ![barbeque](../static/icons/barbeque_24.svg) | -| bars | ![bars](../static/icons/bars_24.svg) | -| beach | ![beach](../static/icons/beach_24.svg) | -| bench | ![bench](../static/icons/bench_24.svg) | -| bike | ![bike](../static/icons/bike_24.svg) | -| bike_rent | ![bike_rent](../static/icons/bike_rent_24.svg) | -| boat_station | ![boat_station](../static/icons/boat_station_24.svg) | -| bookstore | ![bookstore](../static/icons/bookstore_24.svg) | -| buddhism | ![buddhism](../static/icons/buddhism_24.svg) | -| building | ![building](../static/icons/building_24.svg) | -| bus | ![bus](../static/icons/bus_24.svg) | -| cafe | ![cafe](../static/icons/cafe_24.svg) | -| car_park | ![car_park](../static/icons/car_park_24.svg) | -| catholic_church | ![catholic_church](../static/icons/catholic_church_24.svg) | -| cemetery | ![cemetery](../static/icons/cemetery_24.svg) | -| childrens_playground | ![childrens_playground](../static/icons/childrens_playground_24.svg) | -| cinemas | ![cinemas](../static/icons/cinemas_24.svg) | -| clothes_shop | ![clothes_shop](../static/icons/clothes_shop_24.svg) | -| college | ![college](../static/icons/college_24.svg) | -| concert_hall | ![concert_hall](../static/icons/concert_hall_24.svg) | -| confectionary | ![confectionary](../static/icons/confectionary_24.svg) | -| currency_exchange | ![currency_exchange](../static/icons/currency_exchange_24.svg) | -| dental | ![dental](../static/icons/dental_24.svg) | -| driving_school | ![driving_school](../static/icons/driving_school_24.svg) | -| drugstores | ![drugstores](../static/icons/drugstores_24.svg) | -| dry_cleaning | ![dry_cleaning](../static/icons/dry_cleaning_24.svg) | -| equestrian | ![equestrian](../static/icons/equestrian_24.svg) | -| fallback | ![fallback](../static/icons/fallback_24.svg) | -| fast_food | ![fast_food](../static/icons/fast_food_24.svg) | -| film_studio | ![film_studio](../static/icons/film_studio_24.svg) | -| fire_station | ![fire_station](../static/icons/fire_station_24.svg) | -| fitness | ![fitness](../static/icons/fitness_24.svg) | -| flower_shop | ![flower_shop](../static/icons/flower_shop_24.svg) | -| forest | ![forest](../static/icons/forest_24.svg) | -| fountain | ![fountain](../static/icons/fountain_24.svg) | -| furniture_store | ![furniture_store](../static/icons/furniture_store_24.svg) | -| garden | ![garden](../static/icons/garden_24.svg) | -| gasstation | ![gasstation](../static/icons/gasstation_24.svg) | -| government | ![government](../static/icons/government_24.svg) | -| hairdressers | ![hairdressers](../static/icons/hairdressers_24.svg) | -| haulier | ![haulier](../static/icons/haulier_24.svg) | -| helicopter | ![helicopter](../static/icons/helicopter_24.svg) | -| hospital | ![hospital](../static/icons/hospital_24.svg) | -| hotels | ![hotels](../static/icons/hotels_24.svg) | -| hypermarket | ![hypermarket](../static/icons/hypermarket_24.svg) | -| industrial_enterprise | ![industrial_enterprise](../static/icons/industrial_enterprise_24.svg) | -| information | ![information](../static/icons/information_24.svg) | -| kindergarten | ![kindergarten](../static/icons/kindergarten_24.svg) | -| landmark | ![landmark](../static/icons/landmark_24.svg) | -| laundry | ![laundry](../static/icons/laundry_24.svg) | -| library | ![library](../static/icons/library_24.svg) | -| malls | ![malls](../static/icons/malls_24.svg) | -| medicine | ![medicine](../static/icons/medicine_24.svg) | -| memorable_event | ![memorable_event](../static/icons/memorable_event_24.svg) | -| metro | ![metro](../static/icons/metro_24.svg) | -| metro_bus | ![metro_bus](../static/icons/metro_bus_24.svg) | -| metro_cable | ![metro_cable](../static/icons/metro_cable_24.svg) | -| metro_entrance | ![metro_entrance](../static/icons/metro_entrance_24.svg) | -| metro_funicular | ![metro_funicular](../static/icons/metro_funicular_24.svg) | -| metro_light | ![metro_light](../static/icons/metro_light_24.svg) | -| metro_monorail | ![metro_monorail](../static/icons/metro_monorail_24.svg) | -| metro_tram | ![metro_tram](../static/icons/metro_tram_24.svg) | -| mobile_phones | ![mobile_phones](../static/icons/mobile_phones_24.svg) | -| money_coin | ![money_coin](../static/icons/money_coin_24.svg) | -| monument | ![monument](../static/icons/monument_24.svg) | -| mosque | ![mosque](../static/icons/mosque_24.svg) | -| mountain | ![mountain](../static/icons/mountain_24.svg) | -| museum | ![museum](../static/icons/museum_24.svg) | -| office | ![office](../static/icons/office_24.svg) | -| office_service | ![office_service](../static/icons/office_service_24.svg) | -| orthodox_church | ![orthodox_church](../static/icons/orthodox_church_24.svg) | -| park | ![park](../static/icons/park_24.svg) | -| pavilion | ![pavilion](../static/icons/pavilion_24.svg) | -| pet_playground | ![pet_playground](../static/icons/pet_playground_24.svg) | -| petshop | ![petshop](../static/icons/petshop_24.svg) | -| photo | ![photo](../static/icons/photo_24.svg) | -| picnic | ![picnic](../static/icons/picnic_24.svg) | -| pier | ![pier](../static/icons/pier_24.svg) | -| playground | ![playground](../static/icons/playground_24.svg) | -| police | ![police](../static/icons/police_24.svg) | -| police_post | ![police_post](../static/icons/police_post_24.svg) | -| port | ![port](../static/icons/port_24.svg) | -| post_office | ![post_office](../static/icons/post_office_24.svg) | -| printing_services | ![printing_services](../static/icons/printing_services_24.svg) | -| protestant_church | ![protestant_church](../static/icons/protestant_church_24.svg) | -| racing | ![racing](../static/icons/racing_24.svg) | -| railway | ![railway](../static/icons/railway_24.svg) | -| railway_station | ![railway_station](../static/icons/railway_station_24.svg) | -| recycling | ![recycling](../static/icons/recycling_24.svg) | -| restaurants | ![restaurants](../static/icons/restaurants_24.svg) | -| rezervation | ![rezervation](../static/icons/rezervation_24.svg) | -| sanatorium | ![sanatorium](../static/icons/sanatorium_24.svg) | -| science | ![science](../static/icons/science_24.svg) | -| skating_rink | ![skating_rink](../static/icons/skating_rink_24.svg) | -| software | ![software](../static/icons/software_24.svg) | -| spa | ![spa](../static/icons/spa_24.svg) | -| sportcenter | ![sportcenter](../static/icons/sportcenter_24.svg) | -| spring | ![spring](../static/icons/spring_24.svg) | -| stadium | ![stadium](../static/icons/stadium_24.svg) | -| supermarket | ![supermarket](../static/icons/supermarket_24.svg) | -| sushi | ![sushi](../static/icons/sushi_24.svg) | -| swimming_pool | ![swimming_pool](../static/icons/swimming_pool_24.svg) | -| synagogue | ![synagogue](../static/icons/synagogue_24.svg) | -| tailor | ![tailor](../static/icons/tailor_24.svg) | -| taxi | ![taxi](../static/icons/taxi_24.svg) | -| theatre | ![theatre](../static/icons/theatre_24.svg) | -| ticket_office | ![ticket_office](../static/icons/ticket_office_24.svg) | -| tire_fitting | ![tire_fitting](../static/icons/tire_fitting_24.svg) | -| tram | ![tram](../static/icons/tram_24.svg) | -| trash | ![trash](../static/icons/trash_24.svg) | -| travel_agency | ![travel_agency](../static/icons/travel_agency_24.svg) | -| viewpoint | ![viewpoint](../static/icons/viewpoint_24.svg) | -| waterfall | ![waterfall](../static/icons/waterfall_24.svg) | -| wc | ![wc](../static/icons/wc_24.svg) | -| zoo | ![zoo](../static/icons/zoo_24.svg) | diff --git a/src/icons/icon-colors.ts b/src/icons/icon-colors.ts deleted file mode 100644 index b2b9e33..0000000 --- a/src/icons/icon-colors.ts +++ /dev/null @@ -1,13 +0,0 @@ -/** Don't edit manually only color names. Generated by script: ./tools/icons/generate-colors.ts */ -export const iconColors = { - darkgray: {day: '#ada9a6ff', night: '#6f737aff'}, - pink: {day: '#ff8f96ff', night: '#b96066ff'}, - seawave: {day: '#62c0c6ff', night: '#468286ff'}, - orchid: {day: '#e096d0ff', night: '#916187ff'}, - steelblue: {day: '#498ea5ff', night: '#57a8c2ff'}, - bluebell: {day: '#9d9dc4ff', night: '#6767a3ff'}, - ceil: {day: '#88aecfff', night: '#537695ff'}, - green: {day: '#5ebd8cff', night: '#468c68ff'}, - darksalmon: {day: '#f09a75ff', night: '#ab6f55ff'} -} as const; -export type IconColor = keyof typeof iconColors; diff --git a/src/icons/icon-name.ts b/src/icons/icon-name.ts deleted file mode 100644 index a69b724..0000000 --- a/src/icons/icon-name.ts +++ /dev/null @@ -1,123 +0,0 @@ -/** Don't edit manually. Generated by script: ./tools/icons/generate-types.ts */ -export type IconName = - | 'airport' - | 'attraction' - | 'auto' - | 'aviary' - | 'baby_shop' - | 'banks' - | 'barbeque' - | 'bars' - | 'beach' - | 'bench' - | 'bike' - | 'bike_rent' - | 'boat_station' - | 'bookstore' - | 'buddhism' - | 'building' - | 'bus' - | 'cafe' - | 'car_park' - | 'catholic_church' - | 'cemetery' - | 'childrens_playground' - | 'cinemas' - | 'clothes_shop' - | 'college' - | 'concert_hall' - | 'confectionary' - | 'currency_exchange' - | 'dental' - | 'driving_school' - | 'drugstores' - | 'dry_cleaning' - | 'equestrian' - | 'fallback' - | 'fast_food' - | 'film_studio' - | 'fire_station' - | 'fitness' - | 'flower_shop' - | 'forest' - | 'fountain' - | 'furniture_store' - | 'garden' - | 'gasstation' - | 'government' - | 'hairdressers' - | 'haulier' - | 'helicopter' - | 'hospital' - | 'hotels' - | 'hypermarket' - | 'industrial_enterprise' - | 'information' - | 'kindergarten' - | 'landmark' - | 'laundry' - | 'library' - | 'malls' - | 'medicine' - | 'memorable_event' - | 'metro' - | 'metro_bus' - | 'metro_cable' - | 'metro_entrance' - | 'metro_funicular' - | 'metro_light' - | 'metro_monorail' - | 'metro_tram' - | 'mobile_phones' - | 'money_coin' - | 'monument' - | 'mosque' - | 'mountain' - | 'museum' - | 'office' - | 'office_service' - | 'orthodox_church' - | 'park' - | 'pavilion' - | 'pet_playground' - | 'petshop' - | 'photo' - | 'picnic' - | 'pier' - | 'playground' - | 'police' - | 'police_post' - | 'port' - | 'post_office' - | 'printing_services' - | 'protestant_church' - | 'racing' - | 'railway' - | 'railway_station' - | 'recycling' - | 'restaurants' - | 'rezervation' - | 'sanatorium' - | 'science' - | 'skating_rink' - | 'software' - | 'spa' - | 'sportcenter' - | 'spring' - | 'stadium' - | 'supermarket' - | 'sushi' - | 'swimming_pool' - | 'synagogue' - | 'tailor' - | 'taxi' - | 'theatre' - | 'ticket_office' - | 'tire_fitting' - | 'tram' - | 'trash' - | 'travel_agency' - | 'viewpoint' - | 'waterfall' - | 'wc' - | 'zoo'; diff --git a/src/icons/icons.ts b/src/icons/icons.ts deleted file mode 100644 index ae767aa..0000000 --- a/src/icons/icons.ts +++ /dev/null @@ -1,247 +0,0 @@ -/** Don't edit manually. Generated by script: ./tools/icons/generate-imports.ts */ -import airport_24 from '../../static/icons/airport_24.svg'; -import attraction_24 from '../../static/icons/attraction_24.svg'; -import auto_24 from '../../static/icons/auto_24.svg'; -import aviary_24 from '../../static/icons/aviary_24.svg'; -import baby_shop_24 from '../../static/icons/baby_shop_24.svg'; -import banks_24 from '../../static/icons/banks_24.svg'; -import barbeque_24 from '../../static/icons/barbeque_24.svg'; -import bars_24 from '../../static/icons/bars_24.svg'; -import beach_24 from '../../static/icons/beach_24.svg'; -import bench_24 from '../../static/icons/bench_24.svg'; -import bike_24 from '../../static/icons/bike_24.svg'; -import bike_rent_24 from '../../static/icons/bike_rent_24.svg'; -import boat_station_24 from '../../static/icons/boat_station_24.svg'; -import bookstore_24 from '../../static/icons/bookstore_24.svg'; -import buddhism_24 from '../../static/icons/buddhism_24.svg'; -import building_24 from '../../static/icons/building_24.svg'; -import bus_24 from '../../static/icons/bus_24.svg'; -import cafe_24 from '../../static/icons/cafe_24.svg'; -import car_park_24 from '../../static/icons/car_park_24.svg'; -import catholic_church_24 from '../../static/icons/catholic_church_24.svg'; -import cemetery_24 from '../../static/icons/cemetery_24.svg'; -import childrens_playground_24 from '../../static/icons/childrens_playground_24.svg'; -import cinemas_24 from '../../static/icons/cinemas_24.svg'; -import clothes_shop_24 from '../../static/icons/clothes_shop_24.svg'; -import college_24 from '../../static/icons/college_24.svg'; -import concert_hall_24 from '../../static/icons/concert_hall_24.svg'; -import confectionary_24 from '../../static/icons/confectionary_24.svg'; -import currency_exchange_24 from '../../static/icons/currency_exchange_24.svg'; -import dental_24 from '../../static/icons/dental_24.svg'; -import driving_school_24 from '../../static/icons/driving_school_24.svg'; -import drugstores_24 from '../../static/icons/drugstores_24.svg'; -import dry_cleaning_24 from '../../static/icons/dry_cleaning_24.svg'; -import equestrian_24 from '../../static/icons/equestrian_24.svg'; -import fallback_24 from '../../static/icons/fallback_24.svg'; -import fast_food_24 from '../../static/icons/fast_food_24.svg'; -import film_studio_24 from '../../static/icons/film_studio_24.svg'; -import fire_station_24 from '../../static/icons/fire_station_24.svg'; -import fitness_24 from '../../static/icons/fitness_24.svg'; -import flower_shop_24 from '../../static/icons/flower_shop_24.svg'; -import forest_24 from '../../static/icons/forest_24.svg'; -import fountain_24 from '../../static/icons/fountain_24.svg'; -import furniture_store_24 from '../../static/icons/furniture_store_24.svg'; -import garden_24 from '../../static/icons/garden_24.svg'; -import gasstation_24 from '../../static/icons/gasstation_24.svg'; -import government_24 from '../../static/icons/government_24.svg'; -import hairdressers_24 from '../../static/icons/hairdressers_24.svg'; -import haulier_24 from '../../static/icons/haulier_24.svg'; -import helicopter_24 from '../../static/icons/helicopter_24.svg'; -import hospital_24 from '../../static/icons/hospital_24.svg'; -import hotels_24 from '../../static/icons/hotels_24.svg'; -import hypermarket_24 from '../../static/icons/hypermarket_24.svg'; -import industrial_enterprise_24 from '../../static/icons/industrial_enterprise_24.svg'; -import information_24 from '../../static/icons/information_24.svg'; -import kindergarten_24 from '../../static/icons/kindergarten_24.svg'; -import landmark_24 from '../../static/icons/landmark_24.svg'; -import laundry_24 from '../../static/icons/laundry_24.svg'; -import library_24 from '../../static/icons/library_24.svg'; -import malls_24 from '../../static/icons/malls_24.svg'; -import medicine_24 from '../../static/icons/medicine_24.svg'; -import memorable_event_24 from '../../static/icons/memorable_event_24.svg'; -import metro_24 from '../../static/icons/metro_24.svg'; -import metro_bus_24 from '../../static/icons/metro_bus_24.svg'; -import metro_cable_24 from '../../static/icons/metro_cable_24.svg'; -import metro_entrance_24 from '../../static/icons/metro_entrance_24.svg'; -import metro_funicular_24 from '../../static/icons/metro_funicular_24.svg'; -import metro_light_24 from '../../static/icons/metro_light_24.svg'; -import metro_monorail_24 from '../../static/icons/metro_monorail_24.svg'; -import metro_tram_24 from '../../static/icons/metro_tram_24.svg'; -import mobile_phones_24 from '../../static/icons/mobile_phones_24.svg'; -import money_coin_24 from '../../static/icons/money_coin_24.svg'; -import monument_24 from '../../static/icons/monument_24.svg'; -import mosque_24 from '../../static/icons/mosque_24.svg'; -import mountain_24 from '../../static/icons/mountain_24.svg'; -import museum_24 from '../../static/icons/museum_24.svg'; -import office_24 from '../../static/icons/office_24.svg'; -import office_service_24 from '../../static/icons/office_service_24.svg'; -import orthodox_church_24 from '../../static/icons/orthodox_church_24.svg'; -import park_24 from '../../static/icons/park_24.svg'; -import pavilion_24 from '../../static/icons/pavilion_24.svg'; -import pet_playground_24 from '../../static/icons/pet_playground_24.svg'; -import petshop_24 from '../../static/icons/petshop_24.svg'; -import photo_24 from '../../static/icons/photo_24.svg'; -import picnic_24 from '../../static/icons/picnic_24.svg'; -import pier_24 from '../../static/icons/pier_24.svg'; -import playground_24 from '../../static/icons/playground_24.svg'; -import police_24 from '../../static/icons/police_24.svg'; -import police_post_24 from '../../static/icons/police_post_24.svg'; -import port_24 from '../../static/icons/port_24.svg'; -import post_office_24 from '../../static/icons/post_office_24.svg'; -import printing_services_24 from '../../static/icons/printing_services_24.svg'; -import protestant_church_24 from '../../static/icons/protestant_church_24.svg'; -import racing_24 from '../../static/icons/racing_24.svg'; -import railway_24 from '../../static/icons/railway_24.svg'; -import railway_station_24 from '../../static/icons/railway_station_24.svg'; -import recycling_24 from '../../static/icons/recycling_24.svg'; -import restaurants_24 from '../../static/icons/restaurants_24.svg'; -import rezervation_24 from '../../static/icons/rezervation_24.svg'; -import sanatorium_24 from '../../static/icons/sanatorium_24.svg'; -import science_24 from '../../static/icons/science_24.svg'; -import skating_rink_24 from '../../static/icons/skating_rink_24.svg'; -import software_24 from '../../static/icons/software_24.svg'; -import spa_24 from '../../static/icons/spa_24.svg'; -import sportcenter_24 from '../../static/icons/sportcenter_24.svg'; -import spring_24 from '../../static/icons/spring_24.svg'; -import stadium_24 from '../../static/icons/stadium_24.svg'; -import supermarket_24 from '../../static/icons/supermarket_24.svg'; -import sushi_24 from '../../static/icons/sushi_24.svg'; -import swimming_pool_24 from '../../static/icons/swimming_pool_24.svg'; -import synagogue_24 from '../../static/icons/synagogue_24.svg'; -import tailor_24 from '../../static/icons/tailor_24.svg'; -import taxi_24 from '../../static/icons/taxi_24.svg'; -import theatre_24 from '../../static/icons/theatre_24.svg'; -import ticket_office_24 from '../../static/icons/ticket_office_24.svg'; -import tire_fitting_24 from '../../static/icons/tire_fitting_24.svg'; -import tram_24 from '../../static/icons/tram_24.svg'; -import trash_24 from '../../static/icons/trash_24.svg'; -import travel_agency_24 from '../../static/icons/travel_agency_24.svg'; -import viewpoint_24 from '../../static/icons/viewpoint_24.svg'; -import waterfall_24 from '../../static/icons/waterfall_24.svg'; -import wc_24 from '../../static/icons/wc_24.svg'; -import zoo_24 from '../../static/icons/zoo_24.svg'; - -import type {Icons} from './types'; -export const icons: Icons = { - airport: airport_24, - attraction: attraction_24, - auto: auto_24, - aviary: aviary_24, - baby_shop: baby_shop_24, - banks: banks_24, - barbeque: barbeque_24, - bars: bars_24, - beach: beach_24, - bench: bench_24, - bike: bike_24, - bike_rent: bike_rent_24, - boat_station: boat_station_24, - bookstore: bookstore_24, - buddhism: buddhism_24, - building: building_24, - bus: bus_24, - cafe: cafe_24, - car_park: car_park_24, - catholic_church: catholic_church_24, - cemetery: cemetery_24, - childrens_playground: childrens_playground_24, - cinemas: cinemas_24, - clothes_shop: clothes_shop_24, - college: college_24, - concert_hall: concert_hall_24, - confectionary: confectionary_24, - currency_exchange: currency_exchange_24, - dental: dental_24, - driving_school: driving_school_24, - drugstores: drugstores_24, - dry_cleaning: dry_cleaning_24, - equestrian: equestrian_24, - fallback: fallback_24, - fast_food: fast_food_24, - film_studio: film_studio_24, - fire_station: fire_station_24, - fitness: fitness_24, - flower_shop: flower_shop_24, - forest: forest_24, - fountain: fountain_24, - furniture_store: furniture_store_24, - garden: garden_24, - gasstation: gasstation_24, - government: government_24, - hairdressers: hairdressers_24, - haulier: haulier_24, - helicopter: helicopter_24, - hospital: hospital_24, - hotels: hotels_24, - hypermarket: hypermarket_24, - industrial_enterprise: industrial_enterprise_24, - information: information_24, - kindergarten: kindergarten_24, - landmark: landmark_24, - laundry: laundry_24, - library: library_24, - malls: malls_24, - medicine: medicine_24, - memorable_event: memorable_event_24, - metro: metro_24, - metro_bus: metro_bus_24, - metro_cable: metro_cable_24, - metro_entrance: metro_entrance_24, - metro_funicular: metro_funicular_24, - metro_light: metro_light_24, - metro_monorail: metro_monorail_24, - metro_tram: metro_tram_24, - mobile_phones: mobile_phones_24, - money_coin: money_coin_24, - monument: monument_24, - mosque: mosque_24, - mountain: mountain_24, - museum: museum_24, - office: office_24, - office_service: office_service_24, - orthodox_church: orthodox_church_24, - park: park_24, - pavilion: pavilion_24, - pet_playground: pet_playground_24, - petshop: petshop_24, - photo: photo_24, - picnic: picnic_24, - pier: pier_24, - playground: playground_24, - police: police_24, - police_post: police_post_24, - port: port_24, - post_office: post_office_24, - printing_services: printing_services_24, - protestant_church: protestant_church_24, - racing: racing_24, - railway: railway_24, - railway_station: railway_station_24, - recycling: recycling_24, - restaurants: restaurants_24, - rezervation: rezervation_24, - sanatorium: sanatorium_24, - science: science_24, - skating_rink: skating_rink_24, - software: software_24, - spa: spa_24, - sportcenter: sportcenter_24, - spring: spring_24, - stadium: stadium_24, - supermarket: supermarket_24, - sushi: sushi_24, - swimming_pool: swimming_pool_24, - synagogue: synagogue_24, - tailor: tailor_24, - taxi: taxi_24, - theatre: theatre_24, - ticket_office: ticket_office_24, - tire_fitting: tire_fitting_24, - tram: tram_24, - trash: trash_24, - travel_agency: travel_agency_24, - viewpoint: viewpoint_24, - waterfall: waterfall_24, - wc: wc_24, - zoo: zoo_24 -}; From 41328c24c73cc955595cb0722f67772635dcc847 Mon Sep 17 00:00:00 2001 From: Matthew Kalinin Date: Mon, 15 Apr 2024 14:16:24 +0300 Subject: [PATCH 29/48] add tooltip marker --- example/default-tooltip/common.ts | 5 ++ example/default-tooltip/react/index.html | 36 ++++++++++ example/default-tooltip/react/index.tsx | 48 +++++++++++++ example/default-tooltip/vanilla/index.html | 34 +++++++++ example/default-tooltip/vanilla/index.ts | 27 ++++++++ example/default-tooltip/vue/index.html | 35 ++++++++++ example/default-tooltip/vue/index.ts | 62 +++++++++++++++++ src/markers/MMapPopupMarker/index.ts | 3 + src/markers/MMapTooltipMarker/index.css | 62 +++++++++++++++++ src/markers/MMapTooltipMarker/index.ts | 80 ++++++++++++++++++++++ src/markers/MMapTooltipMarker/tail.svg | 1 + src/markers/MMapTooltipMarker/vue/index.ts | 28 ++++++++ src/markers/index.ts | 2 + 13 files changed, 423 insertions(+) create mode 100644 example/default-tooltip/common.ts create mode 100644 example/default-tooltip/react/index.html create mode 100644 example/default-tooltip/react/index.tsx create mode 100644 example/default-tooltip/vanilla/index.html create mode 100644 example/default-tooltip/vanilla/index.ts create mode 100644 example/default-tooltip/vue/index.html create mode 100644 example/default-tooltip/vue/index.ts create mode 100644 src/markers/MMapPopupMarker/index.ts create mode 100644 src/markers/MMapTooltipMarker/index.css create mode 100644 src/markers/MMapTooltipMarker/index.ts create mode 100644 src/markers/MMapTooltipMarker/tail.svg create mode 100644 src/markers/MMapTooltipMarker/vue/index.ts diff --git a/example/default-tooltip/common.ts b/example/default-tooltip/common.ts new file mode 100644 index 0000000..e840eca --- /dev/null +++ b/example/default-tooltip/common.ts @@ -0,0 +1,5 @@ +import type {MMapLocationRequest, LngLat} from '@mappable-world/mappable-types'; + +export const CENTER: LngLat = [55.442795, 25.24107]; +export const LOCATION: MMapLocationRequest = {center: CENTER, zoom: 14}; +export const TOOLTIP_TEXT = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse dictum'; diff --git a/example/default-tooltip/react/index.html b/example/default-tooltip/react/index.html new file mode 100644 index 0000000..b9c642a --- /dev/null +++ b/example/default-tooltip/react/index.html @@ -0,0 +1,36 @@ + + + + React example mappable-default-ui-theme + + + + + + + + + + + + + + +
+ + diff --git a/example/default-tooltip/react/index.tsx b/example/default-tooltip/react/index.tsx new file mode 100644 index 0000000..4ffd3bf --- /dev/null +++ b/example/default-tooltip/react/index.tsx @@ -0,0 +1,48 @@ +import {MMapTooltipMarkerProps} from '../../src'; +import {CENTER, LOCATION, TOOLTIP_TEXT} from '../common'; + +window.map = null; + +main(); +async function main() { + const [mappableReact] = await Promise.all([mappable.import('@mappable-world/mappable-reactify'), mappable.ready]); + const reactify = mappableReact.reactify.bindTo(React, ReactDOM); + + const {MMap, MMapDefaultSchemeLayer, MMapDefaultFeaturesLayer, MMapControls, MMapControlButton} = + reactify.module(mappable); + + const {useState, useCallback} = React; + + const {MMapTooltipMarker} = reactify.module(await mappable.import('@mappable-world/mappable-default-ui-theme')); + + ReactDOM.render( + + + , + document.getElementById('app') + ); + + function App() { + const [location] = useState(LOCATION); + const [position, setPosition] = useState(undefined); + + const positionLeft = useCallback(() => setPosition('left'), []); + const positionBottom = useCallback(() => setPosition('bottom'), []); + const positionTop = useCallback(() => setPosition('top'), []); + const positionRight = useCallback(() => setPosition('right'), []); + + return ( + (map = x)}> + + + + + + + + + + + ); + } +} diff --git a/example/default-tooltip/vanilla/index.html b/example/default-tooltip/vanilla/index.html new file mode 100644 index 0000000..e4d869a --- /dev/null +++ b/example/default-tooltip/vanilla/index.html @@ -0,0 +1,34 @@ + + + + Vanilla example mappable-default-ui-theme + + + + + + + + + + + + +
+ + diff --git a/example/default-tooltip/vanilla/index.ts b/example/default-tooltip/vanilla/index.ts new file mode 100644 index 0000000..2ae4008 --- /dev/null +++ b/example/default-tooltip/vanilla/index.ts @@ -0,0 +1,27 @@ +import {CENTER, LOCATION, TOOLTIP_TEXT} from '../common'; +window.map = null; + +main(); +async function main() { + await mappable.ready; + const {MMap, MMapDefaultSchemeLayer, MMapDefaultFeaturesLayer, MMapControls, MMapControlButton} = mappable; + + const {MMapTooltipMarker} = await mappable.import('@mappable-world/mappable-default-ui-theme'); + + map = new MMap(document.getElementById('app'), {location: LOCATION}); + + map.addChild(new MMapDefaultSchemeLayer({})); + map.addChild(new MMapDefaultFeaturesLayer({})); + + map.addChild( + new MMapControls({position: 'top right'}, [ + new MMapControlButton({text: 'Left', onClick: () => tooltip.update({position: 'left'})}), + new MMapControlButton({text: 'Bottom', onClick: () => tooltip.update({position: 'bottom'})}), + new MMapControlButton({text: 'Top', onClick: () => tooltip.update({position: 'top'})}), + new MMapControlButton({text: 'Right', onClick: () => tooltip.update({position: 'right'})}) + ]) + ); + + const tooltip = new MMapTooltipMarker({coordinates: CENTER, draggable: true, text: TOOLTIP_TEXT}); + map.addChild(tooltip); +} diff --git a/example/default-tooltip/vue/index.html b/example/default-tooltip/vue/index.html new file mode 100644 index 0000000..9471aa8 --- /dev/null +++ b/example/default-tooltip/vue/index.html @@ -0,0 +1,35 @@ + + + + Vue example mappable-default-ui-theme + + + + + + + + + + + + + +
+ + diff --git a/example/default-tooltip/vue/index.ts b/example/default-tooltip/vue/index.ts new file mode 100644 index 0000000..555b9c9 --- /dev/null +++ b/example/default-tooltip/vue/index.ts @@ -0,0 +1,62 @@ +import {MMapTooltipMarkerProps} from '../../src'; +import {CENTER, LOCATION, TOOLTIP_TEXT} from '../common'; + +window.map = null; + +main(); +async function main() { + const [mappableVue] = await Promise.all([mappable.import('@mappable-world/mappable-vuefy'), mappable.ready]); + const vuefy = mappableVue.vuefy.bindTo(Vue); + + const {MMap, MMapDefaultSchemeLayer, MMapDefaultFeaturesLayer, MMapControls, MMapControlButton} = + vuefy.module(mappable); + + const {MMapTooltipMarker} = vuefy.module(await mappable.import('@mappable-world/mappable-default-ui-theme')); + + const app = Vue.createApp({ + components: { + MMap, + MMapDefaultSchemeLayer, + MMapDefaultFeaturesLayer, + MMapControls, + MMapControlButton, + MMapTooltipMarker + }, + setup() { + const refMap = (ref: any) => { + window.map = ref?.entity; + }; + const position = Vue.ref(undefined); + + const positionLeft = () => (position.value = 'left'); + const positionBottom = () => (position.value = 'bottom'); + const positionTop = () => (position.value = 'top'); + const positionRight = () => (position.value = 'right'); + + return { + LOCATION, + CENTER, + TOOLTIP_TEXT, + position, + refMap, + positionLeft, + positionBottom, + positionTop, + positionRight + }; + }, + template: ` + + + + + + + + + + + ` + }); + app.mount('#app'); +} diff --git a/src/markers/MMapPopupMarker/index.ts b/src/markers/MMapPopupMarker/index.ts new file mode 100644 index 0000000..d51c1bc --- /dev/null +++ b/src/markers/MMapPopupMarker/index.ts @@ -0,0 +1,3 @@ +// TODO: add popup marker +export type MMapPopupMarkerProps = {}; +export class MMapPopupMarker extends mappable.MMapComplexEntity {} diff --git a/src/markers/MMapTooltipMarker/index.css b/src/markers/MMapTooltipMarker/index.css new file mode 100644 index 0000000..c109b63 --- /dev/null +++ b/src/markers/MMapTooltipMarker/index.css @@ -0,0 +1,62 @@ +.mappable--tooltip-marker { + --tail-height: 12px; +} +.mappable--tooltip-marker svg { + display: block; +} + +.mappable--tooltip-marker_container { + box-sizing: border-box; + display: block; + position: absolute; + padding: 8px 12px; + border-radius: 12px; + background-color: #fff; + color: #34374a; + font-size: 14px; + font-style: normal; + font-weight: 400; + line-height: 20px; + box-shadow: 0px 4px 12px 0px #5f69831a; +} + +.mappable--tooltip-marker_tail { + display: block; + position: absolute; + color: #fff; + box-shadow: 0px 4px 24px 0px #5f69830a; +} + +/* positions */ +.position-top { + .mappable--tooltip-marker_container { + transform: translate(-50%, calc(-100% - var(--tail-height))); + } + .mappable--tooltip-marker_tail { + transform: translate(-50%, -100%) rotate(180deg); + } +} +.position-bottom { + .mappable--tooltip-marker_container { + transform: translate(-50%, var(--tail-height)); + } + .mappable--tooltip-marker_tail { + transform: translate(-50%, 0); + } +} +.position-left { + .mappable--tooltip-marker_container { + transform: translate(calc(-100% - var(--tail-height)), -50%); + } + .mappable--tooltip-marker_tail { + transform: translate(-100%, -50%) rotate(90deg); + } +} +.position-right { + .mappable--tooltip-marker_container { + transform: translate(var(--tail-height), -50%); + } + .mappable--tooltip-marker_tail { + transform: translate(0, -50%) rotate(-90deg); + } +} diff --git a/src/markers/MMapTooltipMarker/index.ts b/src/markers/MMapTooltipMarker/index.ts new file mode 100644 index 0000000..386e78a --- /dev/null +++ b/src/markers/MMapTooltipMarker/index.ts @@ -0,0 +1,80 @@ +import {MMapMarker, MMapMarkerProps} from '@mappable-world/mappable-types'; +import {MMapMarkerVuefyOptions} from './vue'; + +import './index.css'; +import tailSVG from './tail.svg'; + +type VerticalPosition = 'top' | 'bottom'; +type HorizontalPosition = 'left' | 'right'; +export type MMapTooltipPositionProps = VerticalPosition | HorizontalPosition; + +export type MMapTooltipMarkerProps = MMapMarkerProps & { + text: string; + position?: MMapTooltipPositionProps; +}; + +const defaultProps = Object.freeze({position: 'top'}); +type DefaultProps = typeof defaultProps; + +export class MMapTooltipMarker extends mappable.MMapComplexEntity { + static defaultProps = defaultProps; + static [mappable.optionsKeyVuefy] = MMapMarkerVuefyOptions; + + private _markerElement: HTMLElement; + private _tooltipContainer: HTMLElement; + private _tooltipTail: HTMLElement; + private _marker: MMapMarker; + + protected __implGetDefaultProps(): DefaultProps { + return MMapTooltipMarker.defaultProps; + } + + protected _onAttach(): void { + this._markerElement = document.createElement('mappable'); + this._markerElement.classList.add('mappable--tooltip-marker'); + + this._tooltipContainer = document.createElement('mappable'); + this._tooltipContainer.classList.add('mappable--tooltip-marker_container'); + this._tooltipContainer.textContent = this._props.text; + + this._tooltipTail = document.createElement('mappable'); + this._tooltipTail.classList.add('mappable--tooltip-marker_tail'); + this._tooltipTail.innerHTML = tailSVG; + + this._updateElementPosition(); + + this._markerElement.appendChild(this._tooltipContainer); + this._markerElement.appendChild(this._tooltipTail); + + this._marker = new mappable.MMapMarker(this._props, this._markerElement); + this.addChild(this._marker); + } + + protected _onUpdate(propsDiff: Partial): void { + if (propsDiff.position !== undefined) { + this._updateElementPosition(); + } + + if (propsDiff.text !== undefined) { + this._tooltipContainer.textContent = this._props.text; + } + + this._marker.update(this._props); + } + + private _updateElementPosition() { + const {position} = this._props; + + // check top position + this._markerElement.classList.toggle('position-top', position === 'top'); + + // check bottom position + this._markerElement.classList.toggle('position-bottom', position === 'bottom'); + + // check left position + this._markerElement.classList.toggle('position-left', position === 'left'); + + // check right position + this._markerElement.classList.toggle('position-right', position === 'right'); + } +} diff --git a/src/markers/MMapTooltipMarker/tail.svg b/src/markers/MMapTooltipMarker/tail.svg new file mode 100644 index 0000000..0ac3f80 --- /dev/null +++ b/src/markers/MMapTooltipMarker/tail.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/markers/MMapTooltipMarker/vue/index.ts b/src/markers/MMapTooltipMarker/vue/index.ts new file mode 100644 index 0000000..03736e6 --- /dev/null +++ b/src/markers/MMapTooltipMarker/vue/index.ts @@ -0,0 +1,28 @@ +import {MMapFeatureProps, MMapMarkerEventHandler} from '@mappable-world/mappable-types'; +import {CustomVuefyOptions} from '@mappable-world/mappable-types/modules/vuefy'; +import type TVue from '@vue/runtime-core'; +import {MMapTooltipMarker, MMapTooltipPositionProps} from '../'; + +export const MMapMarkerVuefyOptions: CustomVuefyOptions = { + props: { + coordinates: {type: Object, required: true}, + source: String, + zIndex: {type: Number, default: 0}, + properties: Object, + id: String, + disableRoundCoordinates: {type: Boolean, default: undefined}, + hideOutsideViewport: {type: [Object, Boolean], default: false}, + draggable: {type: Boolean, default: false}, + mapFollowsOnDrag: {type: [Boolean, Object]}, + onDragStart: Function as TVue.PropType, + onDragEnd: Function as TVue.PropType, + onDragMove: Function as TVue.PropType, + blockEvents: {type: Boolean, default: undefined}, + blockBehaviors: {type: Boolean, default: undefined}, + onDoubleClick: Function as TVue.PropType, + onClick: Function as TVue.PropType, + onFastClick: Function as TVue.PropType, + text: {type: String, required: true}, + position: {type: String as TVue.PropType} + } +}; diff --git a/src/markers/index.ts b/src/markers/index.ts index 8ad99ac..3334694 100644 --- a/src/markers/index.ts +++ b/src/markers/index.ts @@ -1 +1,3 @@ export * from './MMapDefaultMarker'; +export {MMapPopupMarker, MMapPopupMarkerProps} from './MMapPopupMarker'; +export {MMapTooltipMarker, MMapTooltipMarkerProps} from './MMapTooltipMarker'; From 90d16cad12a235ae0d8f65f3b17ba9d5ed0ba51d Mon Sep 17 00:00:00 2001 From: Matthew Kalinin Date: Mon, 15 Apr 2024 16:58:55 +0300 Subject: [PATCH 30/48] tooltip combined positions, tooltip offset --- example/default-tooltip/common.ts | 2 +- example/default-tooltip/react/index.tsx | 8 ++ example/default-tooltip/vanilla/index.ts | 4 + example/default-tooltip/vue/index.ts | 12 +++ src/markers/MMapTooltipMarker/index.css | 92 +++++++++++++++++++--- src/markers/MMapTooltipMarker/index.ts | 59 +++++++++++--- src/markers/MMapTooltipMarker/vue/index.ts | 5 +- 7 files changed, 156 insertions(+), 26 deletions(-) diff --git a/example/default-tooltip/common.ts b/example/default-tooltip/common.ts index e840eca..ee8a754 100644 --- a/example/default-tooltip/common.ts +++ b/example/default-tooltip/common.ts @@ -2,4 +2,4 @@ import type {MMapLocationRequest, LngLat} from '@mappable-world/mappable-types'; export const CENTER: LngLat = [55.442795, 25.24107]; export const LOCATION: MMapLocationRequest = {center: CENTER, zoom: 14}; -export const TOOLTIP_TEXT = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse dictum'; +export const TOOLTIP_TEXT = 'Default tooltip'; diff --git a/example/default-tooltip/react/index.tsx b/example/default-tooltip/react/index.tsx index 4ffd3bf..6a9a432 100644 --- a/example/default-tooltip/react/index.tsx +++ b/example/default-tooltip/react/index.tsx @@ -27,8 +27,12 @@ async function main() { const [position, setPosition] = useState(undefined); const positionLeft = useCallback(() => setPosition('left'), []); + const positionLeftTop = useCallback(() => setPosition('left top'), []); + const positionLeftBottom = useCallback(() => setPosition('left bottom'), []); const positionBottom = useCallback(() => setPosition('bottom'), []); const positionTop = useCallback(() => setPosition('top'), []); + const positionRightTop = useCallback(() => setPosition('right top'), []); + const positionRightBottom = useCallback(() => setPosition('right bottom'), []); const positionRight = useCallback(() => setPosition('right'), []); return ( @@ -37,8 +41,12 @@ async function main() { + + + + diff --git a/example/default-tooltip/vanilla/index.ts b/example/default-tooltip/vanilla/index.ts index 2ae4008..f882d20 100644 --- a/example/default-tooltip/vanilla/index.ts +++ b/example/default-tooltip/vanilla/index.ts @@ -16,8 +16,12 @@ async function main() { map.addChild( new MMapControls({position: 'top right'}, [ new MMapControlButton({text: 'Left', onClick: () => tooltip.update({position: 'left'})}), + new MMapControlButton({text: 'Left Top', onClick: () => tooltip.update({position: 'left top'})}), + new MMapControlButton({text: 'Left Bottom', onClick: () => tooltip.update({position: 'left bottom'})}), new MMapControlButton({text: 'Bottom', onClick: () => tooltip.update({position: 'bottom'})}), new MMapControlButton({text: 'Top', onClick: () => tooltip.update({position: 'top'})}), + new MMapControlButton({text: 'Right Top', onClick: () => tooltip.update({position: 'right top'})}), + new MMapControlButton({text: 'Right Bottom', onClick: () => tooltip.update({position: 'right bottom'})}), new MMapControlButton({text: 'Right', onClick: () => tooltip.update({position: 'right'})}) ]) ); diff --git a/example/default-tooltip/vue/index.ts b/example/default-tooltip/vue/index.ts index 555b9c9..a2b9d1d 100644 --- a/example/default-tooltip/vue/index.ts +++ b/example/default-tooltip/vue/index.ts @@ -29,8 +29,12 @@ async function main() { const position = Vue.ref(undefined); const positionLeft = () => (position.value = 'left'); + const positionLeftTop = () => (position.value = 'left top'); + const positionLeftBottom = () => (position.value = 'left bottom'); const positionBottom = () => (position.value = 'bottom'); const positionTop = () => (position.value = 'top'); + const positionRightTop = () => (position.value = 'right top'); + const positionRightBottom = () => (position.value = 'right bottom'); const positionRight = () => (position.value = 'right'); return { @@ -40,8 +44,12 @@ async function main() { position, refMap, positionLeft, + positionLeftTop, + positionLeftBottom, positionBottom, positionTop, + positionRightTop, + positionRightBottom, positionRight }; }, @@ -51,8 +59,12 @@ async function main() { + + + + diff --git a/src/markers/MMapTooltipMarker/index.css b/src/markers/MMapTooltipMarker/index.css index c109b63..ed38ce6 100644 --- a/src/markers/MMapTooltipMarker/index.css +++ b/src/markers/MMapTooltipMarker/index.css @@ -1,62 +1,130 @@ .mappable--tooltip-marker { --tail-height: 12px; + --tail-width: 16px; + --border-radius: 12px; + + --tail-height-and-offset: calc(var(--tail-height) + var(--offset)); + + --tooltip-tail-transform-top: translate(-50%, calc(-100% - var(--offset))) rotate(180deg); + --tooltip-tail-transform-bottom: translate(-50%, var(--offset)); } + .mappable--tooltip-marker svg { display: block; } .mappable--tooltip-marker_container { - box-sizing: border-box; + width: max-content; + max-width: 500px; + max-height: 600px; display: block; position: absolute; padding: 8px 12px; - border-radius: 12px; + border-radius: var(--border-radius); background-color: #fff; color: #34374a; font-size: 14px; font-style: normal; font-weight: 400; line-height: 20px; - box-shadow: 0px 4px 12px 0px #5f69831a; + box-shadow: + 0px 4px 12px 0px #5f69831a, + 0px 4px 24px 0px #5f69830a; + overflow: hidden; + text-overflow: ellipsis; } .mappable--tooltip-marker_tail { display: block; position: absolute; color: #fff; - box-shadow: 0px 4px 24px 0px #5f69830a; + + svg { + filter: drop-shadow(0px 4px 24px rgba(95, 105, 131, 0.04)) drop-shadow(0px 4px 12px rgba(95, 105, 131, 0.1)); + } } /* positions */ .position-top { .mappable--tooltip-marker_container { - transform: translate(-50%, calc(-100% - var(--tail-height))); + transform: translate(-50%, calc(-100% - var(--tail-height-and-offset))); } .mappable--tooltip-marker_tail { - transform: translate(-50%, -100%) rotate(180deg); + transform: var(--tooltip-tail-transform-top); + } + /* top left */ + &.position-left { + .mappable--tooltip-marker_container { + transform: translate( + calc(-100% + var(--border-radius) + var(--tail-width) / 2), + calc(-100% - var(--tail-height-and-offset)) + ); + } + .mappable--tooltip-marker_tail { + transform: var(--tooltip-tail-transform-top); + } + } + /* top right */ + &.position-right { + .mappable--tooltip-marker_container { + transform: translate( + calc(-1 * var(--border-radius) - var(--tail-width) / 2), + calc(-100% - var(--tail-height-and-offset)) + ); + } + .mappable--tooltip-marker_tail { + transform: var(--tooltip-tail-transform-top); + } } } + .position-bottom { .mappable--tooltip-marker_container { - transform: translate(-50%, var(--tail-height)); + transform: translate(-50%, var(--tail-height-and-offset)); } .mappable--tooltip-marker_tail { - transform: translate(-50%, 0); + transform: var(--tooltip-tail-transform-bottom); + } + /* bottom left */ + &.position-left { + .mappable--tooltip-marker_container { + transform: translate( + calc(-100% + var(--border-radius) + var(--tail-width) / 2), + var(--tail-height-and-offset) + ); + } + .mappable--tooltip-marker_tail { + transform: var(--tooltip-tail-transform-bottom); + } + } + /* bottom right */ + &.position-right { + .mappable--tooltip-marker_container { + transform: translate( + calc(-1 * var(--border-radius) - var(--tail-width) / 2), + var(--tail-height-and-offset) + ); + } + .mappable--tooltip-marker_tail { + transform: var(--tooltip-tail-transform-bottom); + } } } + .position-left { .mappable--tooltip-marker_container { - transform: translate(calc(-100% - var(--tail-height)), -50%); + transform: translate(calc(-100% - var(--tail-height-and-offset)), -50%); } .mappable--tooltip-marker_tail { - transform: translate(-100%, -50%) rotate(90deg); + transform: translate(calc(-100% - var(--offset)), -50%) rotate(90deg); } } + .position-right { .mappable--tooltip-marker_container { - transform: translate(var(--tail-height), -50%); + transform: translate(var(--tail-height-and-offset), -50%); } .mappable--tooltip-marker_tail { - transform: translate(0, -50%) rotate(-90deg); + transform: translate(var(--offset), -50%) rotate(-90deg); } } diff --git a/src/markers/MMapTooltipMarker/index.ts b/src/markers/MMapTooltipMarker/index.ts index 386e78a..ccf19cb 100644 --- a/src/markers/MMapTooltipMarker/index.ts +++ b/src/markers/MMapTooltipMarker/index.ts @@ -1,24 +1,29 @@ import {MMapMarker, MMapMarkerProps} from '@mappable-world/mappable-types'; -import {MMapMarkerVuefyOptions} from './vue'; +import {MMapTooltipMarkerVuefyOptions} from './vue'; import './index.css'; import tailSVG from './tail.svg'; type VerticalPosition = 'top' | 'bottom'; type HorizontalPosition = 'left' | 'right'; -export type MMapTooltipPositionProps = VerticalPosition | HorizontalPosition; +export type MMapTooltipPositionProps = + | VerticalPosition + | HorizontalPosition + | `${VerticalPosition} ${HorizontalPosition}` + | `${HorizontalPosition} ${VerticalPosition}`; export type MMapTooltipMarkerProps = MMapMarkerProps & { text: string; position?: MMapTooltipPositionProps; + offset?: number; }; -const defaultProps = Object.freeze({position: 'top'}); +const defaultProps = Object.freeze({position: 'top', offset: 0}); type DefaultProps = typeof defaultProps; export class MMapTooltipMarker extends mappable.MMapComplexEntity { static defaultProps = defaultProps; - static [mappable.optionsKeyVuefy] = MMapMarkerVuefyOptions; + static [mappable.optionsKeyVuefy] = MMapTooltipMarkerVuefyOptions; private _markerElement: HTMLElement; private _tooltipContainer: HTMLElement; @@ -41,7 +46,8 @@ export class MMapTooltipMarker extends mappable.MMapComplexEntity): void { if (propsDiff.position !== undefined) { - this._updateElementPosition(); + this._updatePosition(); + } + if (propsDiff.offset !== undefined) { + this._updateOffset(); } if (propsDiff.text !== undefined) { @@ -62,19 +71,47 @@ export class MMapTooltipMarker extends mappable.MMapComplexEntity = { + top: 'vertical', + left: 'horizontal', + bottom: 'vertical', + right: 'horizontal' + }; + + if (position === 'top' || position === 'bottom') { + verticalPosition = position; + } else if (position === 'left' || position === 'right') { + horizontalPosition = position; + } else { + const [first, second] = position.split(' ') as (HorizontalPosition | VerticalPosition)[]; + if (positionTypeHash[first] === 'vertical' && positionTypeHash[second] === 'horizontal') { + verticalPosition = first as VerticalPosition; + horizontalPosition = second as HorizontalPosition; + } else if (positionTypeHash[first] === 'horizontal' && positionTypeHash[second] === 'vertical') { + verticalPosition = second as VerticalPosition; + horizontalPosition = first as HorizontalPosition; + } + } // check top position - this._markerElement.classList.toggle('position-top', position === 'top'); + this._markerElement.classList.toggle('position-top', verticalPosition === 'top'); // check bottom position - this._markerElement.classList.toggle('position-bottom', position === 'bottom'); + this._markerElement.classList.toggle('position-bottom', verticalPosition === 'bottom'); // check left position - this._markerElement.classList.toggle('position-left', position === 'left'); + this._markerElement.classList.toggle('position-left', horizontalPosition === 'left'); // check right position - this._markerElement.classList.toggle('position-right', position === 'right'); + this._markerElement.classList.toggle('position-right', horizontalPosition === 'right'); } } diff --git a/src/markers/MMapTooltipMarker/vue/index.ts b/src/markers/MMapTooltipMarker/vue/index.ts index 03736e6..90ca9fe 100644 --- a/src/markers/MMapTooltipMarker/vue/index.ts +++ b/src/markers/MMapTooltipMarker/vue/index.ts @@ -3,7 +3,7 @@ import {CustomVuefyOptions} from '@mappable-world/mappable-types/modules/vuefy'; import type TVue from '@vue/runtime-core'; import {MMapTooltipMarker, MMapTooltipPositionProps} from '../'; -export const MMapMarkerVuefyOptions: CustomVuefyOptions = { +export const MMapTooltipMarkerVuefyOptions: CustomVuefyOptions = { props: { coordinates: {type: Object, required: true}, source: String, @@ -23,6 +23,7 @@ export const MMapMarkerVuefyOptions: CustomVuefyOptions = { onClick: Function as TVue.PropType, onFastClick: Function as TVue.PropType, text: {type: String, required: true}, - position: {type: String as TVue.PropType} + position: {type: String as TVue.PropType}, + offset: {type: Number, default: 0} } }; From 5999b7f1dc06bfd65318e06af641339d15a609c6 Mon Sep 17 00:00:00 2001 From: Matthew Kalinin Date: Mon, 15 Apr 2024 17:25:32 +0300 Subject: [PATCH 31/48] support dark theme --- src/markers/MMapTooltipMarker/index.css | 9 +++++++++ src/markers/MMapTooltipMarker/index.ts | 18 ++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/src/markers/MMapTooltipMarker/index.css b/src/markers/MMapTooltipMarker/index.css index ed38ce6..3c56af6 100644 --- a/src/markers/MMapTooltipMarker/index.css +++ b/src/markers/MMapTooltipMarker/index.css @@ -32,6 +32,11 @@ 0px 4px 24px 0px #5f69830a; overflow: hidden; text-overflow: ellipsis; + + &.mappable--tooltip__dark { + background-color: #272729; + color: #c8c9cc; + } } .mappable--tooltip-marker_tail { @@ -39,6 +44,10 @@ position: absolute; color: #fff; + &.mappable--tooltip__dark { + color: #272729; + } + svg { filter: drop-shadow(0px 4px 24px rgba(95, 105, 131, 0.04)) drop-shadow(0px 4px 12px rgba(95, 105, 131, 0.1)); } diff --git a/src/markers/MMapTooltipMarker/index.ts b/src/markers/MMapTooltipMarker/index.ts index ccf19cb..8efbd03 100644 --- a/src/markers/MMapTooltipMarker/index.ts +++ b/src/markers/MMapTooltipMarker/index.ts @@ -30,6 +30,8 @@ export class MMapTooltipMarker extends mappable.MMapComplexEntity void; + protected __implGetDefaultProps(): DefaultProps { return MMapTooltipMarker.defaultProps; } @@ -54,6 +56,10 @@ export class MMapTooltipMarker extends mappable.MMapComplexEntity this._updateTheme(), { + immediate: true + }); } protected _onUpdate(propsDiff: Partial): void { @@ -71,6 +77,18 @@ export class MMapTooltipMarker extends mappable.MMapComplexEntity Date: Mon, 15 Apr 2024 18:16:13 +0300 Subject: [PATCH 32/48] separated tooltip and balloon --- example/default-tooltip/react/index.tsx | 2 +- example/default-tooltip/vanilla/index.ts | 2 +- example/default-tooltip/vue/index.ts | 2 +- .../index.css | 60 ++++---- src/markers/MMapBalloonMarker/index.ts | 136 ++++++++++++++++++ .../tail.svg | 0 src/markers/MMapBalloonMarker/vue/index.ts | 30 ++++ src/markers/MMapTooltipMarker/index.ts | 134 ++--------------- src/markers/MMapTooltipMarker/vue/index.ts | 6 +- src/markers/index.ts | 1 + 10 files changed, 211 insertions(+), 162 deletions(-) rename src/markers/{MMapTooltipMarker => MMapBalloonMarker}/index.css (69%) create mode 100644 src/markers/MMapBalloonMarker/index.ts rename src/markers/{MMapTooltipMarker => MMapBalloonMarker}/tail.svg (100%) create mode 100644 src/markers/MMapBalloonMarker/vue/index.ts diff --git a/example/default-tooltip/react/index.tsx b/example/default-tooltip/react/index.tsx index 6a9a432..1f661b1 100644 --- a/example/default-tooltip/react/index.tsx +++ b/example/default-tooltip/react/index.tsx @@ -49,7 +49,7 @@ async function main() { - + ); } diff --git a/example/default-tooltip/vanilla/index.ts b/example/default-tooltip/vanilla/index.ts index f882d20..9f54205 100644 --- a/example/default-tooltip/vanilla/index.ts +++ b/example/default-tooltip/vanilla/index.ts @@ -26,6 +26,6 @@ async function main() { ]) ); - const tooltip = new MMapTooltipMarker({coordinates: CENTER, draggable: true, text: TOOLTIP_TEXT}); + const tooltip = new MMapTooltipMarker({coordinates: CENTER, draggable: true, content: TOOLTIP_TEXT}); map.addChild(tooltip); } diff --git a/example/default-tooltip/vue/index.ts b/example/default-tooltip/vue/index.ts index a2b9d1d..7f9ddf5 100644 --- a/example/default-tooltip/vue/index.ts +++ b/example/default-tooltip/vue/index.ts @@ -67,7 +67,7 @@ async function main() { - + ` }); app.mount('#app'); diff --git a/src/markers/MMapTooltipMarker/index.css b/src/markers/MMapBalloonMarker/index.css similarity index 69% rename from src/markers/MMapTooltipMarker/index.css rename to src/markers/MMapBalloonMarker/index.css index 3c56af6..e065b93 100644 --- a/src/markers/MMapTooltipMarker/index.css +++ b/src/markers/MMapBalloonMarker/index.css @@ -1,19 +1,19 @@ -.mappable--tooltip-marker { +.mappable--balloon-marker { --tail-height: 12px; --tail-width: 16px; --border-radius: 12px; --tail-height-and-offset: calc(var(--tail-height) + var(--offset)); - --tooltip-tail-transform-top: translate(-50%, calc(-100% - var(--offset))) rotate(180deg); - --tooltip-tail-transform-bottom: translate(-50%, var(--offset)); + --balloon-tail-transform-top: translate(-50%, calc(-100% - var(--offset))) rotate(180deg); + --balloon-tail-transform-bottom: translate(-50%, var(--offset)); } -.mappable--tooltip-marker svg { +.mappable--balloon-marker svg { display: block; } -.mappable--tooltip-marker_container { +.mappable--balloon-marker_container { width: max-content; max-width: 500px; max-height: 600px; @@ -33,18 +33,18 @@ overflow: hidden; text-overflow: ellipsis; - &.mappable--tooltip__dark { + &.mappable--balloon__dark { background-color: #272729; color: #c8c9cc; } } -.mappable--tooltip-marker_tail { +.mappable--balloon-marker_tail { display: block; position: absolute; color: #fff; - &.mappable--tooltip__dark { + &.mappable--balloon__dark { color: #272729; } @@ -55,85 +55,85 @@ /* positions */ .position-top { - .mappable--tooltip-marker_container { + .mappable--balloon-marker_container { transform: translate(-50%, calc(-100% - var(--tail-height-and-offset))); } - .mappable--tooltip-marker_tail { - transform: var(--tooltip-tail-transform-top); + .mappable--balloon-marker_tail { + transform: var(--balloon-tail-transform-top); } /* top left */ &.position-left { - .mappable--tooltip-marker_container { + .mappable--balloon-marker_container { transform: translate( calc(-100% + var(--border-radius) + var(--tail-width) / 2), calc(-100% - var(--tail-height-and-offset)) ); } - .mappable--tooltip-marker_tail { - transform: var(--tooltip-tail-transform-top); + .mappable--balloon-marker_tail { + transform: var(--balloon-tail-transform-top); } } /* top right */ &.position-right { - .mappable--tooltip-marker_container { + .mappable--balloon-marker_container { transform: translate( calc(-1 * var(--border-radius) - var(--tail-width) / 2), calc(-100% - var(--tail-height-and-offset)) ); } - .mappable--tooltip-marker_tail { - transform: var(--tooltip-tail-transform-top); + .mappable--balloon-marker_tail { + transform: var(--balloon-tail-transform-top); } } } .position-bottom { - .mappable--tooltip-marker_container { + .mappable--balloon-marker_container { transform: translate(-50%, var(--tail-height-and-offset)); } - .mappable--tooltip-marker_tail { - transform: var(--tooltip-tail-transform-bottom); + .mappable--balloon-marker_tail { + transform: var(--balloon-tail-transform-bottom); } /* bottom left */ &.position-left { - .mappable--tooltip-marker_container { + .mappable--balloon-marker_container { transform: translate( calc(-100% + var(--border-radius) + var(--tail-width) / 2), var(--tail-height-and-offset) ); } - .mappable--tooltip-marker_tail { - transform: var(--tooltip-tail-transform-bottom); + .mappable--balloon-marker_tail { + transform: var(--balloon-tail-transform-bottom); } } /* bottom right */ &.position-right { - .mappable--tooltip-marker_container { + .mappable--balloon-marker_container { transform: translate( calc(-1 * var(--border-radius) - var(--tail-width) / 2), var(--tail-height-and-offset) ); } - .mappable--tooltip-marker_tail { - transform: var(--tooltip-tail-transform-bottom); + .mappable--balloon-marker_tail { + transform: var(--balloon-tail-transform-bottom); } } } .position-left { - .mappable--tooltip-marker_container { + .mappable--balloon-marker_container { transform: translate(calc(-100% - var(--tail-height-and-offset)), -50%); } - .mappable--tooltip-marker_tail { + .mappable--balloon-marker_tail { transform: translate(calc(-100% - var(--offset)), -50%) rotate(90deg); } } .position-right { - .mappable--tooltip-marker_container { + .mappable--balloon-marker_container { transform: translate(var(--tail-height-and-offset), -50%); } - .mappable--tooltip-marker_tail { + .mappable--balloon-marker_tail { transform: translate(var(--offset), -50%) rotate(-90deg); } } diff --git a/src/markers/MMapBalloonMarker/index.ts b/src/markers/MMapBalloonMarker/index.ts new file mode 100644 index 0000000..dab1861 --- /dev/null +++ b/src/markers/MMapBalloonMarker/index.ts @@ -0,0 +1,136 @@ +import {MMapMarker, MMapMarkerProps} from '@mappable-world/mappable-types'; +import {MMapBalloonMarkerVuefyOptions} from './vue'; + +import './index.css'; +import tailSVG from './tail.svg'; + +type VerticalPosition = 'top' | 'bottom'; +type HorizontalPosition = 'left' | 'right'; +export type MMapBalloonPositionProps = + | VerticalPosition + | HorizontalPosition + | `${VerticalPosition} ${HorizontalPosition}` + | `${HorizontalPosition} ${VerticalPosition}`; + +export type MMapBalloonMarkerProps = MMapMarkerProps & { + // TODO: content props as string or function + content: string; + position?: MMapBalloonPositionProps; + offset?: number; +}; + +const defaultProps = Object.freeze({position: 'top', offset: 0}); +type DefaultProps = typeof defaultProps; + +export class MMapBalloonMarker extends mappable.MMapComplexEntity { + static defaultProps = defaultProps; + static [mappable.optionsKeyVuefy] = MMapBalloonMarkerVuefyOptions; + + private _markerElement: HTMLElement; + private _balloonContainer: HTMLElement; + private _balloonTail: HTMLElement; + private _marker: MMapMarker; + + private _unwatchThemeContext?: () => void; + + protected __implGetDefaultProps(): DefaultProps { + return MMapBalloonMarker.defaultProps; + } + + protected _onAttach(): void { + this._markerElement = document.createElement('mappable'); + this._markerElement.classList.add('mappable--balloon-marker'); + + this._balloonContainer = document.createElement('mappable'); + this._balloonContainer.classList.add('mappable--balloon-marker_container'); + this._balloonContainer.textContent = this._props.content; + + this._balloonTail = document.createElement('mappable'); + this._balloonTail.classList.add('mappable--balloon-marker_tail'); + this._balloonTail.innerHTML = tailSVG; + + this._updatePosition(); + this._updateOffset(); + + this._markerElement.appendChild(this._balloonContainer); + this._markerElement.appendChild(this._balloonTail); + + this._marker = new mappable.MMapMarker(this._props, this._markerElement); + this.addChild(this._marker); + + this._unwatchThemeContext = this._watchContext(mappable.ThemeContext, () => this._updateTheme(), { + immediate: true + }); + } + + protected _onUpdate(propsDiff: Partial): void { + if (propsDiff.position !== undefined) { + this._updatePosition(); + } + if (propsDiff.offset !== undefined) { + this._updateOffset(); + } + + if (propsDiff.content !== undefined) { + this._balloonContainer.textContent = this._props.content; + } + + this._marker.update(this._props); + } + + protected _onDetach(): void { + this._unwatchThemeContext?.(); + this._unwatchThemeContext = undefined; + } + + private _updateTheme() { + const themeCtx = this._consumeContext(mappable.ThemeContext); + const {theme} = themeCtx; + this._balloonContainer.classList.toggle('mappable--balloon__dark', theme === 'dark'); + this._balloonTail.classList.toggle('mappable--balloon__dark', theme === 'dark'); + } + + private _updateOffset(): void { + this._markerElement.style.setProperty('--offset', `${this._props.offset}px`); + } + + private _updatePosition(): void { + const {position} = this._props; + let verticalPosition: VerticalPosition; + let horizontalPosition: HorizontalPosition; + + const positionTypeHash: Record = { + top: 'vertical', + left: 'horizontal', + bottom: 'vertical', + right: 'horizontal' + }; + + if (position === 'top' || position === 'bottom') { + verticalPosition = position; + } else if (position === 'left' || position === 'right') { + horizontalPosition = position; + } else { + const [first, second] = position.split(' ') as (HorizontalPosition | VerticalPosition)[]; + if (positionTypeHash[first] === 'vertical' && positionTypeHash[second] === 'horizontal') { + verticalPosition = first as VerticalPosition; + horizontalPosition = second as HorizontalPosition; + } else if (positionTypeHash[first] === 'horizontal' && positionTypeHash[second] === 'vertical') { + verticalPosition = second as VerticalPosition; + horizontalPosition = first as HorizontalPosition; + } + } + + // check top position + this._markerElement.classList.toggle('position-top', verticalPosition === 'top'); + + // check bottom position + this._markerElement.classList.toggle('position-bottom', verticalPosition === 'bottom'); + + // check left position + this._markerElement.classList.toggle('position-left', horizontalPosition === 'left'); + + // check right position + this._markerElement.classList.toggle('position-right', horizontalPosition === 'right'); + } +} diff --git a/src/markers/MMapTooltipMarker/tail.svg b/src/markers/MMapBalloonMarker/tail.svg similarity index 100% rename from src/markers/MMapTooltipMarker/tail.svg rename to src/markers/MMapBalloonMarker/tail.svg diff --git a/src/markers/MMapBalloonMarker/vue/index.ts b/src/markers/MMapBalloonMarker/vue/index.ts new file mode 100644 index 0000000..8913cd3 --- /dev/null +++ b/src/markers/MMapBalloonMarker/vue/index.ts @@ -0,0 +1,30 @@ +import {MMapFeatureProps, MMapMarkerEventHandler} from '@mappable-world/mappable-types'; +import {CustomVuefyOptions} from '@mappable-world/mappable-types/modules/vuefy'; +import type TVue from '@vue/runtime-core'; +import {MMapBalloonMarker, MMapBalloonPositionProps} from '../'; + +export const MMapBalloonMarkerVuefyOptions: CustomVuefyOptions = { + props: { + coordinates: {type: Object, required: true}, + source: String, + zIndex: {type: Number, default: 0}, + properties: Object, + id: String, + disableRoundCoordinates: {type: Boolean, default: undefined}, + hideOutsideViewport: {type: [Object, Boolean], default: false}, + draggable: {type: Boolean, default: false}, + mapFollowsOnDrag: {type: [Boolean, Object]}, + onDragStart: Function as TVue.PropType, + onDragEnd: Function as TVue.PropType, + onDragMove: Function as TVue.PropType, + blockEvents: {type: Boolean, default: undefined}, + blockBehaviors: {type: Boolean, default: undefined}, + onDoubleClick: Function as TVue.PropType, + onClick: Function as TVue.PropType, + onFastClick: Function as TVue.PropType, + // TODO: content props as string or function + content: {type: String, required: true}, + position: {type: String as TVue.PropType}, + offset: {type: Number, default: 0} + } +}; diff --git a/src/markers/MMapTooltipMarker/index.ts b/src/markers/MMapTooltipMarker/index.ts index 8efbd03..e13d144 100644 --- a/src/markers/MMapTooltipMarker/index.ts +++ b/src/markers/MMapTooltipMarker/index.ts @@ -1,135 +1,17 @@ -import {MMapMarker, MMapMarkerProps} from '@mappable-world/mappable-types'; +import {MMapBalloonMarker, MMapBalloonMarkerProps} from '../MMapBalloonMarker'; import {MMapTooltipMarkerVuefyOptions} from './vue'; -import './index.css'; -import tailSVG from './tail.svg'; - -type VerticalPosition = 'top' | 'bottom'; -type HorizontalPosition = 'left' | 'right'; -export type MMapTooltipPositionProps = - | VerticalPosition - | HorizontalPosition - | `${VerticalPosition} ${HorizontalPosition}` - | `${HorizontalPosition} ${VerticalPosition}`; - -export type MMapTooltipMarkerProps = MMapMarkerProps & { - text: string; - position?: MMapTooltipPositionProps; - offset?: number; -}; - -const defaultProps = Object.freeze({position: 'top', offset: 0}); -type DefaultProps = typeof defaultProps; - -export class MMapTooltipMarker extends mappable.MMapComplexEntity { - static defaultProps = defaultProps; +export type MMapTooltipMarkerProps = Omit & {content: string}; +export class MMapTooltipMarker extends mappable.MMapComplexEntity { static [mappable.optionsKeyVuefy] = MMapTooltipMarkerVuefyOptions; - - private _markerElement: HTMLElement; - private _tooltipContainer: HTMLElement; - private _tooltipTail: HTMLElement; - private _marker: MMapMarker; - - private _unwatchThemeContext?: () => void; - - protected __implGetDefaultProps(): DefaultProps { - return MMapTooltipMarker.defaultProps; - } + private _balloon: MMapBalloonMarker; protected _onAttach(): void { - this._markerElement = document.createElement('mappable'); - this._markerElement.classList.add('mappable--tooltip-marker'); - - this._tooltipContainer = document.createElement('mappable'); - this._tooltipContainer.classList.add('mappable--tooltip-marker_container'); - this._tooltipContainer.textContent = this._props.text; - - this._tooltipTail = document.createElement('mappable'); - this._tooltipTail.classList.add('mappable--tooltip-marker_tail'); - this._tooltipTail.innerHTML = tailSVG; - - this._updatePosition(); - this._updateOffset(); - - this._markerElement.appendChild(this._tooltipContainer); - this._markerElement.appendChild(this._tooltipTail); - - this._marker = new mappable.MMapMarker(this._props, this._markerElement); - this.addChild(this._marker); - - this._unwatchThemeContext = this._watchContext(mappable.ThemeContext, () => this._updateTheme(), { - immediate: true - }); - } - - protected _onUpdate(propsDiff: Partial): void { - if (propsDiff.position !== undefined) { - this._updatePosition(); - } - if (propsDiff.offset !== undefined) { - this._updateOffset(); - } - - if (propsDiff.text !== undefined) { - this._tooltipContainer.textContent = this._props.text; - } - - this._marker.update(this._props); - } - - protected _onDetach(): void { - this._unwatchThemeContext?.(); - this._unwatchThemeContext = undefined; - } - - private _updateTheme() { - const themeCtx = this._consumeContext(mappable.ThemeContext); - const {theme} = themeCtx; - this._tooltipContainer.classList.toggle('mappable--tooltip__dark', theme === 'dark'); - this._tooltipTail.classList.toggle('mappable--tooltip__dark', theme === 'dark'); + this._balloon = new MMapBalloonMarker(this._props); + this.addChild(this._balloon); } - private _updateOffset(): void { - this._markerElement.style.setProperty('--offset', `${this._props.offset}px`); - } - - private _updatePosition(): void { - const {position} = this._props; - let verticalPosition: VerticalPosition; - let horizontalPosition: HorizontalPosition; - - const positionTypeHash: Record = { - top: 'vertical', - left: 'horizontal', - bottom: 'vertical', - right: 'horizontal' - }; - - if (position === 'top' || position === 'bottom') { - verticalPosition = position; - } else if (position === 'left' || position === 'right') { - horizontalPosition = position; - } else { - const [first, second] = position.split(' ') as (HorizontalPosition | VerticalPosition)[]; - if (positionTypeHash[first] === 'vertical' && positionTypeHash[second] === 'horizontal') { - verticalPosition = first as VerticalPosition; - horizontalPosition = second as HorizontalPosition; - } else if (positionTypeHash[first] === 'horizontal' && positionTypeHash[second] === 'vertical') { - verticalPosition = second as VerticalPosition; - horizontalPosition = first as HorizontalPosition; - } - } - - // check top position - this._markerElement.classList.toggle('position-top', verticalPosition === 'top'); - - // check bottom position - this._markerElement.classList.toggle('position-bottom', verticalPosition === 'bottom'); - - // check left position - this._markerElement.classList.toggle('position-left', horizontalPosition === 'left'); - - // check right position - this._markerElement.classList.toggle('position-right', horizontalPosition === 'right'); + protected _onUpdate(): void { + this._balloon.update(this._props); } } diff --git a/src/markers/MMapTooltipMarker/vue/index.ts b/src/markers/MMapTooltipMarker/vue/index.ts index 90ca9fe..0905913 100644 --- a/src/markers/MMapTooltipMarker/vue/index.ts +++ b/src/markers/MMapTooltipMarker/vue/index.ts @@ -1,7 +1,7 @@ import {MMapFeatureProps, MMapMarkerEventHandler} from '@mappable-world/mappable-types'; import {CustomVuefyOptions} from '@mappable-world/mappable-types/modules/vuefy'; import type TVue from '@vue/runtime-core'; -import {MMapTooltipMarker, MMapTooltipPositionProps} from '../'; +import {MMapTooltipMarker, MMapTooltipMarkerProps} from '../'; export const MMapTooltipMarkerVuefyOptions: CustomVuefyOptions = { props: { @@ -22,8 +22,8 @@ export const MMapTooltipMarkerVuefyOptions: CustomVuefyOptions, onClick: Function as TVue.PropType, onFastClick: Function as TVue.PropType, - text: {type: String, required: true}, - position: {type: String as TVue.PropType}, + content: {type: String, required: true}, + position: {type: String as TVue.PropType}, offset: {type: Number, default: 0} } }; diff --git a/src/markers/index.ts b/src/markers/index.ts index 3334694..dfba150 100644 --- a/src/markers/index.ts +++ b/src/markers/index.ts @@ -1,3 +1,4 @@ export * from './MMapDefaultMarker'; export {MMapPopupMarker, MMapPopupMarkerProps} from './MMapPopupMarker'; +export {MMapBalloonMarker, MMapBalloonMarkerProps, MMapBalloonPositionProps} from './MMapBalloonMarker'; export {MMapTooltipMarker, MMapTooltipMarkerProps} from './MMapTooltipMarker'; From d254b2558a8935ba17043e4c216a2f6163338b53 Mon Sep 17 00:00:00 2001 From: Matthew Kalinin Date: Wed, 17 Apr 2024 16:33:52 +0300 Subject: [PATCH 33/48] add default popup --- src/markers/MMapBalloonMarker/index.css | 4 + src/markers/MMapBalloonMarker/index.ts | 77 ++++++++++-- src/markers/MMapBalloonMarker/vue/index.ts | 10 +- src/markers/MMapDefaultPopupMarker/close.svg | 1 + src/markers/MMapDefaultPopupMarker/index.css | 77 ++++++++++++ src/markers/MMapDefaultPopupMarker/index.ts | 119 +++++++++++++++++++ src/markers/MMapTooltipMarker/index.css | 7 ++ src/markers/MMapTooltipMarker/index.ts | 39 +++++- src/markers/index.ts | 3 +- 9 files changed, 316 insertions(+), 21 deletions(-) create mode 100644 src/markers/MMapDefaultPopupMarker/close.svg create mode 100644 src/markers/MMapDefaultPopupMarker/index.css create mode 100644 src/markers/MMapDefaultPopupMarker/index.ts create mode 100644 src/markers/MMapTooltipMarker/index.css diff --git a/src/markers/MMapBalloonMarker/index.css b/src/markers/MMapBalloonMarker/index.css index e065b93..6a52089 100644 --- a/src/markers/MMapBalloonMarker/index.css +++ b/src/markers/MMapBalloonMarker/index.css @@ -7,6 +7,10 @@ --balloon-tail-transform-top: translate(-50%, calc(-100% - var(--offset))) rotate(180deg); --balloon-tail-transform-bottom: translate(-50%, var(--offset)); + + &.hide { + display: none; + } } .mappable--balloon-marker svg { diff --git a/src/markers/MMapBalloonMarker/index.ts b/src/markers/MMapBalloonMarker/index.ts index dab1861..ac4c96e 100644 --- a/src/markers/MMapBalloonMarker/index.ts +++ b/src/markers/MMapBalloonMarker/index.ts @@ -12,26 +12,74 @@ export type MMapBalloonPositionProps = | `${VerticalPosition} ${HorizontalPosition}` | `${HorizontalPosition} ${VerticalPosition}`; +export type MMapBalloonContentProps = (close: () => void) => HTMLElement; + export type MMapBalloonMarkerProps = MMapMarkerProps & { - // TODO: content props as string or function - content: string; + /** + * The function of creating balloon content + * @param close - A function for hiding balloon content. The `MMapBalloonMarker` is not deleted. + * @returns Balloon content as `HTMLElement` + */ + content: MMapBalloonContentProps; + /** The position of the balloon in relation to the point it is pointing to */ position?: MMapBalloonPositionProps; + /** The offset in pixels between the balloon pointer and the point it is pointing to. */ offset?: number; + /** Hide or show balloon on map */ + show?: boolean; + /** Balloon closing callback */ + onClose?: () => void; + /** Balloon opening callback */ + onOpen?: () => void; }; -const defaultProps = Object.freeze({position: 'top', offset: 0}); +const defaultProps = Object.freeze({position: 'top', offset: 0, show: true}); type DefaultProps = typeof defaultProps; +/** + * `MMapBalloonMarker` is a balloon (popup) with customized content. + * @example + * ```js + * const balloon = new MMapBalloonMarker({ + * content: (close) => createPopupContentHTMLElement(close), + * position: 'top', + * onOpen:() => console.log('open'), + * onClose:() => console.log('close'), + * // support MMapMarker props + * coordinates: BALLOON_COORD, + * draggable: true, + * }); + * map.addChild(balloon); + * ``` + */ export class MMapBalloonMarker extends mappable.MMapComplexEntity { static defaultProps = defaultProps; static [mappable.optionsKeyVuefy] = MMapBalloonMarkerVuefyOptions; + public get isOpen() { + return this._props.show; + } private _markerElement: HTMLElement; private _balloonContainer: HTMLElement; private _balloonTail: HTMLElement; private _marker: MMapMarker; - private _unwatchThemeContext?: () => void; + private _togglePopup(forceShowBalloon?: boolean): void { + let openBalloon = !this._props.show; + if (forceShowBalloon !== undefined) { + openBalloon = forceShowBalloon; + } + + this._markerElement.classList.toggle('hide', !openBalloon); + + if (openBalloon) { + this._props.onOpen?.(); + } else { + this._props.onClose?.(); + } + + this._props.show = openBalloon; + } protected __implGetDefaultProps(): DefaultProps { return MMapBalloonMarker.defaultProps; @@ -43,12 +91,13 @@ export class MMapBalloonMarker extends mappable.MMapComplexEntity this._updateTheme(), { + this._watchContext(mappable.ThemeContext, () => this._updateTheme(), { immediate: true }); } @@ -72,15 +121,15 @@ export class MMapBalloonMarker extends mappable.MMapComplexEntity { + this._togglePopup(false); + }; } diff --git a/src/markers/MMapBalloonMarker/vue/index.ts b/src/markers/MMapBalloonMarker/vue/index.ts index 8913cd3..97de176 100644 --- a/src/markers/MMapBalloonMarker/vue/index.ts +++ b/src/markers/MMapBalloonMarker/vue/index.ts @@ -1,7 +1,7 @@ import {MMapFeatureProps, MMapMarkerEventHandler} from '@mappable-world/mappable-types'; import {CustomVuefyOptions} from '@mappable-world/mappable-types/modules/vuefy'; import type TVue from '@vue/runtime-core'; -import {MMapBalloonMarker, MMapBalloonPositionProps} from '../'; +import {MMapBalloonContentProps, MMapBalloonMarker, MMapBalloonMarkerProps, MMapBalloonPositionProps} from '../'; export const MMapBalloonMarkerVuefyOptions: CustomVuefyOptions = { props: { @@ -22,9 +22,11 @@ export const MMapBalloonMarkerVuefyOptions: CustomVuefyOptions, onClick: Function as TVue.PropType, onFastClick: Function as TVue.PropType, - // TODO: content props as string or function - content: {type: String, required: true}, + content: {type: Function as TVue.PropType, required: true}, position: {type: String as TVue.PropType}, - offset: {type: Number, default: 0} + offset: {type: Number, default: 0}, + show: {type: Boolean, default: true}, + onClose: {type: Function as TVue.PropType}, + onOpen: {type: Function as TVue.PropType} } }; diff --git a/src/markers/MMapDefaultPopupMarker/close.svg b/src/markers/MMapDefaultPopupMarker/close.svg new file mode 100644 index 0000000..5c90cf8 --- /dev/null +++ b/src/markers/MMapDefaultPopupMarker/close.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/markers/MMapDefaultPopupMarker/index.css b/src/markers/MMapDefaultPopupMarker/index.css new file mode 100644 index 0000000..a777209 --- /dev/null +++ b/src/markers/MMapDefaultPopupMarker/index.css @@ -0,0 +1,77 @@ +.mappable--default-popup { + display: flex; + flex-direction: column; + row-gap: 8px; +} + +.mappable--default-popup_header { + display: flex; + flex-direction: row; + justify-content: space-between; + gap: 8px; + + .mappable--default-popup_header_title { + font-size: 16px; + font-weight: 400; + line-height: 22px; + color: #050d33; + } + .mappable--default-popup_header_close { + position: absolute; + top: 0; + right: 0; + display: flex; + justify-content: center; + align-items: center; + width: 32px; + height: 32px; + border: none; + background: none; + color: #c8c9cc; + cursor: pointer; + } +} + +.mappable--default-popup_description { + font-size: 14px; + font-weight: 400; + line-height: 20px; + color: #7b7d85; +} + +.mappable--default-popup_action { + width: max-content; + padding: 12px 16px; + border-radius: 8px; + border: none; + background-color: #eefd7d; + transition: background-color 0.1s ease-out; + color: #050d33; + text-align: center; + cursor: pointer; + white-space: normal; + + font-size: 14px; + font-style: normal; + font-weight: 500; + line-height: 16px; +} + +.mappable--default-popup_action:hover { + background-color: #e5fd30; +} + +.mappable--default-popup__dark { + .header_title { + color: #f2f5fa; + } + .header_close { + color: #46464d; + } + .mappable--default-popup_description { + color: #7b7d85; + } + .mappable--default-popup_action { + background-color: #d6fd63; + } +} diff --git a/src/markers/MMapDefaultPopupMarker/index.ts b/src/markers/MMapDefaultPopupMarker/index.ts new file mode 100644 index 0000000..a183a66 --- /dev/null +++ b/src/markers/MMapDefaultPopupMarker/index.ts @@ -0,0 +1,119 @@ +import {MMapBalloonMarker, MMapBalloonMarkerProps, MMapBalloonContentProps} from '../MMapBalloonMarker'; +import './index.css'; +import closeSVG from './close.svg'; + +export type MMapDefaultPopupMarkerProps = Omit & { + /** Displayed title in popup header */ + title?: string; + /** Displayed description */ + description?: string; + /** The inscription on the action button */ + action?: string; + /** Callback of click the action button */ + onAction?: () => void; +}; +/** + * `MMapDefaultPopupMarker` is a default popup that contains a title, a description, and an action button. + * @example + * ```js + * const defaultPopup = new MMapDefaultPopupMarker({ + * title: 'Default title', + * description: 'Default description', + * action: 'Make action', + * // support MMapMarker props + * coordinates: POPUP_COORD, + * draggable: true, + * // support MMapBalloonMarker props + * position: 'top', + * }); + * map.addChild(defaultPopup); + * ``` + */ +export class MMapDefaultPopupMarker extends mappable.MMapComplexEntity { + private _balloon: MMapBalloonMarker; + private _element: HTMLElement; + + public get isOpen() { + return this._balloon.isOpen; + } + + protected _onAttach(): void { + const {title, description, action} = this._props; + + if (title === undefined && description === undefined && action === undefined) { + throw new Error( + 'There is no content to display. Specify one of the parameters: title, description, action' + ); + } + + this._balloon = new MMapBalloonMarker({ + ...this._props, + content: this.__createDefaultPopup, + onClose: () => (this._props.show = false), + onOpen: () => (this._props.show = true) + }); + this.addChild(this._balloon); + + this._watchContext(mappable.ThemeContext, () => this._updateTheme(), {immediate: true}); + } + + protected _onUpdate(propsDiff: Partial, oldProps: MMapDefaultPopupMarkerProps): void { + const {title, description, action} = this._props; + + const isTitleChange = oldProps.title !== title; + const isDescriptionChange = oldProps.description !== description; + const isActionChange = oldProps.action !== action; + + if (isTitleChange || isDescriptionChange || isActionChange) { + this._balloon.update({content: (close) => this.__createDefaultPopup(close)}); + } + + this._balloon.update(this._props); + } + + private _updateTheme() { + const themeCtx = this._consumeContext(mappable.ThemeContext); + this._element.classList.toggle('mappable--default-popup__dark', themeCtx.theme === 'dark'); + } + + private __createDefaultPopup: MMapBalloonContentProps = (close) => { + const {title, description, action} = this._props; + this._element = document.createElement('mappable'); + this._element.classList.add('mappable--default-popup'); + + const popupHeaderElement = document.createElement('mappable'); + popupHeaderElement.classList.add('mappable--default-popup_header'); + this._element.appendChild(popupHeaderElement); + + if (title) { + const titleElement = document.createElement('mappable'); + titleElement.classList.add('mappable--default-popup_header_title'); + titleElement.textContent = title; + popupHeaderElement.appendChild(titleElement); + } + + const closeButton = document.createElement('button'); + closeButton.classList.add('mappable--default-popup_header_close'); + closeButton.innerHTML = closeSVG; + closeButton.addEventListener('click', () => close()); + popupHeaderElement.appendChild(closeButton); + + if (description) { + const descriptionElement = document.createElement('mappable'); + descriptionElement.classList.add('mappable--default-popup_description'); + descriptionElement.textContent = description; + this._element.appendChild(descriptionElement); + } + if (action) { + const actionButton = document.createElement('button'); + actionButton.classList.add('mappable--default-popup_action'); + actionButton.textContent = action; + actionButton.addEventListener('click', () => { + this._props.onAction?.(); + }); + this._element.appendChild(actionButton); + } + + return this._element; + }; +} diff --git a/src/markers/MMapTooltipMarker/index.css b/src/markers/MMapTooltipMarker/index.css new file mode 100644 index 0000000..5b33e80 --- /dev/null +++ b/src/markers/MMapTooltipMarker/index.css @@ -0,0 +1,7 @@ +.mappable--default-tooltip { + display: block; + max-width: inherit; + max-height: inherit; + overflow: hidden; + text-overflow: ellipsis; +} diff --git a/src/markers/MMapTooltipMarker/index.ts b/src/markers/MMapTooltipMarker/index.ts index e13d144..d79a585 100644 --- a/src/markers/MMapTooltipMarker/index.ts +++ b/src/markers/MMapTooltipMarker/index.ts @@ -1,17 +1,48 @@ import {MMapBalloonMarker, MMapBalloonMarkerProps} from '../MMapBalloonMarker'; import {MMapTooltipMarkerVuefyOptions} from './vue'; +import './index.css'; -export type MMapTooltipMarkerProps = Omit & {content: string}; +export type MMapTooltipMarkerProps = Omit & { + /** The text content that the tooltip will display */ + content: string; +}; +/** + * `MMapTooltipMarker` is a default tooltip - a text popup with no ability to close. + * @example + * ```js + * const tooltip = new MMapTooltipMarker({ + * content:'Default tooltip', + * // support MMapMarker props + * coordinates: TOOLTIP_COORD, + * draggable: true, + * // support MMapBalloonMarker props + * position: 'top', + * }); + * map.addChild(tooltip); + * ``` + */ export class MMapTooltipMarker extends mappable.MMapComplexEntity { static [mappable.optionsKeyVuefy] = MMapTooltipMarkerVuefyOptions; private _balloon: MMapBalloonMarker; + private _element: HTMLElement; protected _onAttach(): void { - this._balloon = new MMapBalloonMarker(this._props); + this._balloon = new MMapBalloonMarker({...this._props, content: this.__createTooltip}); this.addChild(this._balloon); } - protected _onUpdate(): void { - this._balloon.update(this._props); + protected _onUpdate(propsDiff: Partial): void { + if (propsDiff.content !== undefined) { + this._element.textContent = this._props.content; + } + const {content, ...propsWithoutContent} = this._props; + this._balloon.update({...propsWithoutContent}); } + + private __createTooltip = (): HTMLElement => { + this._element = document.createElement('mappable'); + this._element.classList.add('mappable--default-tooltip'); + this._element.textContent = this._props.content; + return this._element; + }; } diff --git a/src/markers/index.ts b/src/markers/index.ts index dfba150..f091abc 100644 --- a/src/markers/index.ts +++ b/src/markers/index.ts @@ -1,4 +1,5 @@ +export {MMapBalloonMarker, MMapBalloonMarkerProps, MMapBalloonPositionProps} from './MMapBalloonMarker'; export * from './MMapDefaultMarker'; +export {MMapDefaultPopupMarker, MMapDefaultPopupMarkerProps} from './MMapDefaultPopupMarker'; export {MMapPopupMarker, MMapPopupMarkerProps} from './MMapPopupMarker'; -export {MMapBalloonMarker, MMapBalloonMarkerProps, MMapBalloonPositionProps} from './MMapBalloonMarker'; export {MMapTooltipMarker, MMapTooltipMarkerProps} from './MMapTooltipMarker'; From cd1d6e9d07000b03c0a88cf382e655b5aa8930b3 Mon Sep 17 00:00:00 2001 From: Matthew Kalinin Date: Wed, 17 Apr 2024 16:49:51 +0300 Subject: [PATCH 34/48] remove close callback from popup content --- src/markers/MMapBalloonMarker/index.ts | 10 +++------- src/markers/MMapDefaultPopupMarker/index.ts | 6 +++--- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/src/markers/MMapBalloonMarker/index.ts b/src/markers/MMapBalloonMarker/index.ts index ac4c96e..15438da 100644 --- a/src/markers/MMapBalloonMarker/index.ts +++ b/src/markers/MMapBalloonMarker/index.ts @@ -12,7 +12,7 @@ export type MMapBalloonPositionProps = | `${VerticalPosition} ${HorizontalPosition}` | `${HorizontalPosition} ${VerticalPosition}`; -export type MMapBalloonContentProps = (close: () => void) => HTMLElement; +export type MMapBalloonContentProps = () => HTMLElement; export type MMapBalloonMarkerProps = MMapMarkerProps & { /** @@ -91,7 +91,7 @@ export class MMapBalloonMarker extends mappable.MMapComplexEntity { - this._togglePopup(false); - }; } diff --git a/src/markers/MMapDefaultPopupMarker/index.ts b/src/markers/MMapDefaultPopupMarker/index.ts index a183a66..878bb1e 100644 --- a/src/markers/MMapDefaultPopupMarker/index.ts +++ b/src/markers/MMapDefaultPopupMarker/index.ts @@ -65,7 +65,7 @@ export class MMapDefaultPopupMarker extends mappable.MMapComplexEntity this.__createDefaultPopup(close)}); + this._balloon.update({content: () => this.__createDefaultPopup()}); } this._balloon.update(this._props); @@ -76,7 +76,7 @@ export class MMapDefaultPopupMarker extends mappable.MMapComplexEntity { + private __createDefaultPopup: MMapBalloonContentProps = () => { const {title, description, action} = this._props; this._element = document.createElement('mappable'); this._element.classList.add('mappable--default-popup'); @@ -95,7 +95,7 @@ export class MMapDefaultPopupMarker extends mappable.MMapComplexEntity close()); + closeButton.addEventListener('click', () => this._balloon.update({show: false})); popupHeaderElement.appendChild(closeButton); if (description) { From 878dabea268cb4b486931769b024177661c0ceb0 Mon Sep 17 00:00:00 2001 From: Matthew Kalinin Date: Wed, 17 Apr 2024 17:00:02 +0300 Subject: [PATCH 35/48] remove mock MMapPopupMarker --- src/markers/MMapPopupMarker/index.ts | 3 --- src/markers/index.ts | 1 - 2 files changed, 4 deletions(-) delete mode 100644 src/markers/MMapPopupMarker/index.ts diff --git a/src/markers/MMapPopupMarker/index.ts b/src/markers/MMapPopupMarker/index.ts deleted file mode 100644 index d51c1bc..0000000 --- a/src/markers/MMapPopupMarker/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -// TODO: add popup marker -export type MMapPopupMarkerProps = {}; -export class MMapPopupMarker extends mappable.MMapComplexEntity {} diff --git a/src/markers/index.ts b/src/markers/index.ts index f091abc..4043a62 100644 --- a/src/markers/index.ts +++ b/src/markers/index.ts @@ -1,5 +1,4 @@ export {MMapBalloonMarker, MMapBalloonMarkerProps, MMapBalloonPositionProps} from './MMapBalloonMarker'; export * from './MMapDefaultMarker'; export {MMapDefaultPopupMarker, MMapDefaultPopupMarkerProps} from './MMapDefaultPopupMarker'; -export {MMapPopupMarker, MMapPopupMarkerProps} from './MMapPopupMarker'; export {MMapTooltipMarker, MMapTooltipMarkerProps} from './MMapTooltipMarker'; From 60068f46522ca91d458e54e63b8e71c8bd35ac98 Mon Sep 17 00:00:00 2001 From: Matthew Kalinin Date: Thu, 18 Apr 2024 14:12:07 +0300 Subject: [PATCH 36/48] add overrides --- src/markers/MMapBalloonMarker/index.ts | 11 +++-- src/markers/MMapBalloonMarker/react/index.tsx | 43 +++++++++++++++++++ src/markers/MMapBalloonMarker/vue/index.ts | 43 ++++++++++++++++++- src/markers/MMapDefaultPopupMarker/index.ts | 17 ++++++-- .../MMapDefaultPopupMarker/vue/index.ts | 35 +++++++++++++++ 5 files changed, 138 insertions(+), 11 deletions(-) create mode 100644 src/markers/MMapBalloonMarker/react/index.tsx create mode 100644 src/markers/MMapDefaultPopupMarker/vue/index.ts diff --git a/src/markers/MMapBalloonMarker/index.ts b/src/markers/MMapBalloonMarker/index.ts index 15438da..7e72c45 100644 --- a/src/markers/MMapBalloonMarker/index.ts +++ b/src/markers/MMapBalloonMarker/index.ts @@ -1,5 +1,6 @@ import {MMapMarker, MMapMarkerProps} from '@mappable-world/mappable-types'; -import {MMapBalloonMarkerVuefyOptions} from './vue'; +import {MMapBalloonMarkerReactifyOverride} from './react'; +import {MMapBalloonMarkerVuefyOptions, MMapBalloonMarkerVuefyOverride} from './vue'; import './index.css'; import tailSVG from './tail.svg'; @@ -15,11 +16,7 @@ export type MMapBalloonPositionProps = export type MMapBalloonContentProps = () => HTMLElement; export type MMapBalloonMarkerProps = MMapMarkerProps & { - /** - * The function of creating balloon content - * @param close - A function for hiding balloon content. The `MMapBalloonMarker` is not deleted. - * @returns Balloon content as `HTMLElement` - */ + /** The function of creating balloon content */ content: MMapBalloonContentProps; /** The position of the balloon in relation to the point it is pointing to */ position?: MMapBalloonPositionProps; @@ -54,6 +51,8 @@ type DefaultProps = typeof defaultProps; */ export class MMapBalloonMarker extends mappable.MMapComplexEntity { static defaultProps = defaultProps; + static [mappable.overrideKeyReactify] = MMapBalloonMarkerReactifyOverride; + static [mappable.overrideKeyVuefy] = MMapBalloonMarkerVuefyOverride; static [mappable.optionsKeyVuefy] = MMapBalloonMarkerVuefyOptions; public get isOpen() { diff --git a/src/markers/MMapBalloonMarker/react/index.tsx b/src/markers/MMapBalloonMarker/react/index.tsx new file mode 100644 index 0000000..2449acd --- /dev/null +++ b/src/markers/MMapBalloonMarker/react/index.tsx @@ -0,0 +1,43 @@ +import type TReact from 'react'; +import {CustomReactify, Prettify, OverrideProps} from '@mappable-world/mappable-types/reactify/reactify'; +import {MMapEntity} from '@mappable-world/mappable-types'; +import {MMapBalloonMarker as MMapBalloonMarkerI, MMapBalloonMarkerProps} from '../'; + +type MMapBalloonMarkerReactifiedProps = Prettify< + OverrideProps< + MMapBalloonMarkerProps, + { + /** The function of creating balloon content */ + content: () => TReact.ReactElement; + } + > +>; + +type MMapBalloonMarkerR = TReact.ForwardRefExoticComponent< + Prettify>> +>; + +export const MMapBalloonMarkerReactifyOverride: CustomReactify = ( + MMapBalloonMarkerI, + {reactify, React, ReactDOM} +) => { + const MMapBalloonMarkerReactified = reactify.entity(MMapBalloonMarkerI); + + const MMapBalloonMarker = React.forwardRef((props, ref) => { + const [balloonElement] = React.useState(document.createElement('mappable')); + const [content, setContent] = React.useState(); + + const balloon = React.useMemo(() => { + setContent(props.content()); + return () => balloonElement; + }, [props.content, balloonElement]); + + return ( + <> + + {ReactDOM.createPortal(content, balloonElement)} + + ); + }); + return MMapBalloonMarker; +}; diff --git a/src/markers/MMapBalloonMarker/vue/index.ts b/src/markers/MMapBalloonMarker/vue/index.ts index 97de176..f16d78e 100644 --- a/src/markers/MMapBalloonMarker/vue/index.ts +++ b/src/markers/MMapBalloonMarker/vue/index.ts @@ -1,5 +1,5 @@ import {MMapFeatureProps, MMapMarkerEventHandler} from '@mappable-world/mappable-types'; -import {CustomVuefyOptions} from '@mappable-world/mappable-types/modules/vuefy'; +import {CustomVuefyFn, CustomVuefyOptions} from '@mappable-world/mappable-types/modules/vuefy'; import type TVue from '@vue/runtime-core'; import {MMapBalloonContentProps, MMapBalloonMarker, MMapBalloonMarkerProps, MMapBalloonPositionProps} from '../'; @@ -30,3 +30,44 @@ export const MMapBalloonMarkerVuefyOptions: CustomVuefyOptions} } }; + +type MMapBalloonMarkerSlots = { + content: void; +}; + +export const MMapBalloonMarkerVuefyOverride: CustomVuefyFn = ( + MMapBalloonMarkerI, + props, + {vuefy, Vue} +) => { + const MMapBalloonMarkerV = vuefy.entity(MMapBalloonMarkerI); + const {content, ...overridedProps} = props; + return Vue.defineComponent({ + name: 'MMapBalloonMarker', + props: overridedProps, + slots: Object as TVue.SlotsType, + setup(props, {slots, expose}) { + const content: TVue.Ref = Vue.ref(null); + const popupHTMLElement = document.createElement('mappable'); + + const markerRef = Vue.ref<{entity: MMapBalloonMarker} | null>(null); + const markerEntity = Vue.computed(() => markerRef.value?.entity); + + const balloon = Vue.computed(() => { + content.value = slots.content?.(); + return () => popupHTMLElement; + }); + expose({entity: markerEntity}); + return () => + Vue.h( + MMapBalloonMarkerV, + { + ...props, + content: balloon.value, + ref: markerRef + }, + () => Vue.h(Vue.Teleport, {to: popupHTMLElement}, [content.value]) + ); + } + }); +}; diff --git a/src/markers/MMapDefaultPopupMarker/index.ts b/src/markers/MMapDefaultPopupMarker/index.ts index 878bb1e..04957de 100644 --- a/src/markers/MMapDefaultPopupMarker/index.ts +++ b/src/markers/MMapDefaultPopupMarker/index.ts @@ -1,6 +1,8 @@ -import {MMapBalloonMarker, MMapBalloonMarkerProps, MMapBalloonContentProps} from '../MMapBalloonMarker'; -import './index.css'; +import {MMapBalloonContentProps, MMapBalloonMarker, MMapBalloonMarkerProps} from '../MMapBalloonMarker'; +import {MMapDefaultPopupMarkerVuefyOptions} from './vue'; + import closeSVG from './close.svg'; +import './index.css'; export type MMapDefaultPopupMarkerProps = Omit & { /** Displayed title in popup header */ @@ -30,6 +32,7 @@ export type MMapDefaultPopupMarkerProps = Omit { + static [mappable.optionsKeyVuefy] = MMapDefaultPopupMarkerVuefyOptions; private _balloon: MMapBalloonMarker; private _element: HTMLElement; @@ -49,8 +52,14 @@ export class MMapDefaultPopupMarker extends mappable.MMapComplexEntity (this._props.show = false), - onOpen: () => (this._props.show = true) + onClose: () => { + this._props.show = false; + this._props.onClose?.(); + }, + onOpen: () => { + this._props.show = true; + this._props.onOpen?.(); + } }); this.addChild(this._balloon); diff --git a/src/markers/MMapDefaultPopupMarker/vue/index.ts b/src/markers/MMapDefaultPopupMarker/vue/index.ts new file mode 100644 index 0000000..feec40b --- /dev/null +++ b/src/markers/MMapDefaultPopupMarker/vue/index.ts @@ -0,0 +1,35 @@ +import {MMapFeatureProps, MMapMarkerEventHandler} from '@mappable-world/mappable-types'; +import {CustomVuefyOptions} from '@mappable-world/mappable-types/modules/vuefy'; +import type TVue from '@vue/runtime-core'; +import {MMapDefaultPopupMarker, MMapDefaultPopupMarkerProps} from '../'; + +export const MMapDefaultPopupMarkerVuefyOptions: CustomVuefyOptions = { + props: { + coordinates: {type: Object, required: true}, + source: String, + zIndex: {type: Number, default: 0}, + properties: Object, + id: String, + disableRoundCoordinates: {type: Boolean, default: undefined}, + hideOutsideViewport: {type: [Object, Boolean], default: false}, + draggable: {type: Boolean, default: false}, + mapFollowsOnDrag: {type: [Boolean, Object]}, + onDragStart: Function as TVue.PropType, + onDragEnd: Function as TVue.PropType, + onDragMove: Function as TVue.PropType, + blockEvents: {type: Boolean, default: undefined}, + blockBehaviors: {type: Boolean, default: undefined}, + onDoubleClick: Function as TVue.PropType, + onClick: Function as TVue.PropType, + onFastClick: Function as TVue.PropType, + position: {type: String as TVue.PropType}, + offset: {type: Number, default: 0}, + show: {type: Boolean, default: true}, + onClose: {type: Function as TVue.PropType}, + onOpen: {type: Function as TVue.PropType}, + title: {type: String}, + description: {type: String}, + action: {type: String}, + onAction: {type: Function as TVue.PropType} + } +}; From 38f69ad1ac0b28b0c6026ae89ec3b5677205a125 Mon Sep 17 00:00:00 2001 From: Matthew Kalinin Date: Thu, 18 Apr 2024 14:12:53 +0300 Subject: [PATCH 37/48] popups examples --- example/default-popups/common.css | 18 +++++ example/default-popups/common.ts | 7 ++ example/default-popups/react/index.html | 37 ++++++++++ example/default-popups/react/index.tsx | 70 +++++++++++++++++++ example/default-popups/vanilla/index.html | 35 ++++++++++ example/default-popups/vanilla/index.ts | 65 ++++++++++++++++++ example/default-popups/vue/index.html | 36 ++++++++++ example/default-popups/vue/index.ts | 84 +++++++++++++++++++++++ 8 files changed, 352 insertions(+) create mode 100644 example/default-popups/common.css create mode 100644 example/default-popups/common.ts create mode 100644 example/default-popups/react/index.html create mode 100644 example/default-popups/react/index.tsx create mode 100644 example/default-popups/vanilla/index.html create mode 100644 example/default-popups/vanilla/index.ts create mode 100644 example/default-popups/vue/index.html create mode 100644 example/default-popups/vue/index.ts diff --git a/example/default-popups/common.css b/example/default-popups/common.css new file mode 100644 index 0000000..c198bfb --- /dev/null +++ b/example/default-popups/common.css @@ -0,0 +1,18 @@ +.custom-popup { + display: grid; + max-width: inherit; + grid-template-columns: 120px 64px; + grid-template-rows: repeat(2, 1fr); + gap: 10px; + + & .title { + font-weight: bold; + font-size: 18px; + } + + & .content { + width: 100%; + font-size: 16px; + grid-column: 1 / 3; + } +} diff --git a/example/default-popups/common.ts b/example/default-popups/common.ts new file mode 100644 index 0000000..cd7487c --- /dev/null +++ b/example/default-popups/common.ts @@ -0,0 +1,7 @@ +import type {LngLat, MMapLocationRequest} from '@mappable-world/mappable-types'; + +const CENTER: LngLat = [55.442795, 25.24107]; +export const LOCATION: MMapLocationRequest = {center: CENTER, zoom: 14}; + +export const CUSTOM_POPUP_COORDS: LngLat = [CENTER[0] - 0.02, CENTER[1]]; +export const DEFAULT_POPUP_COORDS: LngLat = [CENTER[0] + 0.02, CENTER[1]]; diff --git a/example/default-popups/react/index.html b/example/default-popups/react/index.html new file mode 100644 index 0000000..4183912 --- /dev/null +++ b/example/default-popups/react/index.html @@ -0,0 +1,37 @@ + + + + React example mappable-default-ui-theme + + + + + + + + + + + + + + + +
+ + diff --git a/example/default-popups/react/index.tsx b/example/default-popups/react/index.tsx new file mode 100644 index 0000000..cedfa1e --- /dev/null +++ b/example/default-popups/react/index.tsx @@ -0,0 +1,70 @@ +import {CUSTOM_POPUP_COORDS, DEFAULT_POPUP_COORDS, LOCATION} from '../common'; + +window.map = null; + +main(); +async function main() { + const [mappableReact] = await Promise.all([mappable.import('@mappable-world/mappable-reactify'), mappable.ready]); + const reactify = mappableReact.reactify.bindTo(React, ReactDOM); + + const {MMap, MMapDefaultSchemeLayer, MMapDefaultFeaturesLayer, MMapControls, MMapControlButton} = + reactify.module(mappable); + + const {useState, useCallback} = React; + + const {MMapBalloonMarker, MMapDefaultPopupMarker} = reactify.module( + await mappable.import('@mappable-world/mappable-default-ui-theme') + ); + + ReactDOM.render( + + + , + document.getElementById('app') + ); + + function App() { + const [location] = useState(LOCATION); + const [showDefaultPopup, setShowDefaultPopup] = useState(true); + const [showCustomPopup, setShowCustomPopup] = useState(true); + + const toggleDefaultPopup = useCallback(() => setShowDefaultPopup((prev) => !prev), [showDefaultPopup]); + const toggleCustomPopup = useCallback(() => setShowCustomPopup((prev) => !prev), [showCustomPopup]); + const actionCallback = useCallback(() => alert('Click on action button!'), []); + const contentCallback = useCallback( + () => ( +
+
Title
+ +
Custom popup content
+
+ ), + [] + ); + const onCloseCustomPopup = useCallback(() => setShowCustomPopup(false), [showCustomPopup]); + const onCloseDefaultPopup = useCallback(() => setShowDefaultPopup(false), [showDefaultPopup]); + + return ( + (map = x)}> + + + + + + + + + + + + ); + } +} diff --git a/example/default-popups/vanilla/index.html b/example/default-popups/vanilla/index.html new file mode 100644 index 0000000..730390e --- /dev/null +++ b/example/default-popups/vanilla/index.html @@ -0,0 +1,35 @@ + + + + Vanilla example mappable-default-ui-theme + + + + + + + + + + + + + +
+ + diff --git a/example/default-popups/vanilla/index.ts b/example/default-popups/vanilla/index.ts new file mode 100644 index 0000000..1f1e363 --- /dev/null +++ b/example/default-popups/vanilla/index.ts @@ -0,0 +1,65 @@ +import {CUSTOM_POPUP_COORDS, DEFAULT_POPUP_COORDS, LOCATION} from '../common'; +window.map = null; + +main(); +async function main() { + await mappable.ready; + const {MMap, MMapDefaultSchemeLayer, MMapDefaultFeaturesLayer, MMapControls, MMapControlButton} = mappable; + + const {MMapBalloonMarker, MMapDefaultPopupMarker} = await mappable.import( + '@mappable-world/mappable-default-ui-theme' + ); + + map = new MMap(document.getElementById('app'), {location: LOCATION}); + + map.addChild(new MMapDefaultSchemeLayer({})); + map.addChild(new MMapDefaultFeaturesLayer({})); + + map.addChild( + new MMapControls({position: 'top right'}, [ + new MMapControlButton({ + text: 'Toggle custom popup', + onClick: () => customPopup.update({show: !customPopup.isOpen}) + }), + new MMapControlButton({ + text: 'Toggle default popup', + onClick: () => defaultPopup.update({show: !defaultPopup.isOpen}) + }) + ]) + ); + + const customPopup = new MMapBalloonMarker({ + coordinates: CUSTOM_POPUP_COORDS, + content: () => { + const popupElement = document.createElement('div'); + popupElement.classList.add('custom-popup'); + + const titleElement = document.createElement('div'); + titleElement.classList.add('title'); + titleElement.textContent = 'Title'; + popupElement.appendChild(titleElement); + + const closeButton = document.createElement('button'); + closeButton.textContent = 'close'; + closeButton.addEventListener('click', () => customPopup.update({show: false})); + popupElement.appendChild(closeButton); + + const contentElement = document.createElement('div'); + contentElement.classList.add('content'); + contentElement.textContent = 'Custom popup content'; + popupElement.appendChild(contentElement); + + return popupElement; + } + }); + map.addChild(customPopup); + + const defaultPopup = new MMapDefaultPopupMarker({ + coordinates: DEFAULT_POPUP_COORDS, + title: 'Default popup marker', + description: 'Description for default popup', + action: 'Make an action', + onAction: () => alert('Click on action button!') + }); + map.addChild(defaultPopup); +} diff --git a/example/default-popups/vue/index.html b/example/default-popups/vue/index.html new file mode 100644 index 0000000..85eeebd --- /dev/null +++ b/example/default-popups/vue/index.html @@ -0,0 +1,36 @@ + + + + Vue example mappable-default-ui-theme + + + + + + + + + + + + + + +
+ + diff --git a/example/default-popups/vue/index.ts b/example/default-popups/vue/index.ts new file mode 100644 index 0000000..3df68ed --- /dev/null +++ b/example/default-popups/vue/index.ts @@ -0,0 +1,84 @@ +import {CUSTOM_POPUP_COORDS, DEFAULT_POPUP_COORDS, LOCATION} from '../common'; + +window.map = null; + +main(); +async function main() { + const [mappableVue] = await Promise.all([mappable.import('@mappable-world/mappable-vuefy'), mappable.ready]); + const vuefy = mappableVue.vuefy.bindTo(Vue); + + const {MMap, MMapDefaultSchemeLayer, MMapDefaultFeaturesLayer, MMapControls, MMapControlButton} = + vuefy.module(mappable); + + const {MMapBalloonMarker, MMapDefaultPopupMarker} = vuefy.module( + await mappable.import('@mappable-world/mappable-default-ui-theme') + ); + + const app = Vue.createApp({ + components: { + MMap, + MMapDefaultSchemeLayer, + MMapDefaultFeaturesLayer, + MMapControls, + MMapControlButton, + MMapBalloonMarker, + MMapDefaultPopupMarker + }, + setup() { + const location = Vue.ref(LOCATION); + const showDefaultPopup = Vue.ref(true); + const showCustomPopup = Vue.ref(true); + const refMap = (ref: any) => { + window.map = ref?.entity; + }; + + const toggleDefaultPopup = () => (showDefaultPopup.value = !showDefaultPopup.value); + const toggleCustomPopup = () => (showCustomPopup.value = !showCustomPopup.value); + const actionCallback = () => alert('Click on action button!'); + const onCloseCustomPopup = () => (showCustomPopup.value = false); + const onCloseDefaultPopup = () => (showDefaultPopup.value = false); + + return { + location, + refMap, + toggleCustomPopup, + toggleDefaultPopup, + onCloseCustomPopup, + onCloseDefaultPopup, + actionCallback, + showCustomPopup, + showDefaultPopup, + CUSTOM_POPUP_COORDS, + DEFAULT_POPUP_COORDS + }; + }, + template: ` + + + + + + + + + + + + ` + }); + app.mount('#app'); +} From 055e07e193b68cdbf3e26dd45e6edb3017b64a95 Mon Sep 17 00:00:00 2001 From: Matthew Kalinin Date: Thu, 18 Apr 2024 17:41:08 +0300 Subject: [PATCH 38/48] add unit-tests --- jest.config.js | 7 +- .../MMapBalloonMarker.test.ts | 137 ++++++++++++++++++ .../MMapDefaultPopupMarker.test.ts | 91 ++++++++++++ .../MMapTooltipMarker.test.ts | 37 +++++ tests/common.ts | 10 ++ tests/utils/svgTransform.js | 11 ++ 6 files changed, 291 insertions(+), 2 deletions(-) create mode 100644 src/markers/MMapBalloonMarker/MMapBalloonMarker.test.ts create mode 100644 src/markers/MMapDefaultPopupMarker/MMapDefaultPopupMarker.test.ts create mode 100644 src/markers/MMapTooltipMarker/MMapTooltipMarker.test.ts create mode 100644 tests/common.ts create mode 100644 tests/utils/svgTransform.js diff --git a/jest.config.js b/jest.config.js index bbb284a..34b90c9 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,3 +1,6 @@ module.exports = { - ...require('@mappable-world/mappable-cli/jest.config') -} + ...require('@mappable-world/mappable-cli/jest.config'), + transform: { + '^.+\\.svg$': '/tests/utils/svgTransform.js' + } +}; diff --git a/src/markers/MMapBalloonMarker/MMapBalloonMarker.test.ts b/src/markers/MMapBalloonMarker/MMapBalloonMarker.test.ts new file mode 100644 index 0000000..a048a92 --- /dev/null +++ b/src/markers/MMapBalloonMarker/MMapBalloonMarker.test.ts @@ -0,0 +1,137 @@ +import {MMap} from '@mappable-world/mappable-types'; +import {createContainer, CENTER} from '../../../tests/common'; +import {MMapBalloonMarker} from './'; + +describe('MMapBalloonMarker', () => { + let map: MMap; + let container: HTMLElement; + + beforeEach(() => { + container = createContainer(); + document.body.append(container); + map = new mappable.MMap(container, {location: {center: CENTER, zoom: 0}}); + map.addChild(new mappable.MMapDefaultFeaturesLayer({})); + }); + + afterEach(() => { + map.destroy(); + }); + + it('add on map', () => { + const balloon = new MMapBalloonMarker({coordinates: CENTER, content: createPopupContent}); + map.addChild(balloon); + + expect(document.querySelector('.mappable--balloon-marker')).not.toBeNull(); + expect(document.querySelector('.mappable--balloon-marker .test-popup')).not.toBeNull(); + }); + it('changing show props', () => { + const balloon = new MMapBalloonMarker({show: true, coordinates: CENTER, content: createPopupContent}); + map.addChild(balloon); + + const popupMarkerElement = document.querySelector('.mappable--balloon-marker'); + expect(popupMarkerElement).not.toBeNull(); + + expect(balloon.isOpen).toBe(true); + expect(document.querySelector('.mappable--balloon-marker.hide')).toBeNull(); + + balloon.update({show: false}); + expect(balloon.isOpen).toBe(false); + expect(document.querySelector('.mappable--balloon-marker.hide')).not.toBeNull(); + + balloon.update({show: true}); + expect(balloon.isOpen).toBe(true); + expect(document.querySelector('.mappable--balloon-marker.hide')).toBeNull(); + }); + + it('offset props', () => { + const balloon = new MMapBalloonMarker({offset: 12, coordinates: CENTER, content: createPopupContent}); + map.addChild(balloon); + + const popupMarkerElement = document.querySelector('.mappable--balloon-marker'); + expect(popupMarkerElement.style.getPropertyValue('--offset')).toBe('12px'); + + balloon.update({offset: 24}); + expect(popupMarkerElement.style.getPropertyValue('--offset')).toBe('24px'); + }); + + describe('callback for closing and opening', () => { + it('callback on open', (done) => { + const onOpen = () => { + expect(balloon.isOpen).toBe(true); + done(); + }; + const balloon = new MMapBalloonMarker({ + show: true, + coordinates: CENTER, + content: createPopupContent, + onOpen + }); + map.addChild(balloon); + }); + it('callback on close', (done) => { + const onClose = () => { + expect(balloon.isOpen).toBe(false); + done(); + }; + const balloon = new MMapBalloonMarker({ + show: true, + coordinates: CENTER, + content: createPopupContent, + onClose + }); + map.addChild(balloon); + balloon.update({show: false}); + }); + }); + + describe('change popup position', () => { + it('initial default position', () => { + const balloon = new MMapBalloonMarker({coordinates: CENTER, content: createPopupContent}); + map.addChild(balloon); + + expect(document.querySelector('.mappable--balloon-marker.position-top')).not.toBeNull(); + }); + it('initial position', () => { + const balloon = new MMapBalloonMarker({position: 'left', coordinates: CENTER, content: createPopupContent}); + map.addChild(balloon); + + expect(document.querySelector('.mappable--balloon-marker.position-left')).not.toBeNull(); + }); + it('change position props', () => { + const balloon = new MMapBalloonMarker({coordinates: CENTER, content: createPopupContent}); + map.addChild(balloon); + + balloon.update({position: 'top'}); + expect(document.querySelector('.mappable--balloon-marker.position-top')).not.toBeNull(); + + balloon.update({position: 'bottom'}); + expect(document.querySelector('.mappable--balloon-marker.position-bottom')).not.toBeNull(); + + balloon.update({position: 'left'}); + expect(document.querySelector('.mappable--balloon-marker.position-left')).not.toBeNull(); + + balloon.update({position: 'right'}); + expect(document.querySelector('.mappable--balloon-marker.position-right')).not.toBeNull(); + }); + it('change combined position props', () => { + const balloon = new MMapBalloonMarker({coordinates: CENTER, content: createPopupContent}); + map.addChild(balloon); + + balloon.update({position: 'top left'}); + expect(document.querySelector('.mappable--balloon-marker.position-top.position-left')).not.toBeNull(); + balloon.update({position: 'top right'}); + expect(document.querySelector('.mappable--balloon-marker.position-top.position-right')).not.toBeNull(); + + balloon.update({position: 'bottom left'}); + expect(document.querySelector('.mappable--balloon-marker.position-bottom.position-left')).not.toBeNull(); + balloon.update({position: 'bottom right'}); + expect(document.querySelector('.mappable--balloon-marker.position-bottom.position-right')).not.toBeNull(); + }); + }); +}); + +const createPopupContent = () => { + const popup = document.createElement('div'); + popup.classList.add('test-popup'); + return popup; +}; diff --git a/src/markers/MMapDefaultPopupMarker/MMapDefaultPopupMarker.test.ts b/src/markers/MMapDefaultPopupMarker/MMapDefaultPopupMarker.test.ts new file mode 100644 index 0000000..4cbae80 --- /dev/null +++ b/src/markers/MMapDefaultPopupMarker/MMapDefaultPopupMarker.test.ts @@ -0,0 +1,91 @@ +import {MMap} from '@mappable-world/mappable-types'; +import {createContainer, CENTER} from '../../../tests/common'; +import {MMapDefaultPopupMarker} from './'; + +describe('MMapDefaultPopupMarker', () => { + let map: MMap; + let container: HTMLElement; + + beforeEach(() => { + container = createContainer(); + document.body.append(container); + map = new mappable.MMap(container, {location: {center: CENTER, zoom: 0}}); + map.addChild(new mappable.MMapDefaultFeaturesLayer({})); + }); + + afterEach(() => { + map.destroy(); + }); + + it('add on map', () => { + const defaultPopup = new MMapDefaultPopupMarker({coordinates: CENTER, title: 'Title'}); + map.addChild(defaultPopup); + + expect(document.querySelector(BASE_SELECTOR)).not.toBeNull(); + }); + + it('title props', () => { + const defaultPopup = new MMapDefaultPopupMarker({title: 'Title', coordinates: CENTER}); + map.addChild(defaultPopup); + let titleElement = document.querySelector(`${BASE_SELECTOR} .mappable--default-popup_header_title`); + expect(titleElement.textContent).toBe('Title'); + + defaultPopup.update({title: 'New Title'}); + titleElement = document.querySelector(`${BASE_SELECTOR} .mappable--default-popup_header_title`); + expect(titleElement.textContent).toBe('New Title'); + }); + + it('description props', () => { + const defaultPopup = new MMapDefaultPopupMarker({description: 'Description', coordinates: CENTER}); + map.addChild(defaultPopup); + let descriptionElement = document.querySelector(`${BASE_SELECTOR} .mappable--default-popup_description`); + expect(descriptionElement.textContent).toBe('Description'); + + defaultPopup.update({description: 'New Description'}); + descriptionElement = document.querySelector(`${BASE_SELECTOR} .mappable--default-popup_description`); + expect(descriptionElement.textContent).toBe('New Description'); + }); + + it('action props', () => { + const defaultPopup = new MMapDefaultPopupMarker({action: 'Action', coordinates: CENTER}); + map.addChild(defaultPopup); + let actionButton = document.querySelector(`${BASE_SELECTOR} .mappable--default-popup_action`); + expect(actionButton.textContent).toBe('Action'); + + defaultPopup.update({action: 'New Action'}); + actionButton = document.querySelector(`${BASE_SELECTOR} .mappable--default-popup_action`); + expect(actionButton.textContent).toBe('New Action'); + }); + + describe('test buttons', () => { + it('click close button', (done) => { + const onClose = () => { + expect(defaultPopup.isOpen).toBe(false); + done(); + }; + const defaultPopup = new MMapDefaultPopupMarker({title: 'Title', coordinates: CENTER, onClose}); + map.addChild(defaultPopup); + + const closeButton = document.querySelector(`${BASE_SELECTOR} .mappable--default-popup_header_close`); + expect(closeButton).not.toBeNull(); + + const event = new MouseEvent('click'); + closeButton.dispatchEvent(event); + }); + it('click action button', (done) => { + const onAction = () => { + done(); + }; + const defaultPopup = new MMapDefaultPopupMarker({action: 'Action', coordinates: CENTER, onAction}); + map.addChild(defaultPopup); + + const actionButton = document.querySelector(`${BASE_SELECTOR} .mappable--default-popup_action`); + expect(actionButton).not.toBeNull(); + + const event = new MouseEvent('click'); + actionButton.dispatchEvent(event); + }); + }); +}); + +const BASE_SELECTOR = '.mappable--balloon-marker .mappable--default-popup'; diff --git a/src/markers/MMapTooltipMarker/MMapTooltipMarker.test.ts b/src/markers/MMapTooltipMarker/MMapTooltipMarker.test.ts new file mode 100644 index 0000000..32f3150 --- /dev/null +++ b/src/markers/MMapTooltipMarker/MMapTooltipMarker.test.ts @@ -0,0 +1,37 @@ +import {MMap} from '@mappable-world/mappable-types'; +import {createContainer, CENTER} from '../../../tests/common'; +import {MMapTooltipMarker} from './'; + +describe('MMapTooltipMarker', () => { + let map: MMap; + let container: HTMLElement; + + beforeEach(() => { + container = createContainer(); + document.body.append(container); + map = new mappable.MMap(container, {location: {center: CENTER, zoom: 0}}); + map.addChild(new mappable.MMapDefaultFeaturesLayer({})); + }); + + afterEach(() => { + map.destroy(); + }); + + it('add on map', () => { + const tooltip = new MMapTooltipMarker({coordinates: CENTER, content: 'Tooltip'}); + map.addChild(tooltip); + + expect(document.querySelector('.mappable--balloon-marker .mappable--default-tooltip')).not.toBeNull(); + }); + it('change content props', () => { + const tooltip = new MMapTooltipMarker({coordinates: CENTER, content: 'Tooltip'}); + map.addChild(tooltip); + const tooltipElement = document.querySelector( + '.mappable--balloon-marker .mappable--default-tooltip' + ); + expect(tooltipElement.textContent).toBe('Tooltip'); + + tooltip.update({content: 'New content'}); + expect(tooltipElement.textContent).toBe('New content'); + }); +}); diff --git a/tests/common.ts b/tests/common.ts new file mode 100644 index 0000000..0502476 --- /dev/null +++ b/tests/common.ts @@ -0,0 +1,10 @@ +import {LngLat} from '@mappable-world/mappable-types'; + +export const CENTER: LngLat = [0, 0]; + +export const createContainer = () => { + const container = document.createElement('div'); + container.style.width = '640px'; + container.style.height = '480px'; + return container; +}; diff --git a/tests/utils/svgTransform.js b/tests/utils/svgTransform.js new file mode 100644 index 0000000..781ca5d --- /dev/null +++ b/tests/utils/svgTransform.js @@ -0,0 +1,11 @@ +module.exports = { + process() { + return { + code: `module.exports = {};` + }; + }, + getCacheKey() { + // The output is always the same. + return 'svgTransform'; + } +}; From e2a9d4c4b550dccdbc629c082c85131741dc61f9 Mon Sep 17 00:00:00 2001 From: Matthew Kalinin Date: Mon, 22 Apr 2024 12:17:03 +0300 Subject: [PATCH 39/48] add prefix in css --- .../MMapBalloonMarker.test.ts | 58 ++++++++++---- src/markers/MMapBalloonMarker/index.css | 79 ++++++++++--------- src/markers/MMapBalloonMarker/index.ts | 22 ++++-- src/markers/MMapDefaultPopupMarker/index.css | 4 +- 4 files changed, 100 insertions(+), 63 deletions(-) diff --git a/src/markers/MMapBalloonMarker/MMapBalloonMarker.test.ts b/src/markers/MMapBalloonMarker/MMapBalloonMarker.test.ts index a048a92..821511e 100644 --- a/src/markers/MMapBalloonMarker/MMapBalloonMarker.test.ts +++ b/src/markers/MMapBalloonMarker/MMapBalloonMarker.test.ts @@ -32,15 +32,15 @@ describe('MMapBalloonMarker', () => { expect(popupMarkerElement).not.toBeNull(); expect(balloon.isOpen).toBe(true); - expect(document.querySelector('.mappable--balloon-marker.hide')).toBeNull(); + expect(document.querySelector('.mappable--balloon-marker.mappable--balloon-marker__hide')).toBeNull(); balloon.update({show: false}); expect(balloon.isOpen).toBe(false); - expect(document.querySelector('.mappable--balloon-marker.hide')).not.toBeNull(); + expect(document.querySelector('.mappable--balloon-marker.mappable--balloon-marker__hide')).not.toBeNull(); balloon.update({show: true}); expect(balloon.isOpen).toBe(true); - expect(document.querySelector('.mappable--balloon-marker.hide')).toBeNull(); + expect(document.querySelector('.mappable--balloon-marker.mappable--balloon-marker__hide')).toBeNull(); }); it('offset props', () => { @@ -48,10 +48,10 @@ describe('MMapBalloonMarker', () => { map.addChild(balloon); const popupMarkerElement = document.querySelector('.mappable--balloon-marker'); - expect(popupMarkerElement.style.getPropertyValue('--offset')).toBe('12px'); + expect(popupMarkerElement.style.getPropertyValue('--mappable-default-offset')).toBe('12px'); balloon.update({offset: 24}); - expect(popupMarkerElement.style.getPropertyValue('--offset')).toBe('24px'); + expect(popupMarkerElement.style.getPropertyValue('--mappable-default-offset')).toBe('24px'); }); describe('callback for closing and opening', () => { @@ -89,43 +89,71 @@ describe('MMapBalloonMarker', () => { const balloon = new MMapBalloonMarker({coordinates: CENTER, content: createPopupContent}); map.addChild(balloon); - expect(document.querySelector('.mappable--balloon-marker.position-top')).not.toBeNull(); + expect( + document.querySelector('.mappable--balloon-marker.mappable--balloon-marker__position-top') + ).not.toBeNull(); }); it('initial position', () => { const balloon = new MMapBalloonMarker({position: 'left', coordinates: CENTER, content: createPopupContent}); map.addChild(balloon); - expect(document.querySelector('.mappable--balloon-marker.position-left')).not.toBeNull(); + expect( + document.querySelector('.mappable--balloon-marker.mappable--balloon-marker__position-left') + ).not.toBeNull(); }); it('change position props', () => { const balloon = new MMapBalloonMarker({coordinates: CENTER, content: createPopupContent}); map.addChild(balloon); balloon.update({position: 'top'}); - expect(document.querySelector('.mappable--balloon-marker.position-top')).not.toBeNull(); + expect( + document.querySelector('.mappable--balloon-marker.mappable--balloon-marker__position-top') + ).not.toBeNull(); balloon.update({position: 'bottom'}); - expect(document.querySelector('.mappable--balloon-marker.position-bottom')).not.toBeNull(); + expect( + document.querySelector('.mappable--balloon-marker.mappable--balloon-marker__position-bottom') + ).not.toBeNull(); balloon.update({position: 'left'}); - expect(document.querySelector('.mappable--balloon-marker.position-left')).not.toBeNull(); + expect( + document.querySelector('.mappable--balloon-marker.mappable--balloon-marker__position-left') + ).not.toBeNull(); balloon.update({position: 'right'}); - expect(document.querySelector('.mappable--balloon-marker.position-right')).not.toBeNull(); + expect( + document.querySelector('.mappable--balloon-marker.mappable--balloon-marker__position-right') + ).not.toBeNull(); }); it('change combined position props', () => { const balloon = new MMapBalloonMarker({coordinates: CENTER, content: createPopupContent}); map.addChild(balloon); balloon.update({position: 'top left'}); - expect(document.querySelector('.mappable--balloon-marker.position-top.position-left')).not.toBeNull(); + expect( + document.querySelector( + '.mappable--balloon-marker.mappable--balloon-marker__position-top.mappable--balloon-marker__position-left' + ) + ).not.toBeNull(); balloon.update({position: 'top right'}); - expect(document.querySelector('.mappable--balloon-marker.position-top.position-right')).not.toBeNull(); + expect( + document.querySelector( + '.mappable--balloon-marker.mappable--balloon-marker__position-top.mappable--balloon-marker__position-right' + ) + ).not.toBeNull(); balloon.update({position: 'bottom left'}); - expect(document.querySelector('.mappable--balloon-marker.position-bottom.position-left')).not.toBeNull(); + expect( + document.querySelector( + '.mappable--balloon-marker.mappable--balloon-marker__position-bottom.mappable--balloon-marker__position-left' + ) + ).not.toBeNull(); balloon.update({position: 'bottom right'}); - expect(document.querySelector('.mappable--balloon-marker.position-bottom.position-right')).not.toBeNull(); + expect( + document.querySelector( + '.mappable--balloon-marker.mappable--balloon-marker__position-bottom.mappable--balloon-marker__position-right' + ) + ).not.toBeNull(); }); }); }); diff --git a/src/markers/MMapBalloonMarker/index.css b/src/markers/MMapBalloonMarker/index.css index 6a52089..15b815b 100644 --- a/src/markers/MMapBalloonMarker/index.css +++ b/src/markers/MMapBalloonMarker/index.css @@ -1,14 +1,17 @@ .mappable--balloon-marker { - --tail-height: 12px; - --tail-width: 16px; - --border-radius: 12px; + --mappable-default-tail-height: 12px; + --mappable-default-tail-width: 16px; + --mappable-default-border-radius: 12px; - --tail-height-and-offset: calc(var(--tail-height) + var(--offset)); + --mappable-default-tail-height-and-offset: calc( + var(--mappable-default-tail-height) + var(--mappable-default-offset) + ); - --balloon-tail-transform-top: translate(-50%, calc(-100% - var(--offset))) rotate(180deg); - --balloon-tail-transform-bottom: translate(-50%, var(--offset)); + --mappable-default-balloon-tail-transform-top: translate(-50%, calc(-100% - var(--mappable-default-offset))) + rotate(180deg); + --mappable-default-balloon-tail-transform-bottom: translate(-50%, var(--mappable-default-offset)); - &.hide { + &.mappable--balloon-marker__hide { display: none; } } @@ -24,7 +27,7 @@ display: block; position: absolute; padding: 8px 12px; - border-radius: var(--border-radius); + border-radius: var(--mappable-default-border-radius); background-color: #fff; color: #34374a; font-size: 14px; @@ -37,7 +40,7 @@ overflow: hidden; text-overflow: ellipsis; - &.mappable--balloon__dark { + &.mappable--balloon-marker__dark { background-color: #272729; color: #c8c9cc; } @@ -48,7 +51,7 @@ position: absolute; color: #fff; - &.mappable--balloon__dark { + &.mappable--balloon-marker__dark { color: #272729; } @@ -58,86 +61,86 @@ } /* positions */ -.position-top { +.mappable--balloon-marker__position-top { .mappable--balloon-marker_container { - transform: translate(-50%, calc(-100% - var(--tail-height-and-offset))); + transform: translate(-50%, calc(-100% - var(--mappable-default-tail-height-and-offset))); } .mappable--balloon-marker_tail { - transform: var(--balloon-tail-transform-top); + transform: var(--mappable-default-balloon-tail-transform-top); } /* top left */ - &.position-left { + &.mappable--balloon-marker__position-left { .mappable--balloon-marker_container { transform: translate( - calc(-100% + var(--border-radius) + var(--tail-width) / 2), - calc(-100% - var(--tail-height-and-offset)) + calc(-100% + var(--mappable-default-border-radius) + var(--mappable-default-tail-width) / 2), + calc(-100% - var(--mappable-default-tail-height-and-offset)) ); } .mappable--balloon-marker_tail { - transform: var(--balloon-tail-transform-top); + transform: var(--mappable-default-balloon-tail-transform-top); } } /* top right */ - &.position-right { + &.mappable--balloon-marker__position-right { .mappable--balloon-marker_container { transform: translate( - calc(-1 * var(--border-radius) - var(--tail-width) / 2), - calc(-100% - var(--tail-height-and-offset)) + calc(-1 * var(--mappable-default-border-radius) - var(--mappable-default-tail-width) / 2), + calc(-100% - var(--mappable-default-tail-height-and-offset)) ); } .mappable--balloon-marker_tail { - transform: var(--balloon-tail-transform-top); + transform: var(--mappable-default-balloon-tail-transform-top); } } } -.position-bottom { +.mappable--balloon-marker__position-bottom { .mappable--balloon-marker_container { - transform: translate(-50%, var(--tail-height-and-offset)); + transform: translate(-50%, var(--mappable-default-tail-height-and-offset)); } .mappable--balloon-marker_tail { - transform: var(--balloon-tail-transform-bottom); + transform: var(--mappable-default-balloon-tail-transform-bottom); } /* bottom left */ - &.position-left { + &.mappable--balloon-marker__position-left { .mappable--balloon-marker_container { transform: translate( - calc(-100% + var(--border-radius) + var(--tail-width) / 2), - var(--tail-height-and-offset) + calc(-100% + var(--mappable-default-border-radius) + var(--mappable-default-tail-width) / 2), + var(--mappable-default-tail-height-and-offset) ); } .mappable--balloon-marker_tail { - transform: var(--balloon-tail-transform-bottom); + transform: var(--mappable-default-balloon-tail-transform-bottom); } } /* bottom right */ - &.position-right { + &.mappable--balloon-marker__position-right { .mappable--balloon-marker_container { transform: translate( - calc(-1 * var(--border-radius) - var(--tail-width) / 2), - var(--tail-height-and-offset) + calc(-1 * var(--mappable-default-border-radius) - var(--mappable-default-tail-width) / 2), + var(--mappable-default-tail-height-and-offset) ); } .mappable--balloon-marker_tail { - transform: var(--balloon-tail-transform-bottom); + transform: var(--mappable-default-balloon-tail-transform-bottom); } } } -.position-left { +.mappable--balloon-marker__position-left { .mappable--balloon-marker_container { - transform: translate(calc(-100% - var(--tail-height-and-offset)), -50%); + transform: translate(calc(-100% - var(--mappable-default-tail-height-and-offset)), -50%); } .mappable--balloon-marker_tail { - transform: translate(calc(-100% - var(--offset)), -50%) rotate(90deg); + transform: translate(calc(-100% - var(--mappable-default-offset)), -50%) rotate(90deg); } } -.position-right { +.mappable--balloon-marker__position-right { .mappable--balloon-marker_container { - transform: translate(var(--tail-height-and-offset), -50%); + transform: translate(var(--mappable-default-tail-height-and-offset), -50%); } .mappable--balloon-marker_tail { - transform: translate(var(--offset), -50%) rotate(-90deg); + transform: translate(var(--mappable-default-offset), -50%) rotate(-90deg); } } diff --git a/src/markers/MMapBalloonMarker/index.ts b/src/markers/MMapBalloonMarker/index.ts index 7e72c45..f40df2c 100644 --- a/src/markers/MMapBalloonMarker/index.ts +++ b/src/markers/MMapBalloonMarker/index.ts @@ -69,7 +69,7 @@ export class MMapBalloonMarker extends mappable.MMapComplexEntity Date: Mon, 22 Apr 2024 13:23:58 +0300 Subject: [PATCH 40/48] add open animations --- src/markers/MMapBalloonMarker/index.css | 54 +++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/src/markers/MMapBalloonMarker/index.css b/src/markers/MMapBalloonMarker/index.css index 15b815b..50dc5d4 100644 --- a/src/markers/MMapBalloonMarker/index.css +++ b/src/markers/MMapBalloonMarker/index.css @@ -1,3 +1,47 @@ +@keyframes mappable--balloon-marker-show-top { + from { + transform: translateY(12px); + opacity: 0; + } + to { + transform: none; + opacity: 1; + } +} + +@keyframes mappable--balloon-marker-show-bottom { + from { + transform: translateY(-12px); + opacity: 0; + } + to { + transform: none; + opacity: 1; + } +} + +@keyframes mappable--balloon-marker-show-left { + from { + transform: translateX(12px); + opacity: 0; + } + to { + transform: none; + opacity: 1; + } +} + +@keyframes mappable--balloon-marker-show-right { + from { + transform: translateX(-12px); + opacity: 0; + } + to { + transform: none; + opacity: 1; + } +} + .mappable--balloon-marker { --mappable-default-tail-height: 12px; --mappable-default-tail-width: 16px; @@ -11,6 +55,8 @@ rotate(180deg); --mappable-default-balloon-tail-transform-bottom: translate(-50%, var(--mappable-default-offset)); + position: absolute; + &.mappable--balloon-marker__hide { display: none; } @@ -62,6 +108,8 @@ /* positions */ .mappable--balloon-marker__position-top { + animation: mappable--balloon-marker-show-top 200ms ease-out !important; + .mappable--balloon-marker_container { transform: translate(-50%, calc(-100% - var(--mappable-default-tail-height-and-offset))); } @@ -95,6 +143,8 @@ } .mappable--balloon-marker__position-bottom { + animation: mappable--balloon-marker-show-bottom 200ms ease-out !important; + .mappable--balloon-marker_container { transform: translate(-50%, var(--mappable-default-tail-height-and-offset)); } @@ -128,6 +178,8 @@ } .mappable--balloon-marker__position-left { + animation: mappable--balloon-marker-show-left 200ms ease-out; + .mappable--balloon-marker_container { transform: translate(calc(-100% - var(--mappable-default-tail-height-and-offset)), -50%); } @@ -137,6 +189,8 @@ } .mappable--balloon-marker__position-right { + animation: mappable--balloon-marker-show-right 200ms ease-out; + .mappable--balloon-marker_container { transform: translate(var(--mappable-default-tail-height-and-offset), -50%); } From e1a9bedba2b4cfdbc08da00085b3e528e1ca2400 Mon Sep 17 00:00:00 2001 From: Matthew Kalinin Date: Thu, 25 Apr 2024 13:36:26 +0300 Subject: [PATCH 41/48] remove __impl --- src/markers/MMapBalloonMarker/index.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/markers/MMapBalloonMarker/index.ts b/src/markers/MMapBalloonMarker/index.ts index f40df2c..12cf08e 100644 --- a/src/markers/MMapBalloonMarker/index.ts +++ b/src/markers/MMapBalloonMarker/index.ts @@ -80,10 +80,6 @@ export class MMapBalloonMarker extends mappable.MMapComplexEntity Date: Fri, 26 Apr 2024 18:01:48 +0300 Subject: [PATCH 42/48] add popup in default marker --- example/marker-popup/common.css | 0 example/marker-popup/common.ts | 5 ++ example/marker-popup/react/index.html | 37 +++++++++++++ example/marker-popup/react/index.tsx | 49 +++++++++++++++++ example/marker-popup/vanilla/index.html | 35 ++++++++++++ example/marker-popup/vanilla/index.ts | 36 +++++++++++++ example/marker-popup/vue/index.html | 36 +++++++++++++ example/marker-popup/vue/index.ts | 54 +++++++++++++++++++ src/markers/MMapDefaultMarker/index.ts | 62 +++++++++++++++++++++- src/markers/MMapDefaultMarker/vue/index.ts | 10 ++-- 10 files changed, 318 insertions(+), 6 deletions(-) create mode 100644 example/marker-popup/common.css create mode 100644 example/marker-popup/common.ts create mode 100644 example/marker-popup/react/index.html create mode 100644 example/marker-popup/react/index.tsx create mode 100644 example/marker-popup/vanilla/index.html create mode 100644 example/marker-popup/vanilla/index.ts create mode 100644 example/marker-popup/vue/index.html create mode 100644 example/marker-popup/vue/index.ts diff --git a/example/marker-popup/common.css b/example/marker-popup/common.css new file mode 100644 index 0000000..e69de29 diff --git a/example/marker-popup/common.ts b/example/marker-popup/common.ts new file mode 100644 index 0000000..835e6ef --- /dev/null +++ b/example/marker-popup/common.ts @@ -0,0 +1,5 @@ +import type {LngLat, MMapLocationRequest} from '@mappable-world/mappable-types'; + +export const CENTER: LngLat = [55.442795, 25.24107]; + +export const LOCATION: MMapLocationRequest = {center: CENTER, zoom: 9}; diff --git a/example/marker-popup/react/index.html b/example/marker-popup/react/index.html new file mode 100644 index 0000000..4183912 --- /dev/null +++ b/example/marker-popup/react/index.html @@ -0,0 +1,37 @@ + + + + React example mappable-default-ui-theme + + + + + + + + + + + + + + + +
+ + diff --git a/example/marker-popup/react/index.tsx b/example/marker-popup/react/index.tsx new file mode 100644 index 0000000..84cfca8 --- /dev/null +++ b/example/marker-popup/react/index.tsx @@ -0,0 +1,49 @@ +import {MarkerPopupProps, MarkerSizeProps} from '../../src'; +import {CENTER, LOCATION} from '../common'; + +window.map = null; + +main(); +async function main() { + const [mappableReact] = await Promise.all([mappable.import('@mappable-world/mappable-reactify'), mappable.ready]); + const reactify = mappableReact.reactify.bindTo(React, ReactDOM); + + const {MMap, MMapDefaultSchemeLayer, MMapDefaultFeaturesLayer, MMapControls, MMapControlButton} = + reactify.module(mappable); + + const {useState, useCallback} = React; + + const {MMapDefaultMarker} = reactify.module(await mappable.import('@mappable-world/mappable-default-ui-theme')); + + ReactDOM.render( + + + , + document.getElementById('app') + ); + + function App() { + const [location] = useState(LOCATION); + const [size, setSize] = useState('normal'); + const [popup] = useState({ + title: 'Popup', + description: 'Description for this marker', + action: 'Action' + }); + + return ( + (map = x)}> + + + + + + + setSize('normal'), [])} /> + setSize('small'), [])} /> + setSize('micro'), [])} /> + + + ); + } +} diff --git a/example/marker-popup/vanilla/index.html b/example/marker-popup/vanilla/index.html new file mode 100644 index 0000000..730390e --- /dev/null +++ b/example/marker-popup/vanilla/index.html @@ -0,0 +1,35 @@ + + + + Vanilla example mappable-default-ui-theme + + + + + + + + + + + + + +
+ + diff --git a/example/marker-popup/vanilla/index.ts b/example/marker-popup/vanilla/index.ts new file mode 100644 index 0000000..62fad3a --- /dev/null +++ b/example/marker-popup/vanilla/index.ts @@ -0,0 +1,36 @@ +import {CENTER, LOCATION} from '../common'; +window.map = null; + +main(); +async function main() { + await mappable.ready; + const {MMap, MMapDefaultSchemeLayer, MMapDefaultFeaturesLayer, MMapControls, MMapControlButton} = mappable; + + const {MMapDefaultMarker} = await mappable.import('@mappable-world/mappable-default-ui-theme'); + + map = new MMap(document.getElementById('app'), {location: LOCATION}); + + map.addChild(new MMapDefaultSchemeLayer({})); + map.addChild(new MMapDefaultFeaturesLayer({})); + + const marker = new MMapDefaultMarker({ + coordinates: CENTER, + iconName: 'fallback', + size: 'normal', + popup: { + title: 'Popup', + description: 'Description for this marker', + action: 'Action' + } + }); + + map.addChild(marker); + + map.addChild( + new MMapControls({position: 'top left'}, [ + new MMapControlButton({text: 'Normal', onClick: () => marker.update({size: 'normal'})}), + new MMapControlButton({text: 'Small', onClick: () => marker.update({size: 'small'})}), + new MMapControlButton({text: 'Micro', onClick: () => marker.update({size: 'micro'})}) + ]) + ); +} diff --git a/example/marker-popup/vue/index.html b/example/marker-popup/vue/index.html new file mode 100644 index 0000000..85eeebd --- /dev/null +++ b/example/marker-popup/vue/index.html @@ -0,0 +1,36 @@ + + + + Vue example mappable-default-ui-theme + + + + + + + + + + + + + + +
+ + diff --git a/example/marker-popup/vue/index.ts b/example/marker-popup/vue/index.ts new file mode 100644 index 0000000..3ddd9d2 --- /dev/null +++ b/example/marker-popup/vue/index.ts @@ -0,0 +1,54 @@ +import {MarkerPopupProps, MarkerSizeProps} from '../../src'; +import {CENTER, LOCATION} from '../common'; + +window.map = null; + +main(); +async function main() { + const [mappableVue] = await Promise.all([mappable.import('@mappable-world/mappable-vuefy'), mappable.ready]); + const vuefy = mappableVue.vuefy.bindTo(Vue); + + const {MMap, MMapDefaultSchemeLayer, MMapDefaultFeaturesLayer, MMapControls, MMapControlButton} = + vuefy.module(mappable); + + const {MMapDefaultMarker} = vuefy.module(await mappable.import('@mappable-world/mappable-default-ui-theme')); + + const app = Vue.createApp({ + components: { + MMap, + MMapDefaultSchemeLayer, + MMapDefaultFeaturesLayer, + MMapControls, + MMapControlButton, + MMapDefaultMarker + }, + setup() { + const size = Vue.ref('normal'); + const popup = Vue.ref({ + title: 'Popup', + description: 'Description for this marker', + action: 'Action' + }); + const refMap = (ref: any) => { + window.map = ref?.entity; + }; + const setNormalSize = () => (size.value = 'normal'); + const setSmallSize = () => (size.value = 'small'); + const setMicroSize = () => (size.value = 'micro'); + + return {LOCATION, CENTER, size, popup, refMap, setNormalSize, setSmallSize, setMicroSize}; + }, + template: ` + + + + + + + + + + ` + }); + app.mount('#app'); +} diff --git a/src/markers/MMapDefaultMarker/index.ts b/src/markers/MMapDefaultMarker/index.ts index fcad78e..bce4993 100644 --- a/src/markers/MMapDefaultMarker/index.ts +++ b/src/markers/MMapDefaultMarker/index.ts @@ -1,6 +1,7 @@ import {MMapMarker, MMapMarkerProps} from '@mappable-world/mappable-types'; import {IconColor, IconName, iconColors, icons} from '../../icons'; import {MMapDefaultMarkerVuefyOptions} from './vue'; +import {MMapDefaultPopupMarker} from '../'; import microPoiStrokeSVG from './backgrounds/micro-poi-stroke.svg'; import microPoiSVG from './backgrounds/micro-poi.svg'; @@ -30,9 +31,21 @@ const HINT_SUBTITLE_CLASS = 'mappable--hint-subtitle'; const HINT_STABLE = 'mappable--hint__stable'; const HINT_HOVERED = 'mappable--hint__hovered'; +const DISTANCE_BETWEEN_POPUP_AND_MARKER = 8; + export type ThemesColor = {day: string; night: string}; export type MarkerColorProps = IconColor | ThemesColor; export type MarkerSizeProps = 'normal' | 'small' | 'micro'; +export type MarkerPopupProps = { + /** Displayed title in popup header */ + title?: string; + /** Displayed description */ + description?: string; + /** The inscription on the action button */ + action?: string; + /** Callback of click the action button */ + onAction?: () => void; +}; export type MMapDefaultMarkerProps = MMapMarkerProps & { iconName?: IconName; @@ -41,6 +54,7 @@ export type MMapDefaultMarkerProps = MMapMarkerProps & { title?: string; subtitle?: string; staticHint?: boolean; + popup?: MarkerPopupProps; }; const defaultProps = Object.freeze({color: 'darkgray', size: 'small', staticHint: true}); @@ -64,6 +78,8 @@ export class MMapDefaultMarker extends mappable.MMapComplexEntity this._updateTheme(), { immediate: true }); @@ -132,6 +163,11 @@ export class MMapDefaultMarker extends mappable.MMapComplexEntity { + this._popup.update({show: !this._popup.isOpen}); + this._props.onClick?.(event); + }; + private _updateTheme() { const themeCtx = this._consumeContext(mappable.ThemeContext); const theme = themeCtx.theme; @@ -222,6 +263,23 @@ export class MMapDefaultMarker extends mappable.MMapComplexEntity = { @@ -24,10 +24,12 @@ export const MMapDefaultMarkerVuefyOptions: CustomVuefyOptions, onFastClick: Function as TVue.PropType, iconName: {type: String as TVue.PropType}, - color: {type: Object as TVue.PropType, default: 'darkgray'}, + // @ts-ignore CustomVuefyOptions types no support multiple types + color: {type: [Object as TVue.PropType, String], default: 'darkgray'}, size: {type: String as TVue.PropType, default: 'small'}, title: {type: String}, subtitle: {type: String}, - staticHint: {type: Boolean, default: true} + staticHint: {type: Boolean, default: true}, + popup: {type: Object as TVue.PropType} } }; From 4464bb9cb3bb5aecc2391e8784c46fea3a8c8f3c Mon Sep 17 00:00:00 2001 From: Matthew Kalinin Date: Sat, 27 Apr 2024 18:13:50 +0300 Subject: [PATCH 43/48] fix popup in marker --- src/markers/MMapDefaultMarker/index.ts | 55 ++++++++++++++------ src/markers/MMapDefaultPopupMarker/index.css | 1 + 2 files changed, 39 insertions(+), 17 deletions(-) diff --git a/src/markers/MMapDefaultMarker/index.ts b/src/markers/MMapDefaultMarker/index.ts index bce4993..1225270 100644 --- a/src/markers/MMapDefaultMarker/index.ts +++ b/src/markers/MMapDefaultMarker/index.ts @@ -78,7 +78,7 @@ export class MMapDefaultMarker extends mappable.MMapComplexEntity this._updateTheme(), { immediate: true }); } - protected _onUpdate(propsDiff: Partial): void { + protected _onUpdate(propsDiff: Partial, oldProps: MMapDefaultMarkerProps): void { const {title, subtitle} = this._props; if (propsDiff.color !== undefined) { this._color = this._getColor(); this._updateTheme(); } + + // popup props is changed + if (this._props.popup !== oldProps.popup) { + if (this._props.popup === undefined && oldProps.popup !== undefined) { + this.removeChild(this._popup); + this._popup = undefined; + } else if (this._props.popup !== undefined && oldProps.popup === undefined) { + this._popup = this._createPopupMarker(); + this.addChild(this._popup); + } else { + this._popup.update(this._props.popup); + } + } + if (propsDiff.size !== undefined) { this._updateMarkerSize(); this._updateSVG(); - this._updatePopupOffset(); - } - - if (propsDiff.popup !== undefined) { - this._popup.update(this._props.popup); + if (this._popup) { + this._popup.update({offset: this._getPopupOffset()}); + } } this._titleHint.textContent = title ?? ''; @@ -187,6 +195,16 @@ export class MMapDefaultMarker extends mappable.MMapComplexEntity { + if (!this._popup) { + return; + } this._popup.update({show: !this._popup.isOpen}); this._props.onClick?.(event); }; @@ -263,7 +284,7 @@ export class MMapDefaultMarker extends mappable.MMapComplexEntity Date: Wed, 8 May 2024 15:28:30 +0300 Subject: [PATCH 44/48] Rename ballon to popup --- example/default-popups/react/index.tsx | 4 +- example/default-popups/vanilla/index.ts | 4 +- example/default-popups/vue/index.ts | 8 +- .../MMapBalloonMarker.test.ts | 165 ------------------ src/markers/MMapBalloonMarker/react/index.tsx | 43 ----- .../MMapDefaultPopupMarker.test.ts | 4 +- src/markers/MMapDefaultPopupMarker/index.ts | 22 +-- .../MMapPopupMarker/MMapPopupMarker.test.ts | 165 ++++++++++++++++++ .../index.css | 94 +++++----- .../index.ts | 102 +++++------ src/markers/MMapPopupMarker/react/index.tsx | 43 +++++ .../tail.svg | 0 .../vue/index.ts | 34 ++-- .../MMapTextPopupMarker.test.ts} | 6 +- .../index.css | 0 .../index.ts | 16 +- .../vue/index.ts | 0 src/markers/index.ts | 4 +- 18 files changed, 352 insertions(+), 362 deletions(-) delete mode 100644 src/markers/MMapBalloonMarker/MMapBalloonMarker.test.ts delete mode 100644 src/markers/MMapBalloonMarker/react/index.tsx create mode 100644 src/markers/MMapPopupMarker/MMapPopupMarker.test.ts rename src/markers/{MMapBalloonMarker => MMapPopupMarker}/index.css (58%) rename src/markers/{MMapBalloonMarker => MMapPopupMarker}/index.ts (53%) create mode 100644 src/markers/MMapPopupMarker/react/index.tsx rename src/markers/{MMapBalloonMarker => MMapPopupMarker}/tail.svg (100%) rename src/markers/{MMapBalloonMarker => MMapPopupMarker}/vue/index.ts (65%) rename src/markers/{MMapTooltipMarker/MMapTooltipMarker.test.ts => MMapTextPopupMarker/MMapTextPopupMarker.test.ts} (81%) rename src/markers/{MMapTooltipMarker => MMapTextPopupMarker}/index.css (100%) rename src/markers/{MMapTooltipMarker => MMapTextPopupMarker}/index.ts (73%) rename src/markers/{MMapTooltipMarker => MMapTextPopupMarker}/vue/index.ts (100%) diff --git a/example/default-popups/react/index.tsx b/example/default-popups/react/index.tsx index cedfa1e..8934637 100644 --- a/example/default-popups/react/index.tsx +++ b/example/default-popups/react/index.tsx @@ -12,7 +12,7 @@ async function main() { const {useState, useCallback} = React; - const {MMapBalloonMarker, MMapDefaultPopupMarker} = reactify.module( + const {MMapPopupMarker, MMapDefaultPopupMarker} = reactify.module( await mappable.import('@mappable-world/mappable-default-ui-theme') ); @@ -53,7 +53,7 @@ async function main() { - + { const popupElement = document.createElement('div'); diff --git a/example/default-popups/vue/index.ts b/example/default-popups/vue/index.ts index 3df68ed..7ff32fd 100644 --- a/example/default-popups/vue/index.ts +++ b/example/default-popups/vue/index.ts @@ -10,7 +10,7 @@ async function main() { const {MMap, MMapDefaultSchemeLayer, MMapDefaultFeaturesLayer, MMapControls, MMapControlButton} = vuefy.module(mappable); - const {MMapBalloonMarker, MMapDefaultPopupMarker} = vuefy.module( + const {MMapPopupMarker, MMapDefaultPopupMarker} = vuefy.module( await mappable.import('@mappable-world/mappable-default-ui-theme') ); @@ -21,7 +21,7 @@ async function main() { MMapDefaultFeaturesLayer, MMapControls, MMapControlButton, - MMapBalloonMarker, + MMapPopupMarker, MMapDefaultPopupMarker }, setup() { @@ -60,7 +60,7 @@ async function main() { - + - + { - let map: MMap; - let container: HTMLElement; - - beforeEach(() => { - container = createContainer(); - document.body.append(container); - map = new mappable.MMap(container, {location: {center: CENTER, zoom: 0}}); - map.addChild(new mappable.MMapDefaultFeaturesLayer({})); - }); - - afterEach(() => { - map.destroy(); - }); - - it('add on map', () => { - const balloon = new MMapBalloonMarker({coordinates: CENTER, content: createPopupContent}); - map.addChild(balloon); - - expect(document.querySelector('.mappable--balloon-marker')).not.toBeNull(); - expect(document.querySelector('.mappable--balloon-marker .test-popup')).not.toBeNull(); - }); - it('changing show props', () => { - const balloon = new MMapBalloonMarker({show: true, coordinates: CENTER, content: createPopupContent}); - map.addChild(balloon); - - const popupMarkerElement = document.querySelector('.mappable--balloon-marker'); - expect(popupMarkerElement).not.toBeNull(); - - expect(balloon.isOpen).toBe(true); - expect(document.querySelector('.mappable--balloon-marker.mappable--balloon-marker__hide')).toBeNull(); - - balloon.update({show: false}); - expect(balloon.isOpen).toBe(false); - expect(document.querySelector('.mappable--balloon-marker.mappable--balloon-marker__hide')).not.toBeNull(); - - balloon.update({show: true}); - expect(balloon.isOpen).toBe(true); - expect(document.querySelector('.mappable--balloon-marker.mappable--balloon-marker__hide')).toBeNull(); - }); - - it('offset props', () => { - const balloon = new MMapBalloonMarker({offset: 12, coordinates: CENTER, content: createPopupContent}); - map.addChild(balloon); - - const popupMarkerElement = document.querySelector('.mappable--balloon-marker'); - expect(popupMarkerElement.style.getPropertyValue('--mappable-default-offset')).toBe('12px'); - - balloon.update({offset: 24}); - expect(popupMarkerElement.style.getPropertyValue('--mappable-default-offset')).toBe('24px'); - }); - - describe('callback for closing and opening', () => { - it('callback on open', (done) => { - const onOpen = () => { - expect(balloon.isOpen).toBe(true); - done(); - }; - const balloon = new MMapBalloonMarker({ - show: true, - coordinates: CENTER, - content: createPopupContent, - onOpen - }); - map.addChild(balloon); - }); - it('callback on close', (done) => { - const onClose = () => { - expect(balloon.isOpen).toBe(false); - done(); - }; - const balloon = new MMapBalloonMarker({ - show: true, - coordinates: CENTER, - content: createPopupContent, - onClose - }); - map.addChild(balloon); - balloon.update({show: false}); - }); - }); - - describe('change popup position', () => { - it('initial default position', () => { - const balloon = new MMapBalloonMarker({coordinates: CENTER, content: createPopupContent}); - map.addChild(balloon); - - expect( - document.querySelector('.mappable--balloon-marker.mappable--balloon-marker__position-top') - ).not.toBeNull(); - }); - it('initial position', () => { - const balloon = new MMapBalloonMarker({position: 'left', coordinates: CENTER, content: createPopupContent}); - map.addChild(balloon); - - expect( - document.querySelector('.mappable--balloon-marker.mappable--balloon-marker__position-left') - ).not.toBeNull(); - }); - it('change position props', () => { - const balloon = new MMapBalloonMarker({coordinates: CENTER, content: createPopupContent}); - map.addChild(balloon); - - balloon.update({position: 'top'}); - expect( - document.querySelector('.mappable--balloon-marker.mappable--balloon-marker__position-top') - ).not.toBeNull(); - - balloon.update({position: 'bottom'}); - expect( - document.querySelector('.mappable--balloon-marker.mappable--balloon-marker__position-bottom') - ).not.toBeNull(); - - balloon.update({position: 'left'}); - expect( - document.querySelector('.mappable--balloon-marker.mappable--balloon-marker__position-left') - ).not.toBeNull(); - - balloon.update({position: 'right'}); - expect( - document.querySelector('.mappable--balloon-marker.mappable--balloon-marker__position-right') - ).not.toBeNull(); - }); - it('change combined position props', () => { - const balloon = new MMapBalloonMarker({coordinates: CENTER, content: createPopupContent}); - map.addChild(balloon); - - balloon.update({position: 'top left'}); - expect( - document.querySelector( - '.mappable--balloon-marker.mappable--balloon-marker__position-top.mappable--balloon-marker__position-left' - ) - ).not.toBeNull(); - balloon.update({position: 'top right'}); - expect( - document.querySelector( - '.mappable--balloon-marker.mappable--balloon-marker__position-top.mappable--balloon-marker__position-right' - ) - ).not.toBeNull(); - - balloon.update({position: 'bottom left'}); - expect( - document.querySelector( - '.mappable--balloon-marker.mappable--balloon-marker__position-bottom.mappable--balloon-marker__position-left' - ) - ).not.toBeNull(); - balloon.update({position: 'bottom right'}); - expect( - document.querySelector( - '.mappable--balloon-marker.mappable--balloon-marker__position-bottom.mappable--balloon-marker__position-right' - ) - ).not.toBeNull(); - }); - }); -}); - -const createPopupContent = () => { - const popup = document.createElement('div'); - popup.classList.add('test-popup'); - return popup; -}; diff --git a/src/markers/MMapBalloonMarker/react/index.tsx b/src/markers/MMapBalloonMarker/react/index.tsx deleted file mode 100644 index 2449acd..0000000 --- a/src/markers/MMapBalloonMarker/react/index.tsx +++ /dev/null @@ -1,43 +0,0 @@ -import type TReact from 'react'; -import {CustomReactify, Prettify, OverrideProps} from '@mappable-world/mappable-types/reactify/reactify'; -import {MMapEntity} from '@mappable-world/mappable-types'; -import {MMapBalloonMarker as MMapBalloonMarkerI, MMapBalloonMarkerProps} from '../'; - -type MMapBalloonMarkerReactifiedProps = Prettify< - OverrideProps< - MMapBalloonMarkerProps, - { - /** The function of creating balloon content */ - content: () => TReact.ReactElement; - } - > ->; - -type MMapBalloonMarkerR = TReact.ForwardRefExoticComponent< - Prettify>> ->; - -export const MMapBalloonMarkerReactifyOverride: CustomReactify = ( - MMapBalloonMarkerI, - {reactify, React, ReactDOM} -) => { - const MMapBalloonMarkerReactified = reactify.entity(MMapBalloonMarkerI); - - const MMapBalloonMarker = React.forwardRef((props, ref) => { - const [balloonElement] = React.useState(document.createElement('mappable')); - const [content, setContent] = React.useState(); - - const balloon = React.useMemo(() => { - setContent(props.content()); - return () => balloonElement; - }, [props.content, balloonElement]); - - return ( - <> - - {ReactDOM.createPortal(content, balloonElement)} - - ); - }); - return MMapBalloonMarker; -}; diff --git a/src/markers/MMapDefaultPopupMarker/MMapDefaultPopupMarker.test.ts b/src/markers/MMapDefaultPopupMarker/MMapDefaultPopupMarker.test.ts index 4cbae80..72e8f83 100644 --- a/src/markers/MMapDefaultPopupMarker/MMapDefaultPopupMarker.test.ts +++ b/src/markers/MMapDefaultPopupMarker/MMapDefaultPopupMarker.test.ts @@ -1,5 +1,5 @@ import {MMap} from '@mappable-world/mappable-types'; -import {createContainer, CENTER} from '../../../tests/common'; +import {CENTER, createContainer} from '../../../tests/common'; import {MMapDefaultPopupMarker} from './'; describe('MMapDefaultPopupMarker', () => { @@ -88,4 +88,4 @@ describe('MMapDefaultPopupMarker', () => { }); }); -const BASE_SELECTOR = '.mappable--balloon-marker .mappable--default-popup'; +const BASE_SELECTOR = '.mappable--popup-marker .mappable--default-popup'; diff --git a/src/markers/MMapDefaultPopupMarker/index.ts b/src/markers/MMapDefaultPopupMarker/index.ts index 04957de..8ae96c4 100644 --- a/src/markers/MMapDefaultPopupMarker/index.ts +++ b/src/markers/MMapDefaultPopupMarker/index.ts @@ -1,10 +1,10 @@ -import {MMapBalloonContentProps, MMapBalloonMarker, MMapBalloonMarkerProps} from '../MMapBalloonMarker'; +import {MMapPopupContentProps, MMapPopupMarker, MMapPopupMarkerProps} from '../MMapPopupMarker'; import {MMapDefaultPopupMarkerVuefyOptions} from './vue'; import closeSVG from './close.svg'; import './index.css'; -export type MMapDefaultPopupMarkerProps = Omit & { +export type MMapDefaultPopupMarkerProps = Omit & { /** Displayed title in popup header */ title?: string; /** Displayed description */ @@ -25,7 +25,7 @@ export type MMapDefaultPopupMarkerProps = Omit { static [mappable.optionsKeyVuefy] = MMapDefaultPopupMarkerVuefyOptions; - private _balloon: MMapBalloonMarker; + private _popup: MMapPopupMarker; private _element: HTMLElement; public get isOpen() { - return this._balloon.isOpen; + return this._popup.isOpen; } protected _onAttach(): void { @@ -49,7 +49,7 @@ export class MMapDefaultPopupMarker extends mappable.MMapComplexEntity { @@ -61,7 +61,7 @@ export class MMapDefaultPopupMarker extends mappable.MMapComplexEntity this._updateTheme(), {immediate: true}); } @@ -74,10 +74,10 @@ export class MMapDefaultPopupMarker extends mappable.MMapComplexEntity this.__createDefaultPopup()}); + this._popup.update({content: () => this.__createDefaultPopup()}); } - this._balloon.update(this._props); + this._popup.update(this._props); } private _updateTheme() { @@ -85,7 +85,7 @@ export class MMapDefaultPopupMarker extends mappable.MMapComplexEntity { + private __createDefaultPopup: MMapPopupContentProps = () => { const {title, description, action} = this._props; this._element = document.createElement('mappable'); this._element.classList.add('mappable--default-popup'); @@ -104,7 +104,7 @@ export class MMapDefaultPopupMarker extends mappable.MMapComplexEntity this._balloon.update({show: false})); + closeButton.addEventListener('click', () => this._popup.update({show: false})); popupHeaderElement.appendChild(closeButton); if (description) { diff --git a/src/markers/MMapPopupMarker/MMapPopupMarker.test.ts b/src/markers/MMapPopupMarker/MMapPopupMarker.test.ts new file mode 100644 index 0000000..e919986 --- /dev/null +++ b/src/markers/MMapPopupMarker/MMapPopupMarker.test.ts @@ -0,0 +1,165 @@ +import {MMap} from '@mappable-world/mappable-types'; +import {createContainer, CENTER} from '../../../tests/common'; +import {MMapPopupMarker} from './'; + +describe('MMapPopupMarker', () => { + let map: MMap; + let container: HTMLElement; + + beforeEach(() => { + container = createContainer(); + document.body.append(container); + map = new mappable.MMap(container, {location: {center: CENTER, zoom: 0}}); + map.addChild(new mappable.MMapDefaultFeaturesLayer({})); + }); + + afterEach(() => { + map.destroy(); + }); + + it('add on map', () => { + const popup = new MMapPopupMarker({coordinates: CENTER, content: createPopupContent}); + map.addChild(popup); + + expect(document.querySelector('.mappable--popup-marker')).not.toBeNull(); + expect(document.querySelector('.mappable--popup-marker .test-popup')).not.toBeNull(); + }); + it('changing show props', () => { + const popup = new MMapPopupMarker({show: true, coordinates: CENTER, content: createPopupContent}); + map.addChild(popup); + + const popupMarkerElement = document.querySelector('.mappable--popup-marker'); + expect(popupMarkerElement).not.toBeNull(); + + expect(popup.isOpen).toBe(true); + expect(document.querySelector('.mappable--popup-marker.mappable--popup-marker__hide')).toBeNull(); + + popup.update({show: false}); + expect(popup.isOpen).toBe(false); + expect(document.querySelector('.mappable--popup-marker.mappable--popup-marker__hide')).not.toBeNull(); + + popup.update({show: true}); + expect(popup.isOpen).toBe(true); + expect(document.querySelector('.mappable--popup-marker.mappable--popup-marker__hide')).toBeNull(); + }); + + it('offset props', () => { + const popup = new MMapPopupMarker({offset: 12, coordinates: CENTER, content: createPopupContent}); + map.addChild(popup); + + const popupMarkerElement = document.querySelector('.mappable--popup-marker'); + expect(popupMarkerElement.style.getPropertyValue('--mappable-default-offset')).toBe('12px'); + + popup.update({offset: 24}); + expect(popupMarkerElement.style.getPropertyValue('--mappable-default-offset')).toBe('24px'); + }); + + describe('callback for closing and opening', () => { + it('callback on open', (done) => { + const onOpen = () => { + expect(popup.isOpen).toBe(true); + done(); + }; + const popup = new MMapPopupMarker({ + show: true, + coordinates: CENTER, + content: createPopupContent, + onOpen + }); + map.addChild(popup); + }); + it('callback on close', (done) => { + const onClose = () => { + expect(popup.isOpen).toBe(false); + done(); + }; + const popup = new MMapPopupMarker({ + show: true, + coordinates: CENTER, + content: createPopupContent, + onClose + }); + map.addChild(popup); + popup.update({show: false}); + }); + }); + + describe('change popup position', () => { + it('initial default position', () => { + const popup = new MMapPopupMarker({coordinates: CENTER, content: createPopupContent}); + map.addChild(popup); + + expect( + document.querySelector('.mappable--popup-marker.mappable--popup-marker__position-top') + ).not.toBeNull(); + }); + it('initial position', () => { + const popup = new MMapPopupMarker({position: 'left', coordinates: CENTER, content: createPopupContent}); + map.addChild(popup); + + expect( + document.querySelector('.mappable--popup-marker.mappable--popup-marker__position-left') + ).not.toBeNull(); + }); + it('change position props', () => { + const popup = new MMapPopupMarker({coordinates: CENTER, content: createPopupContent}); + map.addChild(popup); + + popup.update({position: 'top'}); + expect( + document.querySelector('.mappable--popup-marker.mappable--popup-marker__position-top') + ).not.toBeNull(); + + popup.update({position: 'bottom'}); + expect( + document.querySelector('.mappable--popup-marker.mappable--popup-marker__position-bottom') + ).not.toBeNull(); + + popup.update({position: 'left'}); + expect( + document.querySelector('.mappable--popup-marker.mappable--popup-marker__position-left') + ).not.toBeNull(); + + popup.update({position: 'right'}); + expect( + document.querySelector('.mappable--popup-marker.mappable--popup-marker__position-right') + ).not.toBeNull(); + }); + it('change combined position props', () => { + const popup = new MMapPopupMarker({coordinates: CENTER, content: createPopupContent}); + map.addChild(popup); + + popup.update({position: 'top left'}); + expect( + document.querySelector( + '.mappable--popup-marker.mappable--popup-marker__position-top.mappable--popup-marker__position-left' + ) + ).not.toBeNull(); + popup.update({position: 'top right'}); + expect( + document.querySelector( + '.mappable--popup-marker.mappable--popup-marker__position-top.mappable--popup-marker__position-right' + ) + ).not.toBeNull(); + + popup.update({position: 'bottom left'}); + expect( + document.querySelector( + '.mappable--popup-marker.mappable--popup-marker__position-bottom.mappable--popup-marker__position-left' + ) + ).not.toBeNull(); + popup.update({position: 'bottom right'}); + expect( + document.querySelector( + '.mappable--popup-marker.mappable--popup-marker__position-bottom.mappable--popup-marker__position-right' + ) + ).not.toBeNull(); + }); + }); +}); + +const createPopupContent = () => { + const popup = document.createElement('div'); + popup.classList.add('test-popup'); + return popup; +}; diff --git a/src/markers/MMapBalloonMarker/index.css b/src/markers/MMapPopupMarker/index.css similarity index 58% rename from src/markers/MMapBalloonMarker/index.css rename to src/markers/MMapPopupMarker/index.css index 50dc5d4..a1658a3 100644 --- a/src/markers/MMapBalloonMarker/index.css +++ b/src/markers/MMapPopupMarker/index.css @@ -1,4 +1,4 @@ -@keyframes mappable--balloon-marker-show-top { +@keyframes mappable--popup-marker-show-top { from { transform: translateY(12px); opacity: 0; @@ -9,7 +9,7 @@ } } -@keyframes mappable--balloon-marker-show-bottom { +@keyframes mappable--popup-marker-show-bottom { from { transform: translateY(-12px); opacity: 0; @@ -20,7 +20,7 @@ } } -@keyframes mappable--balloon-marker-show-left { +@keyframes mappable--popup-marker-show-left { from { transform: translateX(12px); opacity: 0; @@ -31,7 +31,7 @@ } } -@keyframes mappable--balloon-marker-show-right { +@keyframes mappable--popup-marker-show-right { from { transform: translateX(-12px); opacity: 0; @@ -42,7 +42,7 @@ } } -.mappable--balloon-marker { +.mappable--popup-marker { --mappable-default-tail-height: 12px; --mappable-default-tail-width: 16px; --mappable-default-border-radius: 12px; @@ -51,22 +51,22 @@ var(--mappable-default-tail-height) + var(--mappable-default-offset) ); - --mappable-default-balloon-tail-transform-top: translate(-50%, calc(-100% - var(--mappable-default-offset))) + --mappable-default-popup-tail-transform-top: translate(-50%, calc(-100% - var(--mappable-default-offset))) rotate(180deg); - --mappable-default-balloon-tail-transform-bottom: translate(-50%, var(--mappable-default-offset)); + --mappable-default-popup-tail-transform-bottom: translate(-50%, var(--mappable-default-offset)); position: absolute; - &.mappable--balloon-marker__hide { + &.mappable--popup-marker__hide { display: none; } } -.mappable--balloon-marker svg { +.mappable--popup-marker svg { display: block; } -.mappable--balloon-marker_container { +.mappable--popup-marker_container { width: max-content; max-width: 500px; max-height: 600px; @@ -86,18 +86,18 @@ overflow: hidden; text-overflow: ellipsis; - &.mappable--balloon-marker__dark { + &.mappable--popup-marker__dark { background-color: #272729; color: #c8c9cc; } } -.mappable--balloon-marker_tail { +.mappable--popup-marker_tail { display: block; position: absolute; color: #fff; - &.mappable--balloon-marker__dark { + &.mappable--popup-marker__dark { color: #272729; } @@ -107,94 +107,94 @@ } /* positions */ -.mappable--balloon-marker__position-top { - animation: mappable--balloon-marker-show-top 200ms ease-out !important; +.mappable--popup-marker__position-top { + animation: mappable--popup-marker-show-top 200ms ease-out !important; - .mappable--balloon-marker_container { + .mappable--popup-marker_container { transform: translate(-50%, calc(-100% - var(--mappable-default-tail-height-and-offset))); } - .mappable--balloon-marker_tail { - transform: var(--mappable-default-balloon-tail-transform-top); + .mappable--popup-marker_tail { + transform: var(--mappable-default-popup-tail-transform-top); } /* top left */ - &.mappable--balloon-marker__position-left { - .mappable--balloon-marker_container { + &.mappable--popup-marker__position-left { + .mappable--popup-marker_container { transform: translate( calc(-100% + var(--mappable-default-border-radius) + var(--mappable-default-tail-width) / 2), calc(-100% - var(--mappable-default-tail-height-and-offset)) ); } - .mappable--balloon-marker_tail { - transform: var(--mappable-default-balloon-tail-transform-top); + .mappable--popup-marker_tail { + transform: var(--mappable-default-popup-tail-transform-top); } } /* top right */ - &.mappable--balloon-marker__position-right { - .mappable--balloon-marker_container { + &.mappable--popup-marker__position-right { + .mappable--popup-marker_container { transform: translate( calc(-1 * var(--mappable-default-border-radius) - var(--mappable-default-tail-width) / 2), calc(-100% - var(--mappable-default-tail-height-and-offset)) ); } - .mappable--balloon-marker_tail { - transform: var(--mappable-default-balloon-tail-transform-top); + .mappable--popup-marker_tail { + transform: var(--mappable-default-popup-tail-transform-top); } } } -.mappable--balloon-marker__position-bottom { - animation: mappable--balloon-marker-show-bottom 200ms ease-out !important; +.mappable--popup-marker__position-bottom { + animation: mappable--popup-marker-show-bottom 200ms ease-out !important; - .mappable--balloon-marker_container { + .mappable--popup-marker_container { transform: translate(-50%, var(--mappable-default-tail-height-and-offset)); } - .mappable--balloon-marker_tail { - transform: var(--mappable-default-balloon-tail-transform-bottom); + .mappable--popup-marker_tail { + transform: var(--mappable-default-popup-tail-transform-bottom); } /* bottom left */ - &.mappable--balloon-marker__position-left { - .mappable--balloon-marker_container { + &.mappable--popup-marker__position-left { + .mappable--popup-marker_container { transform: translate( calc(-100% + var(--mappable-default-border-radius) + var(--mappable-default-tail-width) / 2), var(--mappable-default-tail-height-and-offset) ); } - .mappable--balloon-marker_tail { - transform: var(--mappable-default-balloon-tail-transform-bottom); + .mappable--popup-marker_tail { + transform: var(--mappable-default-popup-tail-transform-bottom); } } /* bottom right */ - &.mappable--balloon-marker__position-right { - .mappable--balloon-marker_container { + &.mappable--popup-marker__position-right { + .mappable--popup-marker_container { transform: translate( calc(-1 * var(--mappable-default-border-radius) - var(--mappable-default-tail-width) / 2), var(--mappable-default-tail-height-and-offset) ); } - .mappable--balloon-marker_tail { - transform: var(--mappable-default-balloon-tail-transform-bottom); + .mappable--popup-marker_tail { + transform: var(--mappable-default-popup-tail-transform-bottom); } } } -.mappable--balloon-marker__position-left { - animation: mappable--balloon-marker-show-left 200ms ease-out; +.mappable--popup-marker__position-left { + animation: mappable--popup-marker-show-left 200ms ease-out; - .mappable--balloon-marker_container { + .mappable--popup-marker_container { transform: translate(calc(-100% - var(--mappable-default-tail-height-and-offset)), -50%); } - .mappable--balloon-marker_tail { + .mappable--popup-marker_tail { transform: translate(calc(-100% - var(--mappable-default-offset)), -50%) rotate(90deg); } } -.mappable--balloon-marker__position-right { - animation: mappable--balloon-marker-show-right 200ms ease-out; +.mappable--popup-marker__position-right { + animation: mappable--popup-marker-show-right 200ms ease-out; - .mappable--balloon-marker_container { + .mappable--popup-marker_container { transform: translate(var(--mappable-default-tail-height-and-offset), -50%); } - .mappable--balloon-marker_tail { + .mappable--popup-marker_tail { transform: translate(var(--mappable-default-offset), -50%) rotate(-90deg); } } diff --git a/src/markers/MMapBalloonMarker/index.ts b/src/markers/MMapPopupMarker/index.ts similarity index 53% rename from src/markers/MMapBalloonMarker/index.ts rename to src/markers/MMapPopupMarker/index.ts index 12cf08e..b73f6e4 100644 --- a/src/markers/MMapBalloonMarker/index.ts +++ b/src/markers/MMapPopupMarker/index.ts @@ -1,32 +1,32 @@ import {MMapMarker, MMapMarkerProps} from '@mappable-world/mappable-types'; -import {MMapBalloonMarkerReactifyOverride} from './react'; -import {MMapBalloonMarkerVuefyOptions, MMapBalloonMarkerVuefyOverride} from './vue'; +import {MMapPopupMarkerReactifyOverride} from './react'; +import {MMapPopupMarkerVuefyOptions, MMapPopupMarkerVuefyOverride} from './vue'; import './index.css'; import tailSVG from './tail.svg'; type VerticalPosition = 'top' | 'bottom'; type HorizontalPosition = 'left' | 'right'; -export type MMapBalloonPositionProps = +export type MMapPopupPositionProps = | VerticalPosition | HorizontalPosition | `${VerticalPosition} ${HorizontalPosition}` | `${HorizontalPosition} ${VerticalPosition}`; -export type MMapBalloonContentProps = () => HTMLElement; +export type MMapPopupContentProps = () => HTMLElement; -export type MMapBalloonMarkerProps = MMapMarkerProps & { - /** The function of creating balloon content */ - content: MMapBalloonContentProps; - /** The position of the balloon in relation to the point it is pointing to */ - position?: MMapBalloonPositionProps; - /** The offset in pixels between the balloon pointer and the point it is pointing to. */ +export type MMapPopupMarkerProps = MMapMarkerProps & { + /** The function of creating popup content */ + content: MMapPopupContentProps; + /** The position of the popup in relation to the point it is pointing to */ + position?: MMapPopupPositionProps; + /** The offset in pixels between the popup pointer and the point it is pointing to. */ offset?: number; - /** Hide or show balloon on map */ + /** Hide or show popup on map */ show?: boolean; - /** Balloon closing callback */ + /** Popup closing callback */ onClose?: () => void; - /** Balloon opening callback */ + /** Popup opening callback */ onOpen?: () => void; }; @@ -34,70 +34,70 @@ const defaultProps = Object.freeze({position: 'top', offset: 0, show: true}); type DefaultProps = typeof defaultProps; /** - * `MMapBalloonMarker` is a balloon (popup) with customized content. + * `MMapPopupMarker` is a popup with customized content. * @example * ```js - * const balloon = new MMapBalloonMarker({ + * const popup = new MMapPopupMarker({ * content: (close) => createPopupContentHTMLElement(close), * position: 'top', * onOpen:() => console.log('open'), * onClose:() => console.log('close'), * // support MMapMarker props - * coordinates: BALLOON_COORD, + * coordinates: POPUP_COORD, * draggable: true, * }); - * map.addChild(balloon); + * map.addChild(popup); * ``` */ -export class MMapBalloonMarker extends mappable.MMapComplexEntity { +export class MMapPopupMarker extends mappable.MMapComplexEntity { static defaultProps = defaultProps; - static [mappable.overrideKeyReactify] = MMapBalloonMarkerReactifyOverride; - static [mappable.overrideKeyVuefy] = MMapBalloonMarkerVuefyOverride; - static [mappable.optionsKeyVuefy] = MMapBalloonMarkerVuefyOptions; + static [mappable.overrideKeyReactify] = MMapPopupMarkerReactifyOverride; + static [mappable.overrideKeyVuefy] = MMapPopupMarkerVuefyOverride; + static [mappable.optionsKeyVuefy] = MMapPopupMarkerVuefyOptions; public get isOpen() { return this._props.show; } private _markerElement: HTMLElement; - private _balloonContainer: HTMLElement; - private _balloonTail: HTMLElement; + private _popupContainer: HTMLElement; + private _popupTail: HTMLElement; private _marker: MMapMarker; - private _togglePopup(forceShowBalloon?: boolean): void { - let openBalloon = !this._props.show; - if (forceShowBalloon !== undefined) { - openBalloon = forceShowBalloon; + private _togglePopup(forceShowPopup?: boolean): void { + let openPopup = !this._props.show; + if (forceShowPopup !== undefined) { + openPopup = forceShowPopup; } - this._markerElement.classList.toggle('mappable--balloon-marker__hide', !openBalloon); + this._markerElement.classList.toggle('mappable--popup-marker__hide', !openPopup); - if (openBalloon) { + if (openPopup) { this._props.onOpen?.(); } else { this._props.onClose?.(); } - this._props.show = openBalloon; + this._props.show = openPopup; } protected _onAttach(): void { this._markerElement = document.createElement('mappable'); - this._markerElement.classList.add('mappable--balloon-marker'); + this._markerElement.classList.add('mappable--popup-marker'); - this._balloonContainer = document.createElement('mappable'); - this._balloonContainer.classList.add('mappable--balloon-marker_container'); - this._balloonContainer.appendChild(this._props.content()); + this._popupContainer = document.createElement('mappable'); + this._popupContainer.classList.add('mappable--popup-marker_container'); + this._popupContainer.appendChild(this._props.content()); - this._balloonTail = document.createElement('mappable'); - this._balloonTail.classList.add('mappable--balloon-marker_tail'); - this._balloonTail.innerHTML = tailSVG; + this._popupTail = document.createElement('mappable'); + this._popupTail.classList.add('mappable--popup-marker_tail'); + this._popupTail.innerHTML = tailSVG; this._togglePopup(this._props.show); this._updatePosition(); this._updateOffset(); - this._markerElement.appendChild(this._balloonContainer); - this._markerElement.appendChild(this._balloonTail); + this._markerElement.appendChild(this._popupContainer); + this._markerElement.appendChild(this._popupTail); this._marker = new mappable.MMapMarker(this._props, this._markerElement); this.addChild(this._marker); @@ -107,7 +107,7 @@ export class MMapBalloonMarker extends mappable.MMapComplexEntity): void { + protected _onUpdate(propsDiff: Partial): void { if (propsDiff.position !== undefined) { this._updatePosition(); } @@ -116,8 +116,8 @@ export class MMapBalloonMarker extends mappable.MMapComplexEntity TReact.ReactElement; + } + > +>; + +type MMapPopupMarkerR = TReact.ForwardRefExoticComponent< + Prettify>> +>; + +export const MMapPopupMarkerReactifyOverride: CustomReactify = ( + MMapPopupMarkerI, + {reactify, React, ReactDOM} +) => { + const MMapPopupMarkerReactified = reactify.entity(MMapPopupMarkerI); + + const MMapPopupMarker = React.forwardRef((props, ref) => { + const [popupElement] = React.useState(document.createElement('mappable')); + const [content, setContent] = React.useState(); + + const popup = React.useMemo(() => { + setContent(props.content()); + return () => popupElement; + }, [props.content, popupElement]); + + return ( + <> + + {ReactDOM.createPortal(content, popupElement)} + + ); + }); + return MMapPopupMarker; +}; diff --git a/src/markers/MMapBalloonMarker/tail.svg b/src/markers/MMapPopupMarker/tail.svg similarity index 100% rename from src/markers/MMapBalloonMarker/tail.svg rename to src/markers/MMapPopupMarker/tail.svg diff --git a/src/markers/MMapBalloonMarker/vue/index.ts b/src/markers/MMapPopupMarker/vue/index.ts similarity index 65% rename from src/markers/MMapBalloonMarker/vue/index.ts rename to src/markers/MMapPopupMarker/vue/index.ts index f16d78e..42a6c22 100644 --- a/src/markers/MMapBalloonMarker/vue/index.ts +++ b/src/markers/MMapPopupMarker/vue/index.ts @@ -1,9 +1,9 @@ import {MMapFeatureProps, MMapMarkerEventHandler} from '@mappable-world/mappable-types'; import {CustomVuefyFn, CustomVuefyOptions} from '@mappable-world/mappable-types/modules/vuefy'; import type TVue from '@vue/runtime-core'; -import {MMapBalloonContentProps, MMapBalloonMarker, MMapBalloonMarkerProps, MMapBalloonPositionProps} from '../'; +import {MMapPopupContentProps, MMapPopupMarker, MMapPopupMarkerProps, MMapPopupPositionProps} from '../'; -export const MMapBalloonMarkerVuefyOptions: CustomVuefyOptions = { +export const MMapPopupMarkerVuefyOptions: CustomVuefyOptions = { props: { coordinates: {type: Object, required: true}, source: String, @@ -22,48 +22,44 @@ export const MMapBalloonMarkerVuefyOptions: CustomVuefyOptions, onClick: Function as TVue.PropType, onFastClick: Function as TVue.PropType, - content: {type: Function as TVue.PropType, required: true}, - position: {type: String as TVue.PropType}, + content: {type: Function as TVue.PropType, required: true}, + position: {type: String as TVue.PropType}, offset: {type: Number, default: 0}, show: {type: Boolean, default: true}, - onClose: {type: Function as TVue.PropType}, - onOpen: {type: Function as TVue.PropType} + onClose: {type: Function as TVue.PropType}, + onOpen: {type: Function as TVue.PropType} } }; -type MMapBalloonMarkerSlots = { +type MMapPopupMarkerSlots = { content: void; }; -export const MMapBalloonMarkerVuefyOverride: CustomVuefyFn = ( - MMapBalloonMarkerI, - props, - {vuefy, Vue} -) => { - const MMapBalloonMarkerV = vuefy.entity(MMapBalloonMarkerI); +export const MMapPopupMarkerVuefyOverride: CustomVuefyFn = (MMapPopupMarkerI, props, {vuefy, Vue}) => { + const MMapPopupMarkerV = vuefy.entity(MMapPopupMarkerI); const {content, ...overridedProps} = props; return Vue.defineComponent({ - name: 'MMapBalloonMarker', + name: 'MMapPopupMarker', props: overridedProps, - slots: Object as TVue.SlotsType, + slots: Object as TVue.SlotsType, setup(props, {slots, expose}) { const content: TVue.Ref = Vue.ref(null); const popupHTMLElement = document.createElement('mappable'); - const markerRef = Vue.ref<{entity: MMapBalloonMarker} | null>(null); + const markerRef = Vue.ref<{entity: MMapPopupMarker} | null>(null); const markerEntity = Vue.computed(() => markerRef.value?.entity); - const balloon = Vue.computed(() => { + const popup = Vue.computed(() => { content.value = slots.content?.(); return () => popupHTMLElement; }); expose({entity: markerEntity}); return () => Vue.h( - MMapBalloonMarkerV, + MMapPopupMarkerV, { ...props, - content: balloon.value, + content: popup.value, ref: markerRef }, () => Vue.h(Vue.Teleport, {to: popupHTMLElement}, [content.value]) diff --git a/src/markers/MMapTooltipMarker/MMapTooltipMarker.test.ts b/src/markers/MMapTextPopupMarker/MMapTextPopupMarker.test.ts similarity index 81% rename from src/markers/MMapTooltipMarker/MMapTooltipMarker.test.ts rename to src/markers/MMapTextPopupMarker/MMapTextPopupMarker.test.ts index 32f3150..3553958 100644 --- a/src/markers/MMapTooltipMarker/MMapTooltipMarker.test.ts +++ b/src/markers/MMapTextPopupMarker/MMapTextPopupMarker.test.ts @@ -1,5 +1,5 @@ import {MMap} from '@mappable-world/mappable-types'; -import {createContainer, CENTER} from '../../../tests/common'; +import {CENTER, createContainer} from '../../../tests/common'; import {MMapTooltipMarker} from './'; describe('MMapTooltipMarker', () => { @@ -21,13 +21,13 @@ describe('MMapTooltipMarker', () => { const tooltip = new MMapTooltipMarker({coordinates: CENTER, content: 'Tooltip'}); map.addChild(tooltip); - expect(document.querySelector('.mappable--balloon-marker .mappable--default-tooltip')).not.toBeNull(); + expect(document.querySelector('.mappable--popup-marker .mappable--default-tooltip')).not.toBeNull(); }); it('change content props', () => { const tooltip = new MMapTooltipMarker({coordinates: CENTER, content: 'Tooltip'}); map.addChild(tooltip); const tooltipElement = document.querySelector( - '.mappable--balloon-marker .mappable--default-tooltip' + '.mappable--popup-marker .mappable--default-tooltip' ); expect(tooltipElement.textContent).toBe('Tooltip'); diff --git a/src/markers/MMapTooltipMarker/index.css b/src/markers/MMapTextPopupMarker/index.css similarity index 100% rename from src/markers/MMapTooltipMarker/index.css rename to src/markers/MMapTextPopupMarker/index.css diff --git a/src/markers/MMapTooltipMarker/index.ts b/src/markers/MMapTextPopupMarker/index.ts similarity index 73% rename from src/markers/MMapTooltipMarker/index.ts rename to src/markers/MMapTextPopupMarker/index.ts index d79a585..c47c035 100644 --- a/src/markers/MMapTooltipMarker/index.ts +++ b/src/markers/MMapTextPopupMarker/index.ts @@ -1,8 +1,8 @@ -import {MMapBalloonMarker, MMapBalloonMarkerProps} from '../MMapBalloonMarker'; -import {MMapTooltipMarkerVuefyOptions} from './vue'; +import {MMapPopupMarker, MMapPopupMarkerProps} from '../MMapPopupMarker'; import './index.css'; +import {MMapTooltipMarkerVuefyOptions} from './vue'; -export type MMapTooltipMarkerProps = Omit & { +export type MMapTooltipMarkerProps = Omit & { /** The text content that the tooltip will display */ content: string; }; @@ -15,7 +15,7 @@ export type MMapTooltipMarkerProps = Omit { static [mappable.optionsKeyVuefy] = MMapTooltipMarkerVuefyOptions; - private _balloon: MMapBalloonMarker; + private _popup: MMapPopupMarker; private _element: HTMLElement; protected _onAttach(): void { - this._balloon = new MMapBalloonMarker({...this._props, content: this.__createTooltip}); - this.addChild(this._balloon); + this._popup = new MMapPopupMarker({...this._props, content: this.__createTooltip}); + this.addChild(this._popup); } protected _onUpdate(propsDiff: Partial): void { @@ -36,7 +36,7 @@ export class MMapTooltipMarker extends mappable.MMapComplexEntity { diff --git a/src/markers/MMapTooltipMarker/vue/index.ts b/src/markers/MMapTextPopupMarker/vue/index.ts similarity index 100% rename from src/markers/MMapTooltipMarker/vue/index.ts rename to src/markers/MMapTextPopupMarker/vue/index.ts diff --git a/src/markers/index.ts b/src/markers/index.ts index 4043a62..38fa9a0 100644 --- a/src/markers/index.ts +++ b/src/markers/index.ts @@ -1,4 +1,4 @@ -export {MMapBalloonMarker, MMapBalloonMarkerProps, MMapBalloonPositionProps} from './MMapBalloonMarker'; export * from './MMapDefaultMarker'; export {MMapDefaultPopupMarker, MMapDefaultPopupMarkerProps} from './MMapDefaultPopupMarker'; -export {MMapTooltipMarker, MMapTooltipMarkerProps} from './MMapTooltipMarker'; +export {MMapPopupMarker, MMapPopupMarkerProps, MMapPopupPositionProps} from './MMapPopupMarker'; +export {MMapTooltipMarker, MMapTooltipMarkerProps} from './MMapTextPopupMarker'; From e2ca0f33bbe57ad74d2a3b97e28f07155e9e838a Mon Sep 17 00:00:00 2001 From: Matthew Kalinin Date: Wed, 8 May 2024 15:48:34 +0300 Subject: [PATCH 45/48] Rename tooltip to text-popup --- example/default-tooltip/common.ts | 2 +- example/default-tooltip/react/index.tsx | 10 +++---- example/default-tooltip/vanilla/index.ts | 24 ++++++++-------- example/default-tooltip/vue/index.ts | 14 +++++----- .../MMapTextPopupMarker.test.ts | 24 ++++++++-------- src/markers/MMapTextPopupMarker/index.css | 2 +- src/markers/MMapTextPopupMarker/index.ts | 28 +++++++++---------- src/markers/MMapTextPopupMarker/vue/index.ts | 6 ++-- src/markers/index.ts | 2 +- 9 files changed, 56 insertions(+), 56 deletions(-) diff --git a/example/default-tooltip/common.ts b/example/default-tooltip/common.ts index ee8a754..6afeb8e 100644 --- a/example/default-tooltip/common.ts +++ b/example/default-tooltip/common.ts @@ -2,4 +2,4 @@ import type {MMapLocationRequest, LngLat} from '@mappable-world/mappable-types'; export const CENTER: LngLat = [55.442795, 25.24107]; export const LOCATION: MMapLocationRequest = {center: CENTER, zoom: 14}; -export const TOOLTIP_TEXT = 'Default tooltip'; +export const POPUP_TEXT = 'Default text popup'; diff --git a/example/default-tooltip/react/index.tsx b/example/default-tooltip/react/index.tsx index 1f661b1..ebab76a 100644 --- a/example/default-tooltip/react/index.tsx +++ b/example/default-tooltip/react/index.tsx @@ -1,5 +1,5 @@ -import {MMapTooltipMarkerProps} from '../../src'; -import {CENTER, LOCATION, TOOLTIP_TEXT} from '../common'; +import type {MMapTextPopupMarkerProps} from '../../src'; +import {CENTER, LOCATION, POPUP_TEXT} from '../common'; window.map = null; @@ -13,7 +13,7 @@ async function main() { const {useState, useCallback} = React; - const {MMapTooltipMarker} = reactify.module(await mappable.import('@mappable-world/mappable-default-ui-theme')); + const {MMapTextPopupMarker} = reactify.module(await mappable.import('@mappable-world/mappable-default-ui-theme')); ReactDOM.render( @@ -24,7 +24,7 @@ async function main() { function App() { const [location] = useState(LOCATION); - const [position, setPosition] = useState(undefined); + const [position, setPosition] = useState(undefined); const positionLeft = useCallback(() => setPosition('left'), []); const positionLeftTop = useCallback(() => setPosition('left top'), []); @@ -49,7 +49,7 @@ async function main() { - + ); } diff --git a/example/default-tooltip/vanilla/index.ts b/example/default-tooltip/vanilla/index.ts index 9f54205..802f53c 100644 --- a/example/default-tooltip/vanilla/index.ts +++ b/example/default-tooltip/vanilla/index.ts @@ -1,4 +1,4 @@ -import {CENTER, LOCATION, TOOLTIP_TEXT} from '../common'; +import {CENTER, LOCATION, POPUP_TEXT} from '../common'; window.map = null; main(); @@ -6,7 +6,7 @@ async function main() { await mappable.ready; const {MMap, MMapDefaultSchemeLayer, MMapDefaultFeaturesLayer, MMapControls, MMapControlButton} = mappable; - const {MMapTooltipMarker} = await mappable.import('@mappable-world/mappable-default-ui-theme'); + const {MMapTextPopupMarker} = await mappable.import('@mappable-world/mappable-default-ui-theme'); map = new MMap(document.getElementById('app'), {location: LOCATION}); @@ -15,17 +15,17 @@ async function main() { map.addChild( new MMapControls({position: 'top right'}, [ - new MMapControlButton({text: 'Left', onClick: () => tooltip.update({position: 'left'})}), - new MMapControlButton({text: 'Left Top', onClick: () => tooltip.update({position: 'left top'})}), - new MMapControlButton({text: 'Left Bottom', onClick: () => tooltip.update({position: 'left bottom'})}), - new MMapControlButton({text: 'Bottom', onClick: () => tooltip.update({position: 'bottom'})}), - new MMapControlButton({text: 'Top', onClick: () => tooltip.update({position: 'top'})}), - new MMapControlButton({text: 'Right Top', onClick: () => tooltip.update({position: 'right top'})}), - new MMapControlButton({text: 'Right Bottom', onClick: () => tooltip.update({position: 'right bottom'})}), - new MMapControlButton({text: 'Right', onClick: () => tooltip.update({position: 'right'})}) + new MMapControlButton({text: 'Left', onClick: () => textPopup.update({position: 'left'})}), + new MMapControlButton({text: 'Left Top', onClick: () => textPopup.update({position: 'left top'})}), + new MMapControlButton({text: 'Left Bottom', onClick: () => textPopup.update({position: 'left bottom'})}), + new MMapControlButton({text: 'Bottom', onClick: () => textPopup.update({position: 'bottom'})}), + new MMapControlButton({text: 'Top', onClick: () => textPopup.update({position: 'top'})}), + new MMapControlButton({text: 'Right Top', onClick: () => textPopup.update({position: 'right top'})}), + new MMapControlButton({text: 'Right Bottom', onClick: () => textPopup.update({position: 'right bottom'})}), + new MMapControlButton({text: 'Right', onClick: () => textPopup.update({position: 'right'})}) ]) ); - const tooltip = new MMapTooltipMarker({coordinates: CENTER, draggable: true, content: TOOLTIP_TEXT}); - map.addChild(tooltip); + const textPopup = new MMapTextPopupMarker({coordinates: CENTER, draggable: true, content: POPUP_TEXT}); + map.addChild(textPopup); } diff --git a/example/default-tooltip/vue/index.ts b/example/default-tooltip/vue/index.ts index 7f9ddf5..f9abb89 100644 --- a/example/default-tooltip/vue/index.ts +++ b/example/default-tooltip/vue/index.ts @@ -1,5 +1,5 @@ -import {MMapTooltipMarkerProps} from '../../src'; -import {CENTER, LOCATION, TOOLTIP_TEXT} from '../common'; +import type {MMapTextPopupMarkerProps} from '../../src'; +import {CENTER, LOCATION, POPUP_TEXT} from '../common'; window.map = null; @@ -11,7 +11,7 @@ async function main() { const {MMap, MMapDefaultSchemeLayer, MMapDefaultFeaturesLayer, MMapControls, MMapControlButton} = vuefy.module(mappable); - const {MMapTooltipMarker} = vuefy.module(await mappable.import('@mappable-world/mappable-default-ui-theme')); + const {MMapTextPopupMarker} = vuefy.module(await mappable.import('@mappable-world/mappable-default-ui-theme')); const app = Vue.createApp({ components: { @@ -20,13 +20,13 @@ async function main() { MMapDefaultFeaturesLayer, MMapControls, MMapControlButton, - MMapTooltipMarker + MMapTextPopupMarker }, setup() { const refMap = (ref: any) => { window.map = ref?.entity; }; - const position = Vue.ref(undefined); + const position = Vue.ref(undefined); const positionLeft = () => (position.value = 'left'); const positionLeftTop = () => (position.value = 'left top'); @@ -40,7 +40,7 @@ async function main() { return { LOCATION, CENTER, - TOOLTIP_TEXT, + POPUP_TEXT, position, refMap, positionLeft, @@ -67,7 +67,7 @@ async function main() { - + ` }); app.mount('#app'); diff --git a/src/markers/MMapTextPopupMarker/MMapTextPopupMarker.test.ts b/src/markers/MMapTextPopupMarker/MMapTextPopupMarker.test.ts index 3553958..f6a0a8b 100644 --- a/src/markers/MMapTextPopupMarker/MMapTextPopupMarker.test.ts +++ b/src/markers/MMapTextPopupMarker/MMapTextPopupMarker.test.ts @@ -1,8 +1,8 @@ import {MMap} from '@mappable-world/mappable-types'; import {CENTER, createContainer} from '../../../tests/common'; -import {MMapTooltipMarker} from './'; +import {MMapTextPopupMarker} from './'; -describe('MMapTooltipMarker', () => { +describe('MMapTextPopupMarker', () => { let map: MMap; let container: HTMLElement; @@ -18,20 +18,20 @@ describe('MMapTooltipMarker', () => { }); it('add on map', () => { - const tooltip = new MMapTooltipMarker({coordinates: CENTER, content: 'Tooltip'}); - map.addChild(tooltip); + const popup = new MMapTextPopupMarker({coordinates: CENTER, content: 'Popup'}); + map.addChild(popup); - expect(document.querySelector('.mappable--popup-marker .mappable--default-tooltip')).not.toBeNull(); + expect(document.querySelector('.mappable--popup-marker .mappable--default-text-popup')).not.toBeNull(); }); it('change content props', () => { - const tooltip = new MMapTooltipMarker({coordinates: CENTER, content: 'Tooltip'}); - map.addChild(tooltip); - const tooltipElement = document.querySelector( - '.mappable--popup-marker .mappable--default-tooltip' + const popup = new MMapTextPopupMarker({coordinates: CENTER, content: 'Popup'}); + map.addChild(popup); + const popupElement = document.querySelector( + '.mappable--popup-marker .mappable--default-text-popup' ); - expect(tooltipElement.textContent).toBe('Tooltip'); + expect(popupElement.textContent).toBe('Popup'); - tooltip.update({content: 'New content'}); - expect(tooltipElement.textContent).toBe('New content'); + popup.update({content: 'New content'}); + expect(popupElement.textContent).toBe('New content'); }); }); diff --git a/src/markers/MMapTextPopupMarker/index.css b/src/markers/MMapTextPopupMarker/index.css index 5b33e80..2175dda 100644 --- a/src/markers/MMapTextPopupMarker/index.css +++ b/src/markers/MMapTextPopupMarker/index.css @@ -1,4 +1,4 @@ -.mappable--default-tooltip { +.mappable--default-text-popup { display: block; max-width: inherit; max-height: inherit; diff --git a/src/markers/MMapTextPopupMarker/index.ts b/src/markers/MMapTextPopupMarker/index.ts index c47c035..55b0aaf 100644 --- a/src/markers/MMapTextPopupMarker/index.ts +++ b/src/markers/MMapTextPopupMarker/index.ts @@ -1,37 +1,37 @@ import {MMapPopupMarker, MMapPopupMarkerProps} from '../MMapPopupMarker'; import './index.css'; -import {MMapTooltipMarkerVuefyOptions} from './vue'; +import {MMapTextPopupMarkerVuefyOptions} from './vue'; -export type MMapTooltipMarkerProps = Omit & { - /** The text content that the tooltip will display */ +export type MMapTextPopupMarkerProps = Omit & { + /** The text content that the popup will display */ content: string; }; /** - * `MMapTooltipMarker` is a default tooltip - a text popup with no ability to close. + * `MMapTextPopupMarker` - a text popup with no ability to close. * @example * ```js - * const tooltip = new MMapTooltipMarker({ - * content:'Default tooltip', + * const popup = new MMapTextPopupMarker({ + * content:'Text popup', * // support MMapMarker props - * coordinates: TOOLTIP_COORD, + * coordinates: POPUP_COORD, * draggable: true, * // support MMapPopupMarker props * position: 'top', * }); - * map.addChild(tooltip); + * map.addChild(popup); * ``` */ -export class MMapTooltipMarker extends mappable.MMapComplexEntity { - static [mappable.optionsKeyVuefy] = MMapTooltipMarkerVuefyOptions; +export class MMapTextPopupMarker extends mappable.MMapComplexEntity { + static [mappable.optionsKeyVuefy] = MMapTextPopupMarkerVuefyOptions; private _popup: MMapPopupMarker; private _element: HTMLElement; protected _onAttach(): void { - this._popup = new MMapPopupMarker({...this._props, content: this.__createTooltip}); + this._popup = new MMapPopupMarker({...this._props, content: this.__createTextPopup}); this.addChild(this._popup); } - protected _onUpdate(propsDiff: Partial): void { + protected _onUpdate(propsDiff: Partial): void { if (propsDiff.content !== undefined) { this._element.textContent = this._props.content; } @@ -39,9 +39,9 @@ export class MMapTooltipMarker extends mappable.MMapComplexEntity { + private __createTextPopup = (): HTMLElement => { this._element = document.createElement('mappable'); - this._element.classList.add('mappable--default-tooltip'); + this._element.classList.add('mappable--default-text-popup'); this._element.textContent = this._props.content; return this._element; }; diff --git a/src/markers/MMapTextPopupMarker/vue/index.ts b/src/markers/MMapTextPopupMarker/vue/index.ts index 0905913..4118765 100644 --- a/src/markers/MMapTextPopupMarker/vue/index.ts +++ b/src/markers/MMapTextPopupMarker/vue/index.ts @@ -1,9 +1,9 @@ import {MMapFeatureProps, MMapMarkerEventHandler} from '@mappable-world/mappable-types'; import {CustomVuefyOptions} from '@mappable-world/mappable-types/modules/vuefy'; import type TVue from '@vue/runtime-core'; -import {MMapTooltipMarker, MMapTooltipMarkerProps} from '../'; +import {MMapTextPopupMarker, MMapTextPopupMarkerProps} from '../'; -export const MMapTooltipMarkerVuefyOptions: CustomVuefyOptions = { +export const MMapTextPopupMarkerVuefyOptions: CustomVuefyOptions = { props: { coordinates: {type: Object, required: true}, source: String, @@ -23,7 +23,7 @@ export const MMapTooltipMarkerVuefyOptions: CustomVuefyOptions, onFastClick: Function as TVue.PropType, content: {type: String, required: true}, - position: {type: String as TVue.PropType}, + position: {type: String as TVue.PropType}, offset: {type: Number, default: 0} } }; diff --git a/src/markers/index.ts b/src/markers/index.ts index 38fa9a0..53fb089 100644 --- a/src/markers/index.ts +++ b/src/markers/index.ts @@ -1,4 +1,4 @@ export * from './MMapDefaultMarker'; export {MMapDefaultPopupMarker, MMapDefaultPopupMarkerProps} from './MMapDefaultPopupMarker'; export {MMapPopupMarker, MMapPopupMarkerProps, MMapPopupPositionProps} from './MMapPopupMarker'; -export {MMapTooltipMarker, MMapTooltipMarkerProps} from './MMapTextPopupMarker'; +export {MMapTextPopupMarker, MMapTextPopupMarkerProps} from './MMapTextPopupMarker'; From 838ffd0c3e09f833f18f78ac6463d7a41ebd9fef Mon Sep 17 00:00:00 2001 From: Matthew Kalinin Date: Mon, 13 May 2024 11:52:33 +0300 Subject: [PATCH 46/48] fix issues --- example/{default-popups => popups-on-map}/common.css | 0 example/{default-popups => popups-on-map}/common.ts | 0 example/{default-popups => popups-on-map}/react/index.html | 0 example/{default-popups => popups-on-map}/react/index.tsx | 0 example/{default-popups => popups-on-map}/vanilla/index.html | 0 example/{default-popups => popups-on-map}/vanilla/index.ts | 0 example/{default-popups => popups-on-map}/vue/index.html | 0 example/{default-popups => popups-on-map}/vue/index.ts | 0 example/{default-tooltip => text-popup-on-map}/common.ts | 0 .../{default-tooltip => text-popup-on-map}/react/index.html | 0 .../{default-tooltip => text-popup-on-map}/react/index.tsx | 0 .../vanilla/index.html | 0 .../{default-tooltip => text-popup-on-map}/vanilla/index.ts | 0 .../{default-tooltip => text-popup-on-map}/vue/index.html | 0 example/{default-tooltip => text-popup-on-map}/vue/index.ts | 0 src/markers/MMapDefaultMarker/index.ts | 2 +- src/markers/MMapDefaultPopupMarker/index.ts | 2 +- src/markers/MMapPopupMarker/index.ts | 5 +---- 18 files changed, 3 insertions(+), 6 deletions(-) rename example/{default-popups => popups-on-map}/common.css (100%) rename example/{default-popups => popups-on-map}/common.ts (100%) rename example/{default-popups => popups-on-map}/react/index.html (100%) rename example/{default-popups => popups-on-map}/react/index.tsx (100%) rename example/{default-popups => popups-on-map}/vanilla/index.html (100%) rename example/{default-popups => popups-on-map}/vanilla/index.ts (100%) rename example/{default-popups => popups-on-map}/vue/index.html (100%) rename example/{default-popups => popups-on-map}/vue/index.ts (100%) rename example/{default-tooltip => text-popup-on-map}/common.ts (100%) rename example/{default-tooltip => text-popup-on-map}/react/index.html (100%) rename example/{default-tooltip => text-popup-on-map}/react/index.tsx (100%) rename example/{default-tooltip => text-popup-on-map}/vanilla/index.html (100%) rename example/{default-tooltip => text-popup-on-map}/vanilla/index.ts (100%) rename example/{default-tooltip => text-popup-on-map}/vue/index.html (100%) rename example/{default-tooltip => text-popup-on-map}/vue/index.ts (100%) diff --git a/example/default-popups/common.css b/example/popups-on-map/common.css similarity index 100% rename from example/default-popups/common.css rename to example/popups-on-map/common.css diff --git a/example/default-popups/common.ts b/example/popups-on-map/common.ts similarity index 100% rename from example/default-popups/common.ts rename to example/popups-on-map/common.ts diff --git a/example/default-popups/react/index.html b/example/popups-on-map/react/index.html similarity index 100% rename from example/default-popups/react/index.html rename to example/popups-on-map/react/index.html diff --git a/example/default-popups/react/index.tsx b/example/popups-on-map/react/index.tsx similarity index 100% rename from example/default-popups/react/index.tsx rename to example/popups-on-map/react/index.tsx diff --git a/example/default-popups/vanilla/index.html b/example/popups-on-map/vanilla/index.html similarity index 100% rename from example/default-popups/vanilla/index.html rename to example/popups-on-map/vanilla/index.html diff --git a/example/default-popups/vanilla/index.ts b/example/popups-on-map/vanilla/index.ts similarity index 100% rename from example/default-popups/vanilla/index.ts rename to example/popups-on-map/vanilla/index.ts diff --git a/example/default-popups/vue/index.html b/example/popups-on-map/vue/index.html similarity index 100% rename from example/default-popups/vue/index.html rename to example/popups-on-map/vue/index.html diff --git a/example/default-popups/vue/index.ts b/example/popups-on-map/vue/index.ts similarity index 100% rename from example/default-popups/vue/index.ts rename to example/popups-on-map/vue/index.ts diff --git a/example/default-tooltip/common.ts b/example/text-popup-on-map/common.ts similarity index 100% rename from example/default-tooltip/common.ts rename to example/text-popup-on-map/common.ts diff --git a/example/default-tooltip/react/index.html b/example/text-popup-on-map/react/index.html similarity index 100% rename from example/default-tooltip/react/index.html rename to example/text-popup-on-map/react/index.html diff --git a/example/default-tooltip/react/index.tsx b/example/text-popup-on-map/react/index.tsx similarity index 100% rename from example/default-tooltip/react/index.tsx rename to example/text-popup-on-map/react/index.tsx diff --git a/example/default-tooltip/vanilla/index.html b/example/text-popup-on-map/vanilla/index.html similarity index 100% rename from example/default-tooltip/vanilla/index.html rename to example/text-popup-on-map/vanilla/index.html diff --git a/example/default-tooltip/vanilla/index.ts b/example/text-popup-on-map/vanilla/index.ts similarity index 100% rename from example/default-tooltip/vanilla/index.ts rename to example/text-popup-on-map/vanilla/index.ts diff --git a/example/default-tooltip/vue/index.html b/example/text-popup-on-map/vue/index.html similarity index 100% rename from example/default-tooltip/vue/index.html rename to example/text-popup-on-map/vue/index.html diff --git a/example/default-tooltip/vue/index.ts b/example/text-popup-on-map/vue/index.ts similarity index 100% rename from example/default-tooltip/vue/index.ts rename to example/text-popup-on-map/vue/index.ts diff --git a/src/markers/MMapDefaultMarker/index.ts b/src/markers/MMapDefaultMarker/index.ts index 1225270..cc00fe0 100644 --- a/src/markers/MMapDefaultMarker/index.ts +++ b/src/markers/MMapDefaultMarker/index.ts @@ -41,7 +41,7 @@ export type MarkerPopupProps = { title?: string; /** Displayed description */ description?: string; - /** The inscription on the action button */ + /** The description on the action button */ action?: string; /** Callback of click the action button */ onAction?: () => void; diff --git a/src/markers/MMapDefaultPopupMarker/index.ts b/src/markers/MMapDefaultPopupMarker/index.ts index 8ae96c4..a6c3797 100644 --- a/src/markers/MMapDefaultPopupMarker/index.ts +++ b/src/markers/MMapDefaultPopupMarker/index.ts @@ -9,7 +9,7 @@ export type MMapDefaultPopupMarkerProps = Omit title?: string; /** Displayed description */ description?: string; - /** The inscription on the action button */ + /** The description on the action button */ action?: string; /** Callback of click the action button */ onAction?: () => void; diff --git a/src/markers/MMapPopupMarker/index.ts b/src/markers/MMapPopupMarker/index.ts index b73f6e4..ee48fd6 100644 --- a/src/markers/MMapPopupMarker/index.ts +++ b/src/markers/MMapPopupMarker/index.ts @@ -64,10 +64,7 @@ export class MMapPopupMarker extends mappable.MMapComplexEntity Date: Mon, 13 May 2024 18:18:43 +0300 Subject: [PATCH 47/48] remove text popup and default popup --- example/marker-popup/react/index.tsx | 10 +- example/marker-popup/vanilla/index.ts | 8 +- example/marker-popup/vue/index.ts | 15 +- example/popups-on-map/close.svg | 1 + example/popups-on-map/common.css | 89 ++++++++++-- example/popups-on-map/common.ts | 11 +- example/popups-on-map/react/index.tsx | 74 +++++----- example/popups-on-map/vanilla/index.ts | 98 ++++++++------ example/popups-on-map/vue/index.ts | 105 ++++++++------ example/text-popup-on-map/common.ts | 5 - example/text-popup-on-map/react/index.html | 36 ----- example/text-popup-on-map/react/index.tsx | 56 -------- example/text-popup-on-map/vanilla/index.html | 34 ----- example/text-popup-on-map/vanilla/index.ts | 31 ----- example/text-popup-on-map/vue/index.html | 35 ----- example/text-popup-on-map/vue/index.ts | 74 ---------- src/markers/MMapDefaultMarker/index.ts | 21 ++- src/markers/MMapDefaultMarker/react/index.tsx | 54 ++++++++ src/markers/MMapDefaultMarker/vue/index.ts | 52 ++++++- .../MMapDefaultPopupMarker.test.ts | 91 ------------- src/markers/MMapDefaultPopupMarker/close.svg | 1 - src/markers/MMapDefaultPopupMarker/index.css | 78 ----------- src/markers/MMapDefaultPopupMarker/index.ts | 128 ------------------ .../MMapDefaultPopupMarker/vue/index.ts | 35 ----- .../MMapPopupMarker/MMapPopupMarker.test.ts | 10 ++ src/markers/MMapPopupMarker/index.ts | 16 ++- src/markers/MMapPopupMarker/react/index.tsx | 12 +- src/markers/MMapPopupMarker/vue/index.ts | 2 +- .../MMapTextPopupMarker.test.ts | 37 ----- src/markers/MMapTextPopupMarker/index.css | 7 - src/markers/MMapTextPopupMarker/index.ts | 48 ------- src/markers/MMapTextPopupMarker/vue/index.ts | 29 ---- src/markers/index.ts | 11 +- 33 files changed, 412 insertions(+), 902 deletions(-) create mode 100644 example/popups-on-map/close.svg delete mode 100644 example/text-popup-on-map/common.ts delete mode 100644 example/text-popup-on-map/react/index.html delete mode 100644 example/text-popup-on-map/react/index.tsx delete mode 100644 example/text-popup-on-map/vanilla/index.html delete mode 100644 example/text-popup-on-map/vanilla/index.ts delete mode 100644 example/text-popup-on-map/vue/index.html delete mode 100644 example/text-popup-on-map/vue/index.ts create mode 100644 src/markers/MMapDefaultMarker/react/index.tsx delete mode 100644 src/markers/MMapDefaultPopupMarker/MMapDefaultPopupMarker.test.ts delete mode 100644 src/markers/MMapDefaultPopupMarker/close.svg delete mode 100644 src/markers/MMapDefaultPopupMarker/index.css delete mode 100644 src/markers/MMapDefaultPopupMarker/index.ts delete mode 100644 src/markers/MMapDefaultPopupMarker/vue/index.ts delete mode 100644 src/markers/MMapTextPopupMarker/MMapTextPopupMarker.test.ts delete mode 100644 src/markers/MMapTextPopupMarker/index.css delete mode 100644 src/markers/MMapTextPopupMarker/index.ts delete mode 100644 src/markers/MMapTextPopupMarker/vue/index.ts diff --git a/example/marker-popup/react/index.tsx b/example/marker-popup/react/index.tsx index 84cfca8..c7a006e 100644 --- a/example/marker-popup/react/index.tsx +++ b/example/marker-popup/react/index.tsx @@ -1,4 +1,4 @@ -import {MarkerPopupProps, MarkerSizeProps} from '../../src'; +import {MarkerSizeProps} from '../../src'; import {CENTER, LOCATION} from '../common'; window.map = null; @@ -11,7 +11,7 @@ async function main() { const {MMap, MMapDefaultSchemeLayer, MMapDefaultFeaturesLayer, MMapControls, MMapControlButton} = reactify.module(mappable); - const {useState, useCallback} = React; + const {useState, useCallback, useMemo} = React; const {MMapDefaultMarker} = reactify.module(await mappable.import('@mappable-world/mappable-default-ui-theme')); @@ -25,11 +25,7 @@ async function main() { function App() { const [location] = useState(LOCATION); const [size, setSize] = useState('normal'); - const [popup] = useState({ - title: 'Popup', - description: 'Description for this marker', - action: 'Action' - }); + const popup = useMemo(() => ({content: () => Marker popup}), []); return ( (map = x)}> diff --git a/example/marker-popup/vanilla/index.ts b/example/marker-popup/vanilla/index.ts index 62fad3a..7308633 100644 --- a/example/marker-popup/vanilla/index.ts +++ b/example/marker-popup/vanilla/index.ts @@ -18,9 +18,11 @@ async function main() { iconName: 'fallback', size: 'normal', popup: { - title: 'Popup', - description: 'Description for this marker', - action: 'Action' + content: () => { + const popup = document.createElement('span'); + popup.textContent = 'Marker popup'; + return popup; + } } }); diff --git a/example/marker-popup/vue/index.ts b/example/marker-popup/vue/index.ts index 3ddd9d2..074ca99 100644 --- a/example/marker-popup/vue/index.ts +++ b/example/marker-popup/vue/index.ts @@ -1,4 +1,4 @@ -import {MarkerPopupProps, MarkerSizeProps} from '../../src'; +import {MarkerSizeProps} from '../../src'; import {CENTER, LOCATION} from '../common'; window.map = null; @@ -24,11 +24,6 @@ async function main() { }, setup() { const size = Vue.ref('normal'); - const popup = Vue.ref({ - title: 'Popup', - description: 'Description for this marker', - action: 'Action' - }); const refMap = (ref: any) => { window.map = ref?.entity; }; @@ -36,13 +31,17 @@ async function main() { const setSmallSize = () => (size.value = 'small'); const setMicroSize = () => (size.value = 'micro'); - return {LOCATION, CENTER, size, popup, refMap, setNormalSize, setSmallSize, setMicroSize}; + return {LOCATION, CENTER, size, refMap, setNormalSize, setSmallSize, setMicroSize}; }, template: ` - + + + diff --git a/example/popups-on-map/close.svg b/example/popups-on-map/close.svg new file mode 100644 index 0000000..1ba55ba --- /dev/null +++ b/example/popups-on-map/close.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/example/popups-on-map/common.css b/example/popups-on-map/common.css index c198bfb..820bf36 100644 --- a/example/popups-on-map/common.css +++ b/example/popups-on-map/common.css @@ -1,18 +1,81 @@ -.custom-popup { - display: grid; - max-width: inherit; - grid-template-columns: 120px 64px; - grid-template-rows: repeat(2, 1fr); - gap: 10px; +.popup { + display: flex; + flex-direction: column; + row-gap: 8px; +} - & .title { - font-weight: bold; - font-size: 18px; - } +.header { + display: flex; + flex-direction: row; + justify-content: space-between; + gap: 8px; + padding-right: 20px; - & .content { - width: 100%; + .header_title { font-size: 16px; - grid-column: 1 / 3; + font-weight: 400; + line-height: 22px; + color: #050d33; + } + .header_close { + position: absolute; + top: 0; + right: 0; + display: flex; + justify-content: center; + align-items: center; + width: 32px; + height: 32px; + border: none; + background: none; + color: #c8c9cc; + cursor: pointer; + background-image: url('./close.svg?inline'); + background-position: center; + background-repeat: no-repeat; + } +} + +.description { + font-size: 14px; + font-weight: 400; + line-height: 20px; + color: #7b7d85; +} + +.action { + width: max-content; + padding: 12px 16px; + border-radius: 8px; + border: none; + background-color: #eefd7d; + transition: background-color 0.1s ease-out; + color: #050d33; + text-align: center; + cursor: pointer; + white-space: normal; + + font-size: 14px; + font-style: normal; + font-weight: 500; + line-height: 16px; +} + +.action:hover { + background-color: #e5fd30; +} + +._dark { + .header_title { + color: #f2f5fa; + } + .header_close { + color: #46464d; + } + .description { + color: #7b7d85; + } + .action { + background-color: #d6fd63; } } diff --git a/example/popups-on-map/common.ts b/example/popups-on-map/common.ts index cd7487c..b3b7c73 100644 --- a/example/popups-on-map/common.ts +++ b/example/popups-on-map/common.ts @@ -1,7 +1,12 @@ -import type {LngLat, MMapLocationRequest} from '@mappable-world/mappable-types'; +import type {MMapLocationRequest, LngLat} from '@mappable-world/mappable-types'; -const CENTER: LngLat = [55.442795, 25.24107]; +export const CENTER: LngLat = [55.442795, 25.24107]; export const LOCATION: MMapLocationRequest = {center: CENTER, zoom: 14}; +export const POPUP_TEXT = 'Default text popup'; export const CUSTOM_POPUP_COORDS: LngLat = [CENTER[0] - 0.02, CENTER[1]]; -export const DEFAULT_POPUP_COORDS: LngLat = [CENTER[0] + 0.02, CENTER[1]]; +export const TEXT_POPUP_COORDS: LngLat = [CENTER[0] + 0.02, CENTER[1]]; + +export const TITLE = 'Default popup marker'; +export const DESCRIPTION = 'Description for default popup'; +export const ACTION = 'Make an action'; diff --git a/example/popups-on-map/react/index.tsx b/example/popups-on-map/react/index.tsx index 8934637..a07ab88 100644 --- a/example/popups-on-map/react/index.tsx +++ b/example/popups-on-map/react/index.tsx @@ -1,5 +1,5 @@ -import {CUSTOM_POPUP_COORDS, DEFAULT_POPUP_COORDS, LOCATION} from '../common'; - +import type {MMapPopupPositionProps} from '../../src'; +import {ACTION, CUSTOM_POPUP_COORDS, DESCRIPTION, LOCATION, POPUP_TEXT, TEXT_POPUP_COORDS, TITLE} from '../common'; window.map = null; main(); @@ -12,9 +12,7 @@ async function main() { const {useState, useCallback} = React; - const {MMapPopupMarker, MMapDefaultPopupMarker} = reactify.module( - await mappable.import('@mappable-world/mappable-default-ui-theme') - ); + const {MMapPopupMarker} = reactify.module(await mappable.import('@mappable-world/mappable-default-ui-theme')); ReactDOM.render( @@ -24,45 +22,55 @@ async function main() { ); function App() { - const [location] = useState(LOCATION); - const [showDefaultPopup, setShowDefaultPopup] = useState(true); - const [showCustomPopup, setShowCustomPopup] = useState(true); + const [position, setPosition] = useState(undefined); + const [showCustom, setShowCustom] = useState(true); + + const positionLeft = useCallback(() => setPosition('left'), []); + const positionLeftTop = useCallback(() => setPosition('left top'), []); + const positionLeftBottom = useCallback(() => setPosition('left bottom'), []); + const positionBottom = useCallback(() => setPosition('bottom'), []); + const positionTop = useCallback(() => setPosition('top'), []); + const positionRightTop = useCallback(() => setPosition('right top'), []); + const positionRightBottom = useCallback(() => setPosition('right bottom'), []); + const positionRight = useCallback(() => setPosition('right'), []); - const toggleDefaultPopup = useCallback(() => setShowDefaultPopup((prev) => !prev), [showDefaultPopup]); - const toggleCustomPopup = useCallback(() => setShowCustomPopup((prev) => !prev), [showCustomPopup]); - const actionCallback = useCallback(() => alert('Click on action button!'), []); - const contentCallback = useCallback( + const customPopupContent = useCallback( () => ( -
-
Title
- -
Custom popup content
-
+ + + {TITLE} + + + {DESCRIPTION} + + ), [] ); - const onCloseCustomPopup = useCallback(() => setShowCustomPopup(false), [showCustomPopup]); - const onCloseDefaultPopup = useCallback(() => setShowDefaultPopup(false), [showDefaultPopup]); return ( - (map = x)}> + (map = x)}> - - + + + + + + + + - - - - + ); diff --git a/example/popups-on-map/vanilla/index.ts b/example/popups-on-map/vanilla/index.ts index 5840af2..427c3ce 100644 --- a/example/popups-on-map/vanilla/index.ts +++ b/example/popups-on-map/vanilla/index.ts @@ -1,4 +1,6 @@ -import {CUSTOM_POPUP_COORDS, DEFAULT_POPUP_COORDS, LOCATION} from '../common'; +import type {MMapPopupPositionProps} from '../../src'; +import {ACTION, CUSTOM_POPUP_COORDS, DESCRIPTION, LOCATION, POPUP_TEXT, TEXT_POPUP_COORDS, TITLE} from '../common'; + window.map = null; main(); @@ -6,60 +8,74 @@ async function main() { await mappable.ready; const {MMap, MMapDefaultSchemeLayer, MMapDefaultFeaturesLayer, MMapControls, MMapControlButton} = mappable; - const {MMapPopupMarker, MMapDefaultPopupMarker} = await mappable.import( - '@mappable-world/mappable-default-ui-theme' - ); + const {MMapPopupMarker} = await mappable.import('@mappable-world/mappable-default-ui-theme'); map = new MMap(document.getElementById('app'), {location: LOCATION}); map.addChild(new MMapDefaultSchemeLayer({})); map.addChild(new MMapDefaultFeaturesLayer({})); + const updatePositions = (position: MMapPopupPositionProps) => { + textPopup.update({position}); + customPopup.update({position}); + }; + map.addChild( new MMapControls({position: 'top right'}, [ - new MMapControlButton({ - text: 'Toggle custom popup', - onClick: () => customPopup.update({show: !customPopup.isOpen}) - }), - new MMapControlButton({ - text: 'Toggle default popup', - onClick: () => defaultPopup.update({show: !defaultPopup.isOpen}) - }) + new MMapControlButton({text: 'Left', onClick: () => updatePositions('left')}), + new MMapControlButton({text: 'Left Top', onClick: () => updatePositions('left top')}), + new MMapControlButton({text: 'Left Bottom', onClick: () => updatePositions('left bottom')}), + new MMapControlButton({text: 'Bottom', onClick: () => updatePositions('bottom')}), + new MMapControlButton({text: 'Top', onClick: () => updatePositions('top')}), + new MMapControlButton({text: 'Right Top', onClick: () => updatePositions('right top')}), + new MMapControlButton({text: 'Right Bottom', onClick: () => updatePositions('right bottom')}), + new MMapControlButton({text: 'Right', onClick: () => updatePositions('right')}) ]) ); + const textPopup = new MMapPopupMarker({coordinates: TEXT_POPUP_COORDS, draggable: true, content: POPUP_TEXT}); + map.addChild(textPopup); + const customPopup = new MMapPopupMarker({ coordinates: CUSTOM_POPUP_COORDS, - content: () => { - const popupElement = document.createElement('div'); - popupElement.classList.add('custom-popup'); - - const titleElement = document.createElement('div'); - titleElement.classList.add('title'); - titleElement.textContent = 'Title'; - popupElement.appendChild(titleElement); - - const closeButton = document.createElement('button'); - closeButton.textContent = 'close'; - closeButton.addEventListener('click', () => customPopup.update({show: false})); - popupElement.appendChild(closeButton); - - const contentElement = document.createElement('div'); - contentElement.classList.add('content'); - contentElement.textContent = 'Custom popup content'; - popupElement.appendChild(contentElement); - - return popupElement; - } + draggable: true, + content: createDefaultPopup }); map.addChild(customPopup); - const defaultPopup = new MMapDefaultPopupMarker({ - coordinates: DEFAULT_POPUP_COORDS, - title: 'Default popup marker', - description: 'Description for default popup', - action: 'Make an action', - onAction: () => alert('Click on action button!') - }); - map.addChild(defaultPopup); + function createDefaultPopup(): HTMLElement { + const popupRootElement = document.createElement('span'); + popupRootElement.classList.add('popup'); + + const popupHeaderElement = document.createElement('span'); + popupHeaderElement.classList.add('header'); + popupRootElement.appendChild(popupHeaderElement); + + const titleElement = document.createElement('span'); + titleElement.classList.add('header_title'); + titleElement.textContent = TITLE; + popupHeaderElement.appendChild(titleElement); + + const closeButton = document.createElement('button'); + closeButton.classList.add('header_close'); + closeButton.addEventListener('click', () => { + customPopup.update({show: false}); + }); + popupHeaderElement.appendChild(closeButton); + + const descriptionElement = document.createElement('span'); + descriptionElement.classList.add('description'); + descriptionElement.textContent = DESCRIPTION; + popupRootElement.appendChild(descriptionElement); + + const actionButton = document.createElement('button'); + actionButton.classList.add('action'); + actionButton.textContent = ACTION; + actionButton.addEventListener('click', () => { + alert('Click on action button!'); + }); + popupRootElement.appendChild(actionButton); + + return popupRootElement; + } } diff --git a/example/popups-on-map/vue/index.ts b/example/popups-on-map/vue/index.ts index 7ff32fd..8ae13bf 100644 --- a/example/popups-on-map/vue/index.ts +++ b/example/popups-on-map/vue/index.ts @@ -1,4 +1,5 @@ -import {CUSTOM_POPUP_COORDS, DEFAULT_POPUP_COORDS, LOCATION} from '../common'; +import type {MMapPopupPositionProps} from '../../src'; +import {ACTION, CUSTOM_POPUP_COORDS, DESCRIPTION, LOCATION, POPUP_TEXT, TEXT_POPUP_COORDS, TITLE} from '../common'; window.map = null; @@ -10,9 +11,7 @@ async function main() { const {MMap, MMapDefaultSchemeLayer, MMapDefaultFeaturesLayer, MMapControls, MMapControlButton} = vuefy.module(mappable); - const {MMapPopupMarker, MMapDefaultPopupMarker} = vuefy.module( - await mappable.import('@mappable-world/mappable-default-ui-theme') - ); + const {MMapPopupMarker} = vuefy.module(await mappable.import('@mappable-world/mappable-default-ui-theme')); const app = Vue.createApp({ components: { @@ -21,63 +20,85 @@ async function main() { MMapDefaultFeaturesLayer, MMapControls, MMapControlButton, - MMapPopupMarker, - MMapDefaultPopupMarker + MMapPopupMarker }, setup() { - const location = Vue.ref(LOCATION); - const showDefaultPopup = Vue.ref(true); - const showCustomPopup = Vue.ref(true); const refMap = (ref: any) => { window.map = ref?.entity; }; + const position = Vue.ref(undefined); + const showCustom = Vue.ref(true); - const toggleDefaultPopup = () => (showDefaultPopup.value = !showDefaultPopup.value); - const toggleCustomPopup = () => (showCustomPopup.value = !showCustomPopup.value); - const actionCallback = () => alert('Click on action button!'); - const onCloseCustomPopup = () => (showCustomPopup.value = false); - const onCloseDefaultPopup = () => (showDefaultPopup.value = false); + const positionLeft = () => (position.value = 'left'); + const positionLeftTop = () => (position.value = 'left top'); + const positionLeftBottom = () => (position.value = 'left bottom'); + const positionBottom = () => (position.value = 'bottom'); + const positionTop = () => (position.value = 'top'); + const positionRightTop = () => (position.value = 'right top'); + const positionRightBottom = () => (position.value = 'right bottom'); + const positionRight = () => (position.value = 'right'); + + const customPopupAction = () => { + alert('Click on action button!'); + }; return { - location, - refMap, - toggleCustomPopup, - toggleDefaultPopup, - onCloseCustomPopup, - onCloseDefaultPopup, - actionCallback, - showCustomPopup, - showDefaultPopup, + ACTION, CUSTOM_POPUP_COORDS, - DEFAULT_POPUP_COORDS + DESCRIPTION, + LOCATION, + POPUP_TEXT, + TEXT_POPUP_COORDS, + TITLE, + position, + showCustom, + refMap, + positionLeft, + positionLeftTop, + positionLeftBottom, + positionBottom, + positionTop, + positionRightTop, + positionRightBottom, + positionRight, + customPopupAction }; }, template: ` - + - - + + + + + + + + - + + + + + + - ` }); app.mount('#app'); diff --git a/example/text-popup-on-map/common.ts b/example/text-popup-on-map/common.ts deleted file mode 100644 index 6afeb8e..0000000 --- a/example/text-popup-on-map/common.ts +++ /dev/null @@ -1,5 +0,0 @@ -import type {MMapLocationRequest, LngLat} from '@mappable-world/mappable-types'; - -export const CENTER: LngLat = [55.442795, 25.24107]; -export const LOCATION: MMapLocationRequest = {center: CENTER, zoom: 14}; -export const POPUP_TEXT = 'Default text popup'; diff --git a/example/text-popup-on-map/react/index.html b/example/text-popup-on-map/react/index.html deleted file mode 100644 index b9c642a..0000000 --- a/example/text-popup-on-map/react/index.html +++ /dev/null @@ -1,36 +0,0 @@ - - - - React example mappable-default-ui-theme - - - - - - - - - - - - - - -
- - diff --git a/example/text-popup-on-map/react/index.tsx b/example/text-popup-on-map/react/index.tsx deleted file mode 100644 index ebab76a..0000000 --- a/example/text-popup-on-map/react/index.tsx +++ /dev/null @@ -1,56 +0,0 @@ -import type {MMapTextPopupMarkerProps} from '../../src'; -import {CENTER, LOCATION, POPUP_TEXT} from '../common'; - -window.map = null; - -main(); -async function main() { - const [mappableReact] = await Promise.all([mappable.import('@mappable-world/mappable-reactify'), mappable.ready]); - const reactify = mappableReact.reactify.bindTo(React, ReactDOM); - - const {MMap, MMapDefaultSchemeLayer, MMapDefaultFeaturesLayer, MMapControls, MMapControlButton} = - reactify.module(mappable); - - const {useState, useCallback} = React; - - const {MMapTextPopupMarker} = reactify.module(await mappable.import('@mappable-world/mappable-default-ui-theme')); - - ReactDOM.render( - - - , - document.getElementById('app') - ); - - function App() { - const [location] = useState(LOCATION); - const [position, setPosition] = useState(undefined); - - const positionLeft = useCallback(() => setPosition('left'), []); - const positionLeftTop = useCallback(() => setPosition('left top'), []); - const positionLeftBottom = useCallback(() => setPosition('left bottom'), []); - const positionBottom = useCallback(() => setPosition('bottom'), []); - const positionTop = useCallback(() => setPosition('top'), []); - const positionRightTop = useCallback(() => setPosition('right top'), []); - const positionRightBottom = useCallback(() => setPosition('right bottom'), []); - const positionRight = useCallback(() => setPosition('right'), []); - - return ( - (map = x)}> - - - - - - - - - - - - - - - ); - } -} diff --git a/example/text-popup-on-map/vanilla/index.html b/example/text-popup-on-map/vanilla/index.html deleted file mode 100644 index e4d869a..0000000 --- a/example/text-popup-on-map/vanilla/index.html +++ /dev/null @@ -1,34 +0,0 @@ - - - - Vanilla example mappable-default-ui-theme - - - - - - - - - - - - -
- - diff --git a/example/text-popup-on-map/vanilla/index.ts b/example/text-popup-on-map/vanilla/index.ts deleted file mode 100644 index 802f53c..0000000 --- a/example/text-popup-on-map/vanilla/index.ts +++ /dev/null @@ -1,31 +0,0 @@ -import {CENTER, LOCATION, POPUP_TEXT} from '../common'; -window.map = null; - -main(); -async function main() { - await mappable.ready; - const {MMap, MMapDefaultSchemeLayer, MMapDefaultFeaturesLayer, MMapControls, MMapControlButton} = mappable; - - const {MMapTextPopupMarker} = await mappable.import('@mappable-world/mappable-default-ui-theme'); - - map = new MMap(document.getElementById('app'), {location: LOCATION}); - - map.addChild(new MMapDefaultSchemeLayer({})); - map.addChild(new MMapDefaultFeaturesLayer({})); - - map.addChild( - new MMapControls({position: 'top right'}, [ - new MMapControlButton({text: 'Left', onClick: () => textPopup.update({position: 'left'})}), - new MMapControlButton({text: 'Left Top', onClick: () => textPopup.update({position: 'left top'})}), - new MMapControlButton({text: 'Left Bottom', onClick: () => textPopup.update({position: 'left bottom'})}), - new MMapControlButton({text: 'Bottom', onClick: () => textPopup.update({position: 'bottom'})}), - new MMapControlButton({text: 'Top', onClick: () => textPopup.update({position: 'top'})}), - new MMapControlButton({text: 'Right Top', onClick: () => textPopup.update({position: 'right top'})}), - new MMapControlButton({text: 'Right Bottom', onClick: () => textPopup.update({position: 'right bottom'})}), - new MMapControlButton({text: 'Right', onClick: () => textPopup.update({position: 'right'})}) - ]) - ); - - const textPopup = new MMapTextPopupMarker({coordinates: CENTER, draggable: true, content: POPUP_TEXT}); - map.addChild(textPopup); -} diff --git a/example/text-popup-on-map/vue/index.html b/example/text-popup-on-map/vue/index.html deleted file mode 100644 index 9471aa8..0000000 --- a/example/text-popup-on-map/vue/index.html +++ /dev/null @@ -1,35 +0,0 @@ - - - - Vue example mappable-default-ui-theme - - - - - - - - - - - - - -
- - diff --git a/example/text-popup-on-map/vue/index.ts b/example/text-popup-on-map/vue/index.ts deleted file mode 100644 index f9abb89..0000000 --- a/example/text-popup-on-map/vue/index.ts +++ /dev/null @@ -1,74 +0,0 @@ -import type {MMapTextPopupMarkerProps} from '../../src'; -import {CENTER, LOCATION, POPUP_TEXT} from '../common'; - -window.map = null; - -main(); -async function main() { - const [mappableVue] = await Promise.all([mappable.import('@mappable-world/mappable-vuefy'), mappable.ready]); - const vuefy = mappableVue.vuefy.bindTo(Vue); - - const {MMap, MMapDefaultSchemeLayer, MMapDefaultFeaturesLayer, MMapControls, MMapControlButton} = - vuefy.module(mappable); - - const {MMapTextPopupMarker} = vuefy.module(await mappable.import('@mappable-world/mappable-default-ui-theme')); - - const app = Vue.createApp({ - components: { - MMap, - MMapDefaultSchemeLayer, - MMapDefaultFeaturesLayer, - MMapControls, - MMapControlButton, - MMapTextPopupMarker - }, - setup() { - const refMap = (ref: any) => { - window.map = ref?.entity; - }; - const position = Vue.ref(undefined); - - const positionLeft = () => (position.value = 'left'); - const positionLeftTop = () => (position.value = 'left top'); - const positionLeftBottom = () => (position.value = 'left bottom'); - const positionBottom = () => (position.value = 'bottom'); - const positionTop = () => (position.value = 'top'); - const positionRightTop = () => (position.value = 'right top'); - const positionRightBottom = () => (position.value = 'right bottom'); - const positionRight = () => (position.value = 'right'); - - return { - LOCATION, - CENTER, - POPUP_TEXT, - position, - refMap, - positionLeft, - positionLeftTop, - positionLeftBottom, - positionBottom, - positionTop, - positionRightTop, - positionRightBottom, - positionRight - }; - }, - template: ` - - - - - - - - - - - - - - - ` - }); - app.mount('#app'); -} diff --git a/src/markers/MMapDefaultMarker/index.ts b/src/markers/MMapDefaultMarker/index.ts index cc00fe0..83237a6 100644 --- a/src/markers/MMapDefaultMarker/index.ts +++ b/src/markers/MMapDefaultMarker/index.ts @@ -1,7 +1,8 @@ import {MMapMarker, MMapMarkerProps} from '@mappable-world/mappable-types'; import {IconColor, IconName, iconColors, icons} from '../../icons'; -import {MMapDefaultMarkerVuefyOptions} from './vue'; -import {MMapDefaultPopupMarker} from '../'; +import {MMapPopupContentProps, MMapPopupMarker} from '../MMapPopupMarker'; +import {MMapDefaultMarkerReactifyOverride} from './react'; +import {MMapDefaultMarkerVuefyOptions, MMapDefaultMarkerVuefyOverride} from './vue'; import microPoiStrokeSVG from './backgrounds/micro-poi-stroke.svg'; import microPoiSVG from './backgrounds/micro-poi.svg'; @@ -37,14 +38,8 @@ export type ThemesColor = {day: string; night: string}; export type MarkerColorProps = IconColor | ThemesColor; export type MarkerSizeProps = 'normal' | 'small' | 'micro'; export type MarkerPopupProps = { - /** Displayed title in popup header */ - title?: string; - /** Displayed description */ - description?: string; - /** The description on the action button */ - action?: string; - /** Callback of click the action button */ - onAction?: () => void; + /** The function of creating popup content */ + content: MMapPopupContentProps; }; export type MMapDefaultMarkerProps = MMapMarkerProps & { @@ -64,6 +59,8 @@ type BackgroundAndIcon = {background: HTMLElement; stroke: HTMLElement; icon: HT export class MMapDefaultMarker extends mappable.MMapComplexEntity { static defaultProps = defaultProps; + static [mappable.overrideKeyReactify] = MMapDefaultMarkerReactifyOverride; + static [mappable.overrideKeyVuefy] = MMapDefaultMarkerVuefyOverride; static [mappable.optionsKeyVuefy] = MMapDefaultMarkerVuefyOptions; private _marker: MMapMarker; @@ -78,7 +75,7 @@ export class MMapDefaultMarker extends mappable.MMapComplexEntity TReact.ReactElement); + }; + } + > +>; + +type MMapDefaultMarkerR = TReact.ForwardRefExoticComponent< + Prettify>> +>; + +export const MMapDefaultMarkerReactifyOverride: CustomReactify = ( + MMapDefaultMarkerI, + {reactify, React, ReactDOM} +) => { + const MMapDefaultMarkerReactified = reactify.entity(MMapDefaultMarkerI); + + const MMapDefaultMarker = React.forwardRef((props, ref) => { + const [popupElement] = React.useState(document.createElement('mappable')); + const [content, setContent] = React.useState(); + + const popupContent = React.useMemo(() => { + if (props.popup === undefined) { + return undefined; + } + + if (typeof props.popup.content === 'string') { + setContent(<>{props.popup.content}); + } else if (typeof props.popup.content === 'function') { + setContent(props.popup.content()); + } + + return {content: () => popupElement}; + }, [props.popup.content, popupElement]); + + return ( + <> + + {ReactDOM.createPortal(content, popupElement)} + + ); + }); + return MMapDefaultMarker; +}; diff --git a/src/markers/MMapDefaultMarker/vue/index.ts b/src/markers/MMapDefaultMarker/vue/index.ts index aa13877..9a7b547 100644 --- a/src/markers/MMapDefaultMarker/vue/index.ts +++ b/src/markers/MMapDefaultMarker/vue/index.ts @@ -1,7 +1,7 @@ import {MMapFeatureProps, MMapMarkerEventHandler} from '@mappable-world/mappable-types'; -import {CustomVuefyOptions} from '@mappable-world/mappable-types/modules/vuefy'; +import {CustomVuefyFn, CustomVuefyOptions} from '@mappable-world/mappable-types/modules/vuefy'; import type TVue from '@vue/runtime-core'; -import {MMapDefaultMarker, MarkerColorProps, MarkerPopupProps, MarkerSizeProps} from '../'; +import {MMapDefaultMarker, MMapDefaultMarkerProps, MarkerColorProps, MarkerPopupProps, MarkerSizeProps} from '../'; import {IconName} from '../../../icons'; export const MMapDefaultMarkerVuefyOptions: CustomVuefyOptions = { @@ -24,8 +24,7 @@ export const MMapDefaultMarkerVuefyOptions: CustomVuefyOptions, onFastClick: Function as TVue.PropType, iconName: {type: String as TVue.PropType}, - // @ts-ignore CustomVuefyOptions types no support multiple types - color: {type: [Object as TVue.PropType, String], default: 'darkgray'}, + color: {type: [Object, String] as TVue.PropType, default: 'darkgray'}, size: {type: String as TVue.PropType, default: 'small'}, title: {type: String}, subtitle: {type: String}, @@ -33,3 +32,48 @@ export const MMapDefaultMarkerVuefyOptions: CustomVuefyOptions} } }; + +type MMapDefaultMarkerSlots = { + popupContent: void; +}; + +export const MMapDefaultMarkerVuefyOverride: CustomVuefyFn = ( + MMapDefaultMarkerI, + props, + {vuefy, Vue} +) => { + const MMapDefaultMarkerV = vuefy.entity(MMapDefaultMarkerI); + const {popup, ...overridedProps} = props; + + return Vue.defineComponent({ + name: 'MMapDefaultMarker', + props: overridedProps, + slots: Object as TVue.SlotsType, + setup(props, {slots, expose}) { + const content: TVue.Ref = Vue.ref(null); + const popupHTMLElement = document.createElement('mappable'); + + const markerRef = Vue.ref<{entity: MMapDefaultMarker} | null>(null); + const markerEntity = Vue.computed(() => markerRef.value?.entity); + + const popup = Vue.computed(() => { + if (slots.popupContent === undefined) { + return undefined; + } + content.value = slots.popupContent(); + return {content: () => popupHTMLElement}; + }); + expose({entity: markerEntity}); + return () => + Vue.h( + MMapDefaultMarkerV, + { + ...props, + popup: popup.value, + ref: markerRef + }, + () => Vue.h(Vue.Teleport, {to: popupHTMLElement}, [content.value]) + ); + } + }); +}; diff --git a/src/markers/MMapDefaultPopupMarker/MMapDefaultPopupMarker.test.ts b/src/markers/MMapDefaultPopupMarker/MMapDefaultPopupMarker.test.ts deleted file mode 100644 index 72e8f83..0000000 --- a/src/markers/MMapDefaultPopupMarker/MMapDefaultPopupMarker.test.ts +++ /dev/null @@ -1,91 +0,0 @@ -import {MMap} from '@mappable-world/mappable-types'; -import {CENTER, createContainer} from '../../../tests/common'; -import {MMapDefaultPopupMarker} from './'; - -describe('MMapDefaultPopupMarker', () => { - let map: MMap; - let container: HTMLElement; - - beforeEach(() => { - container = createContainer(); - document.body.append(container); - map = new mappable.MMap(container, {location: {center: CENTER, zoom: 0}}); - map.addChild(new mappable.MMapDefaultFeaturesLayer({})); - }); - - afterEach(() => { - map.destroy(); - }); - - it('add on map', () => { - const defaultPopup = new MMapDefaultPopupMarker({coordinates: CENTER, title: 'Title'}); - map.addChild(defaultPopup); - - expect(document.querySelector(BASE_SELECTOR)).not.toBeNull(); - }); - - it('title props', () => { - const defaultPopup = new MMapDefaultPopupMarker({title: 'Title', coordinates: CENTER}); - map.addChild(defaultPopup); - let titleElement = document.querySelector(`${BASE_SELECTOR} .mappable--default-popup_header_title`); - expect(titleElement.textContent).toBe('Title'); - - defaultPopup.update({title: 'New Title'}); - titleElement = document.querySelector(`${BASE_SELECTOR} .mappable--default-popup_header_title`); - expect(titleElement.textContent).toBe('New Title'); - }); - - it('description props', () => { - const defaultPopup = new MMapDefaultPopupMarker({description: 'Description', coordinates: CENTER}); - map.addChild(defaultPopup); - let descriptionElement = document.querySelector(`${BASE_SELECTOR} .mappable--default-popup_description`); - expect(descriptionElement.textContent).toBe('Description'); - - defaultPopup.update({description: 'New Description'}); - descriptionElement = document.querySelector(`${BASE_SELECTOR} .mappable--default-popup_description`); - expect(descriptionElement.textContent).toBe('New Description'); - }); - - it('action props', () => { - const defaultPopup = new MMapDefaultPopupMarker({action: 'Action', coordinates: CENTER}); - map.addChild(defaultPopup); - let actionButton = document.querySelector(`${BASE_SELECTOR} .mappable--default-popup_action`); - expect(actionButton.textContent).toBe('Action'); - - defaultPopup.update({action: 'New Action'}); - actionButton = document.querySelector(`${BASE_SELECTOR} .mappable--default-popup_action`); - expect(actionButton.textContent).toBe('New Action'); - }); - - describe('test buttons', () => { - it('click close button', (done) => { - const onClose = () => { - expect(defaultPopup.isOpen).toBe(false); - done(); - }; - const defaultPopup = new MMapDefaultPopupMarker({title: 'Title', coordinates: CENTER, onClose}); - map.addChild(defaultPopup); - - const closeButton = document.querySelector(`${BASE_SELECTOR} .mappable--default-popup_header_close`); - expect(closeButton).not.toBeNull(); - - const event = new MouseEvent('click'); - closeButton.dispatchEvent(event); - }); - it('click action button', (done) => { - const onAction = () => { - done(); - }; - const defaultPopup = new MMapDefaultPopupMarker({action: 'Action', coordinates: CENTER, onAction}); - map.addChild(defaultPopup); - - const actionButton = document.querySelector(`${BASE_SELECTOR} .mappable--default-popup_action`); - expect(actionButton).not.toBeNull(); - - const event = new MouseEvent('click'); - actionButton.dispatchEvent(event); - }); - }); -}); - -const BASE_SELECTOR = '.mappable--popup-marker .mappable--default-popup'; diff --git a/src/markers/MMapDefaultPopupMarker/close.svg b/src/markers/MMapDefaultPopupMarker/close.svg deleted file mode 100644 index 5c90cf8..0000000 --- a/src/markers/MMapDefaultPopupMarker/close.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/markers/MMapDefaultPopupMarker/index.css b/src/markers/MMapDefaultPopupMarker/index.css deleted file mode 100644 index 313b6b1..0000000 --- a/src/markers/MMapDefaultPopupMarker/index.css +++ /dev/null @@ -1,78 +0,0 @@ -.mappable--default-popup { - display: flex; - flex-direction: column; - row-gap: 8px; -} - -.mappable--default-popup_header { - display: flex; - flex-direction: row; - justify-content: space-between; - gap: 8px; - padding-right: 20px; - - .mappable--default-popup_header_title { - font-size: 16px; - font-weight: 400; - line-height: 22px; - color: #050d33; - } - .mappable--default-popup_header_close { - position: absolute; - top: 0; - right: 0; - display: flex; - justify-content: center; - align-items: center; - width: 32px; - height: 32px; - border: none; - background: none; - color: #c8c9cc; - cursor: pointer; - } -} - -.mappable--default-popup_description { - font-size: 14px; - font-weight: 400; - line-height: 20px; - color: #7b7d85; -} - -.mappable--default-popup_action { - width: max-content; - padding: 12px 16px; - border-radius: 8px; - border: none; - background-color: #eefd7d; - transition: background-color 0.1s ease-out; - color: #050d33; - text-align: center; - cursor: pointer; - white-space: normal; - - font-size: 14px; - font-style: normal; - font-weight: 500; - line-height: 16px; -} - -.mappable--default-popup_action:hover { - background-color: #e5fd30; -} - -.mappable--default-popup__dark { - .mappable--default-popup_header_title { - color: #f2f5fa; - } - .mappable--default-popup_header_close { - color: #46464d; - } - .mappable--default-popup_description { - color: #7b7d85; - } - .mappable--default-popup_action { - background-color: #d6fd63; - } -} diff --git a/src/markers/MMapDefaultPopupMarker/index.ts b/src/markers/MMapDefaultPopupMarker/index.ts deleted file mode 100644 index a6c3797..0000000 --- a/src/markers/MMapDefaultPopupMarker/index.ts +++ /dev/null @@ -1,128 +0,0 @@ -import {MMapPopupContentProps, MMapPopupMarker, MMapPopupMarkerProps} from '../MMapPopupMarker'; -import {MMapDefaultPopupMarkerVuefyOptions} from './vue'; - -import closeSVG from './close.svg'; -import './index.css'; - -export type MMapDefaultPopupMarkerProps = Omit & { - /** Displayed title in popup header */ - title?: string; - /** Displayed description */ - description?: string; - /** The description on the action button */ - action?: string; - /** Callback of click the action button */ - onAction?: () => void; -}; -/** - * `MMapDefaultPopupMarker` is a default popup that contains a title, a description, and an action button. - * @example - * ```js - * const defaultPopup = new MMapDefaultPopupMarker({ - * title: 'Default title', - * description: 'Default description', - * action: 'Make action', - * // support MMapMarker props - * coordinates: POPUP_COORD, - * draggable: true, - * // support MMapPopupMarker props - * position: 'top', - * }); - * map.addChild(defaultPopup); - * ``` - */ -export class MMapDefaultPopupMarker extends mappable.MMapComplexEntity { - static [mappable.optionsKeyVuefy] = MMapDefaultPopupMarkerVuefyOptions; - private _popup: MMapPopupMarker; - private _element: HTMLElement; - - public get isOpen() { - return this._popup.isOpen; - } - - protected _onAttach(): void { - const {title, description, action} = this._props; - - if (title === undefined && description === undefined && action === undefined) { - throw new Error( - 'There is no content to display. Specify one of the parameters: title, description, action' - ); - } - - this._popup = new MMapPopupMarker({ - ...this._props, - content: this.__createDefaultPopup, - onClose: () => { - this._props.show = false; - this._props.onClose?.(); - }, - onOpen: () => { - this._props.show = true; - this._props.onOpen?.(); - } - }); - this.addChild(this._popup); - - this._watchContext(mappable.ThemeContext, () => this._updateTheme(), {immediate: true}); - } - - protected _onUpdate(propsDiff: Partial, oldProps: MMapDefaultPopupMarkerProps): void { - const {title, description, action} = this._props; - - const isTitleChange = oldProps.title !== title; - const isDescriptionChange = oldProps.description !== description; - const isActionChange = oldProps.action !== action; - - if (isTitleChange || isDescriptionChange || isActionChange) { - this._popup.update({content: () => this.__createDefaultPopup()}); - } - - this._popup.update(this._props); - } - - private _updateTheme() { - const themeCtx = this._consumeContext(mappable.ThemeContext); - this._element.classList.toggle('mappable--default-popup__dark', themeCtx.theme === 'dark'); - } - - private __createDefaultPopup: MMapPopupContentProps = () => { - const {title, description, action} = this._props; - this._element = document.createElement('mappable'); - this._element.classList.add('mappable--default-popup'); - - const popupHeaderElement = document.createElement('mappable'); - popupHeaderElement.classList.add('mappable--default-popup_header'); - this._element.appendChild(popupHeaderElement); - - if (title) { - const titleElement = document.createElement('mappable'); - titleElement.classList.add('mappable--default-popup_header_title'); - titleElement.textContent = title; - popupHeaderElement.appendChild(titleElement); - } - - const closeButton = document.createElement('button'); - closeButton.classList.add('mappable--default-popup_header_close'); - closeButton.innerHTML = closeSVG; - closeButton.addEventListener('click', () => this._popup.update({show: false})); - popupHeaderElement.appendChild(closeButton); - - if (description) { - const descriptionElement = document.createElement('mappable'); - descriptionElement.classList.add('mappable--default-popup_description'); - descriptionElement.textContent = description; - this._element.appendChild(descriptionElement); - } - if (action) { - const actionButton = document.createElement('button'); - actionButton.classList.add('mappable--default-popup_action'); - actionButton.textContent = action; - actionButton.addEventListener('click', () => { - this._props.onAction?.(); - }); - this._element.appendChild(actionButton); - } - - return this._element; - }; -} diff --git a/src/markers/MMapDefaultPopupMarker/vue/index.ts b/src/markers/MMapDefaultPopupMarker/vue/index.ts deleted file mode 100644 index feec40b..0000000 --- a/src/markers/MMapDefaultPopupMarker/vue/index.ts +++ /dev/null @@ -1,35 +0,0 @@ -import {MMapFeatureProps, MMapMarkerEventHandler} from '@mappable-world/mappable-types'; -import {CustomVuefyOptions} from '@mappable-world/mappable-types/modules/vuefy'; -import type TVue from '@vue/runtime-core'; -import {MMapDefaultPopupMarker, MMapDefaultPopupMarkerProps} from '../'; - -export const MMapDefaultPopupMarkerVuefyOptions: CustomVuefyOptions = { - props: { - coordinates: {type: Object, required: true}, - source: String, - zIndex: {type: Number, default: 0}, - properties: Object, - id: String, - disableRoundCoordinates: {type: Boolean, default: undefined}, - hideOutsideViewport: {type: [Object, Boolean], default: false}, - draggable: {type: Boolean, default: false}, - mapFollowsOnDrag: {type: [Boolean, Object]}, - onDragStart: Function as TVue.PropType, - onDragEnd: Function as TVue.PropType, - onDragMove: Function as TVue.PropType, - blockEvents: {type: Boolean, default: undefined}, - blockBehaviors: {type: Boolean, default: undefined}, - onDoubleClick: Function as TVue.PropType, - onClick: Function as TVue.PropType, - onFastClick: Function as TVue.PropType, - position: {type: String as TVue.PropType}, - offset: {type: Number, default: 0}, - show: {type: Boolean, default: true}, - onClose: {type: Function as TVue.PropType}, - onOpen: {type: Function as TVue.PropType}, - title: {type: String}, - description: {type: String}, - action: {type: String}, - onAction: {type: Function as TVue.PropType} - } -}; diff --git a/src/markers/MMapPopupMarker/MMapPopupMarker.test.ts b/src/markers/MMapPopupMarker/MMapPopupMarker.test.ts index e919986..3dce9e0 100644 --- a/src/markers/MMapPopupMarker/MMapPopupMarker.test.ts +++ b/src/markers/MMapPopupMarker/MMapPopupMarker.test.ts @@ -24,6 +24,16 @@ describe('MMapPopupMarker', () => { expect(document.querySelector('.mappable--popup-marker')).not.toBeNull(); expect(document.querySelector('.mappable--popup-marker .test-popup')).not.toBeNull(); }); + it('add on map with text', () => { + const popup = new MMapPopupMarker({coordinates: CENTER, content: 'test popup'}); + map.addChild(popup); + + expect(document.querySelector('.mappable--popup-marker')).not.toBeNull(); + expect(document.querySelector('.mappable--popup-marker .mappable--popup-marker_container').textContent).toBe( + 'test popup' + ); + }); + it('changing show props', () => { const popup = new MMapPopupMarker({show: true, coordinates: CENTER, content: createPopupContent}); map.addChild(popup); diff --git a/src/markers/MMapPopupMarker/index.ts b/src/markers/MMapPopupMarker/index.ts index ee48fd6..9f65c77 100644 --- a/src/markers/MMapPopupMarker/index.ts +++ b/src/markers/MMapPopupMarker/index.ts @@ -13,7 +13,7 @@ export type MMapPopupPositionProps = | `${VerticalPosition} ${HorizontalPosition}` | `${HorizontalPosition} ${VerticalPosition}`; -export type MMapPopupContentProps = () => HTMLElement; +export type MMapPopupContentProps = string | (() => HTMLElement); export type MMapPopupMarkerProps = MMapMarkerProps & { /** The function of creating popup content */ @@ -83,7 +83,12 @@ export class MMapPopupMarker extends mappable.MMapComplexEntity TReact.ReactElement; + content: string | (() => TReact.ReactElement); } > >; @@ -28,7 +28,11 @@ export const MMapPopupMarkerReactifyOverride: CustomReactify(); const popup = React.useMemo(() => { - setContent(props.content()); + if (typeof props.content === 'string') { + setContent(<>{props.content}); + } else { + setContent(props.content()); + } return () => popupElement; }, [props.content, popupElement]); diff --git a/src/markers/MMapPopupMarker/vue/index.ts b/src/markers/MMapPopupMarker/vue/index.ts index 42a6c22..2d26804 100644 --- a/src/markers/MMapPopupMarker/vue/index.ts +++ b/src/markers/MMapPopupMarker/vue/index.ts @@ -22,7 +22,7 @@ export const MMapPopupMarkerVuefyOptions: CustomVuefyOptions = onDoubleClick: Function as TVue.PropType, onClick: Function as TVue.PropType, onFastClick: Function as TVue.PropType, - content: {type: Function as TVue.PropType, required: true}, + content: {type: [Function, String] as TVue.PropType, required: true}, position: {type: String as TVue.PropType}, offset: {type: Number, default: 0}, show: {type: Boolean, default: true}, diff --git a/src/markers/MMapTextPopupMarker/MMapTextPopupMarker.test.ts b/src/markers/MMapTextPopupMarker/MMapTextPopupMarker.test.ts deleted file mode 100644 index f6a0a8b..0000000 --- a/src/markers/MMapTextPopupMarker/MMapTextPopupMarker.test.ts +++ /dev/null @@ -1,37 +0,0 @@ -import {MMap} from '@mappable-world/mappable-types'; -import {CENTER, createContainer} from '../../../tests/common'; -import {MMapTextPopupMarker} from './'; - -describe('MMapTextPopupMarker', () => { - let map: MMap; - let container: HTMLElement; - - beforeEach(() => { - container = createContainer(); - document.body.append(container); - map = new mappable.MMap(container, {location: {center: CENTER, zoom: 0}}); - map.addChild(new mappable.MMapDefaultFeaturesLayer({})); - }); - - afterEach(() => { - map.destroy(); - }); - - it('add on map', () => { - const popup = new MMapTextPopupMarker({coordinates: CENTER, content: 'Popup'}); - map.addChild(popup); - - expect(document.querySelector('.mappable--popup-marker .mappable--default-text-popup')).not.toBeNull(); - }); - it('change content props', () => { - const popup = new MMapTextPopupMarker({coordinates: CENTER, content: 'Popup'}); - map.addChild(popup); - const popupElement = document.querySelector( - '.mappable--popup-marker .mappable--default-text-popup' - ); - expect(popupElement.textContent).toBe('Popup'); - - popup.update({content: 'New content'}); - expect(popupElement.textContent).toBe('New content'); - }); -}); diff --git a/src/markers/MMapTextPopupMarker/index.css b/src/markers/MMapTextPopupMarker/index.css deleted file mode 100644 index 2175dda..0000000 --- a/src/markers/MMapTextPopupMarker/index.css +++ /dev/null @@ -1,7 +0,0 @@ -.mappable--default-text-popup { - display: block; - max-width: inherit; - max-height: inherit; - overflow: hidden; - text-overflow: ellipsis; -} diff --git a/src/markers/MMapTextPopupMarker/index.ts b/src/markers/MMapTextPopupMarker/index.ts deleted file mode 100644 index 55b0aaf..0000000 --- a/src/markers/MMapTextPopupMarker/index.ts +++ /dev/null @@ -1,48 +0,0 @@ -import {MMapPopupMarker, MMapPopupMarkerProps} from '../MMapPopupMarker'; -import './index.css'; -import {MMapTextPopupMarkerVuefyOptions} from './vue'; - -export type MMapTextPopupMarkerProps = Omit & { - /** The text content that the popup will display */ - content: string; -}; -/** - * `MMapTextPopupMarker` - a text popup with no ability to close. - * @example - * ```js - * const popup = new MMapTextPopupMarker({ - * content:'Text popup', - * // support MMapMarker props - * coordinates: POPUP_COORD, - * draggable: true, - * // support MMapPopupMarker props - * position: 'top', - * }); - * map.addChild(popup); - * ``` - */ -export class MMapTextPopupMarker extends mappable.MMapComplexEntity { - static [mappable.optionsKeyVuefy] = MMapTextPopupMarkerVuefyOptions; - private _popup: MMapPopupMarker; - private _element: HTMLElement; - - protected _onAttach(): void { - this._popup = new MMapPopupMarker({...this._props, content: this.__createTextPopup}); - this.addChild(this._popup); - } - - protected _onUpdate(propsDiff: Partial): void { - if (propsDiff.content !== undefined) { - this._element.textContent = this._props.content; - } - const {content, ...propsWithoutContent} = this._props; - this._popup.update({...propsWithoutContent}); - } - - private __createTextPopup = (): HTMLElement => { - this._element = document.createElement('mappable'); - this._element.classList.add('mappable--default-text-popup'); - this._element.textContent = this._props.content; - return this._element; - }; -} diff --git a/src/markers/MMapTextPopupMarker/vue/index.ts b/src/markers/MMapTextPopupMarker/vue/index.ts deleted file mode 100644 index 4118765..0000000 --- a/src/markers/MMapTextPopupMarker/vue/index.ts +++ /dev/null @@ -1,29 +0,0 @@ -import {MMapFeatureProps, MMapMarkerEventHandler} from '@mappable-world/mappable-types'; -import {CustomVuefyOptions} from '@mappable-world/mappable-types/modules/vuefy'; -import type TVue from '@vue/runtime-core'; -import {MMapTextPopupMarker, MMapTextPopupMarkerProps} from '../'; - -export const MMapTextPopupMarkerVuefyOptions: CustomVuefyOptions = { - props: { - coordinates: {type: Object, required: true}, - source: String, - zIndex: {type: Number, default: 0}, - properties: Object, - id: String, - disableRoundCoordinates: {type: Boolean, default: undefined}, - hideOutsideViewport: {type: [Object, Boolean], default: false}, - draggable: {type: Boolean, default: false}, - mapFollowsOnDrag: {type: [Boolean, Object]}, - onDragStart: Function as TVue.PropType, - onDragEnd: Function as TVue.PropType, - onDragMove: Function as TVue.PropType, - blockEvents: {type: Boolean, default: undefined}, - blockBehaviors: {type: Boolean, default: undefined}, - onDoubleClick: Function as TVue.PropType, - onClick: Function as TVue.PropType, - onFastClick: Function as TVue.PropType, - content: {type: String, required: true}, - position: {type: String as TVue.PropType}, - offset: {type: Number, default: 0} - } -}; diff --git a/src/markers/index.ts b/src/markers/index.ts index 53fb089..8fa6200 100644 --- a/src/markers/index.ts +++ b/src/markers/index.ts @@ -1,4 +1,9 @@ -export * from './MMapDefaultMarker'; -export {MMapDefaultPopupMarker, MMapDefaultPopupMarkerProps} from './MMapDefaultPopupMarker'; +export { + MMapDefaultMarker, + MMapDefaultMarkerProps, + MarkerColorProps, + MarkerPopupProps, + MarkerSizeProps, + ThemesColor +} from './MMapDefaultMarker'; export {MMapPopupMarker, MMapPopupMarkerProps, MMapPopupPositionProps} from './MMapPopupMarker'; -export {MMapTextPopupMarker, MMapTextPopupMarkerProps} from './MMapTextPopupMarker'; From 3ed3960b74ea766b2be85364a67d58acfbfe4361 Mon Sep 17 00:00:00 2001 From: Matthew Kalinin Date: Tue, 14 May 2024 11:48:16 +0300 Subject: [PATCH 48/48] fix --- example/marker-popup/common.css | 0 example/marker-popup/react/index.html | 1 - example/marker-popup/vanilla/index.html | 1 - example/marker-popup/vue/index.html | 1 - src/markers/MMapPopupMarker/index.ts | 2 +- src/markers/index.ts | 2 +- 6 files changed, 2 insertions(+), 5 deletions(-) delete mode 100644 example/marker-popup/common.css diff --git a/example/marker-popup/common.css b/example/marker-popup/common.css deleted file mode 100644 index e69de29..0000000 diff --git a/example/marker-popup/react/index.html b/example/marker-popup/react/index.html index 4183912..b9c642a 100644 --- a/example/marker-popup/react/index.html +++ b/example/marker-popup/react/index.html @@ -29,7 +29,6 @@ > -
diff --git a/example/marker-popup/vanilla/index.html b/example/marker-popup/vanilla/index.html index 730390e..e4d869a 100644 --- a/example/marker-popup/vanilla/index.html +++ b/example/marker-popup/vanilla/index.html @@ -27,7 +27,6 @@ > -
diff --git a/example/marker-popup/vue/index.html b/example/marker-popup/vue/index.html index 85eeebd..9471aa8 100644 --- a/example/marker-popup/vue/index.html +++ b/example/marker-popup/vue/index.html @@ -28,7 +28,6 @@ > -
diff --git a/src/markers/MMapPopupMarker/index.ts b/src/markers/MMapPopupMarker/index.ts index 9f65c77..7fee34d 100644 --- a/src/markers/MMapPopupMarker/index.ts +++ b/src/markers/MMapPopupMarker/index.ts @@ -38,7 +38,7 @@ type DefaultProps = typeof defaultProps; * @example * ```js * const popup = new MMapPopupMarker({ - * content: (close) => createPopupContentHTMLElement(close), + * content: () => createPopupContentHTMLElement(), * position: 'top', * onOpen:() => console.log('open'), * onClose:() => console.log('close'), diff --git a/src/markers/index.ts b/src/markers/index.ts index 8fa6200..2d182cb 100644 --- a/src/markers/index.ts +++ b/src/markers/index.ts @@ -6,4 +6,4 @@ export { MarkerSizeProps, ThemesColor } from './MMapDefaultMarker'; -export {MMapPopupMarker, MMapPopupMarkerProps, MMapPopupPositionProps} from './MMapPopupMarker'; +export {MMapPopupMarker, MMapPopupMarkerProps, MMapPopupPositionProps, MMapPopupContentProps} from './MMapPopupMarker';