diff --git a/.claude/settings.local.json b/.claude/settings.local.json new file mode 100644 index 0000000..7867010 --- /dev/null +++ b/.claude/settings.local.json @@ -0,0 +1,7 @@ +{ + "permissions": { + "allow": [ + "Bash(npm ls:*)" + ] + } +} diff --git a/lerna.json b/lerna.json index b0d0fc5..791bfbf 100644 --- a/lerna.json +++ b/lerna.json @@ -2,5 +2,5 @@ "packages": [ "packages/*" ], - "version": "2.0.19" + "version": "2.1.0-alpha.0" } diff --git a/packages/react-ui/package-lock.json b/packages/react-ui/package-lock.json index 0893918..07fdc28 100644 --- a/packages/react-ui/package-lock.json +++ b/packages/react-ui/package-lock.json @@ -1,17 +1,17 @@ { "name": "datocms-react-ui", - "version": "2.0.19", + "version": "2.1.0-alpha.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "datocms-react-ui", - "version": "2.0.19", + "version": "2.1.0-alpha.0", "license": "MIT", "dependencies": { "@floating-ui/react": "^0.27.16", "classnames": "^2.3.1", - "datocms-plugin-sdk": "^2.0.18", + "datocms-plugin-sdk": "^2.1.0-alpha.0", "react-intersection-observer": "^8.31.0", "react-select": "^5.2.1", "scroll-into-view-if-needed": "^2.2.20" diff --git a/packages/react-ui/package.json b/packages/react-ui/package.json index 4d2d24f..936cf91 100644 --- a/packages/react-ui/package.json +++ b/packages/react-ui/package.json @@ -1,6 +1,6 @@ { "name": "datocms-react-ui", - "version": "2.0.19", + "version": "2.1.0-alpha.0", "description": "React components to use inside DatoCMS plugins", "keywords": [ "datocms", @@ -42,7 +42,7 @@ "dependencies": { "@floating-ui/react": "^0.27.16", "classnames": "^2.3.1", - "datocms-plugin-sdk": "^2.0.18", + "datocms-plugin-sdk": "^2.1.0-alpha.0", "react-intersection-observer": "^8.31.0", "react-select": "^5.2.1", "scroll-into-view-if-needed": "^2.2.20" diff --git a/packages/sdk/package-lock.json b/packages/sdk/package-lock.json index eccac2f..a042fd1 100644 --- a/packages/sdk/package-lock.json +++ b/packages/sdk/package-lock.json @@ -1,17 +1,18 @@ { "name": "datocms-plugin-sdk", - "version": "2.0.18", + "version": "2.1.0-alpha.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "datocms-plugin-sdk", - "version": "2.0.18", + "version": "2.1.0-alpha.0", "license": "MIT", "dependencies": { "@datocms/cma-client": "*", "@types/react": "^17.0.3", "datocms-structured-text-utils": "^2.0.0", + "emoji-regex-xs": "*", "penpal": "^4.1.1" }, "devDependencies": { @@ -200,6 +201,15 @@ "dev": true, "license": "MIT" }, + "node_modules/emoji-regex-xs": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/emoji-regex-xs/-/emoji-regex-xs-2.0.1.tgz", + "integrity": "sha512-1QFuh8l7LqUcKe24LsPUNzjrzJQ7pgRwp1QMcZ5MX6mFplk2zQ08NVCM84++1cveaUUYtcCYHmeFEuNg16sU4g==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/foreground-child": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", @@ -782,6 +792,11 @@ "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", "dev": true }, + "emoji-regex-xs": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/emoji-regex-xs/-/emoji-regex-xs-2.0.1.tgz", + "integrity": "sha512-1QFuh8l7LqUcKe24LsPUNzjrzJQ7pgRwp1QMcZ5MX6mFplk2zQ08NVCM84++1cveaUUYtcCYHmeFEuNg16sU4g==" + }, "foreground-child": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", diff --git a/packages/sdk/package.json b/packages/sdk/package.json index 95759b2..6b76b45 100644 --- a/packages/sdk/package.json +++ b/packages/sdk/package.json @@ -1,6 +1,6 @@ { "name": "datocms-plugin-sdk", - "version": "2.0.18", + "version": "2.1.0-alpha.0", "description": "DatoCMS Plugin SDK", "keywords": [ "datocms", @@ -39,6 +39,7 @@ "@datocms/cma-client": "*", "@types/react": "^17.0.3", "datocms-structured-text-utils": "^2.0.0", + "emoji-regex-xs": "*", "penpal": "^4.1.1" }, "devDependencies": { diff --git a/packages/sdk/src/guardUtils.ts b/packages/sdk/src/guardUtils.ts index eefc1ca..43cc6a8 100644 --- a/packages/sdk/src/guardUtils.ts +++ b/packages/sdk/src/guardUtils.ts @@ -1,3 +1,7 @@ +import emojiRegex from 'emoji-regex-xs'; + +export type Emoji = string & { readonly __brand: 'Emoji' }; + export function isNullish(value: unknown): value is null | undefined { return value === null || value === undefined; } @@ -10,6 +14,15 @@ export function isString(value: unknown): value is string { return typeof value === 'string'; } +export function isEmoji(value: unknown): value is Emoji { + if (!isString(value)) return false; + + const regex = emojiRegex(); + const match = value.match(regex); + + return match !== null && match.length === 1 && match[0] === value; +} + export function isNumber(value: unknown): value is number { return typeof value === 'number'; } diff --git a/packages/sdk/src/icon.ts b/packages/sdk/src/icon.ts index b7eac44..e21787d 100644 --- a/packages/sdk/src/icon.ts +++ b/packages/sdk/src/icon.ts @@ -1,6 +1,10 @@ -import { isRecord, isString } from './guardUtils.js'; +import { isEmoji, isRecord, isString } from './guardUtils.js'; -export type Icon = AwesomeFontIconIdentifier | SvgDefinition; +export type Icon = AwesomeFontIconIdentifier | SvgDefinition | EmojiDefinition; + +export function isIcon(value: unknown): value is Icon { + return isString(value) || isSvgDefinition(value) || isEmojiDefinition(value); +} /** * Defines a custom SVG icon for use in DatoCMS plugins. @@ -40,16 +44,40 @@ export type SvgDefinition = { content: string; }; -export function isIcon(value: unknown): value is Icon { +export function isSvgDefinition(value: unknown): value is SvgDefinition { return ( - isString(value) || - (isRecord(value) && - value.type === 'svg' && - isString(value.viewBox) && - isString(value.content)) + isRecord(value) && + value.type === 'svg' && + isString(value.viewBox) && + isString(value.content) ); } +/** + * Defines an emoji icon for use in DatoCMS plugins. + * + * @example + * ```typescript + * const starIcon: EmojiDefinition = { + * type: 'emoji', + * emoji: '⭐' + * }; + * ``` + */ +export type EmojiDefinition = { + /** Always set to 'emoji' to indicate this is an emoji icon */ + type: 'emoji'; + + /** + * A string contaning a single emoji. + */ + emoji: string; +}; + +export function isEmojiDefinition(value: unknown): value is EmojiDefinition { + return isRecord(value) && value.type === 'emoji' && isEmoji(value.emoji); +} + /** * Font Awesome icon identifier for use in DatoCMS plugins. *