From 98df3fdde4cb157f9ffa485d8bb3d23c2da47795 Mon Sep 17 00:00:00 2001 From: Mike Wilkerson <11575183+mlwilkerson@users.noreply.github.com> Date: Wed, 5 Jun 2024 15:18:39 -0700 Subject: [PATCH] Dynamically load available familyStyles (#51) * families and styles in dropdown selects * capitalize Kit * halfway styling for putting family and styles in dropdowns * WIP: cleanup and generalize familyStyles * update familyStyles on initial kit metadata query * handle duotone custom icons * add fak and fakd to dropdowns when it's a kit * remove style filter and wire-up familyStyle dropdowns * remove obsolete code pertaining to hardcoded familyStyles and prefixes * fix pathData for iconUpload in test * WIP: compute familyStyle path segments from prefixes without hardcoding * remove obsolete test expectations * remove obsolete IconPrefix import * don't use watch after al * fix onKeyUp to only issue a new query with the query changes * change the defaultSearchResult to stop hardcoding pro familyStyles * dynamically build the defaultIcons query result to avoid harcoding pro familyStyles * handle duotone legacy form for familyStyle path segments * remove obsolete slots * rebuild auto-generated readmes * exclude custom icon familyStyles when building default icon search results * include kit and kit-duotone on family drop down only when present in the kit * format labels for drop down options * update handleQuery to take optional second arg for variables * for search input field, respond to onInput instead of onKeyUp * add selected attr to style drop down necessary to ensure that it re-renders when this.selectedFamily or this.selectedState changes * fix test * auto-formatting * update readmes from rebuild * update changelog * Update CHANGELOG to show new QueryHandler type signature * auto-formatting * Update CHANGELOG to mention duotone custom icons * update version and deps --------- Co-authored-by: Frances Botsford --- CHANGELOG.md | 69 +- packages/fa-icon-chooser-react/package.json | 4 +- packages/fa-icon-chooser/dev/runtime.js | 370 ++++---- packages/fa-icon-chooser/package-lock.json | 19 +- packages/fa-icon-chooser/package.json | 4 +- packages/fa-icon-chooser/src/components.d.ts | 8 +- .../fa-icon-chooser/fa-icon-chooser.css | 88 +- .../fa-icon-chooser/fa-icon-chooser.spec.ts | 49 +- .../fa-icon-chooser/fa-icon-chooser.tsx | 752 +++++------------ .../src/components/fa-icon-chooser/readme.md | 63 +- .../src/components/fa-icon/fa-icon.tsx | 19 +- .../src/components/fa-icon/readme.md | 27 +- .../components/fa-icon/test/fa-icon.spec.tsx | 2 +- .../src/utils/defaultIconsSearchResult.json | 790 ++---------------- packages/fa-icon-chooser/src/utils/slots.tsx | 36 +- packages/fa-icon-chooser/src/utils/utils.ts | 94 +-- 16 files changed, 690 insertions(+), 1704 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ff523c5..443dd58 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,73 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). --- +## [0.7.0](https://github.com/FortAwesome/fa-icon-chooser/releases/tag/0.7.0) - 2024-06-05 + +### Changed + +- Family and style selection are now based on drop down selections, and the available + values are retrieved from the GraphQL API. This removes the hardcoding of familyStyles + and lets the icon chooser work with all available familyStyles for the active version + of Font Awesome. +- Several slots for messages to indicate the unavailability of certain familyStyles + have been removed. They are no longer used, now that the UI only includes in + the drop down selectors available familyStyle combinations for the active version + of Font Awesome. + + The following slots have been removed: + - `light-requires-pro` + - `thin-requires-pro` + - `duotone-requires-pro` + - `sharp-solid-requires-pro` + - `sharp-regular-requires-pro` + - `sharp-light-requires-pro` + - `uploaded-requires-pro` + - `sharp-solid-style-filter-sr-message` + - `sharp-regular-style-filter-sr-message` + - `sharp-light-style-filter-sr-message` + - `solid-style-filter-sr-message` + - `regular-style-filter-sr-message` + - `light-style-filter-sr-message` + - `thin-style-filter-sr-message` + - `duotone-style-filter-sr-message` + - `brands-style-filter-sr-message` + - `uploaded-style-filter-sr-message` + +- The `QueryHandler` type now takes a second optional argument for variables. + (See also Breaking Changes)o + +### Fixed +- Duotone custom icons in kits now work correctly. + +### Breaking Changes + +- The icon chooser's queries now use variables, instead of interpolating all values + into a single query document string. Thus, any `QueryHandler` callback must be updated + to handle the query variables. + + Its type signature is now: + + ```typescript + export type QueryHandler = (document: string, variables?: object) => Promise; + ``` + + Suggestion: + + ```javascript + async function handleQuery(query, variables) { + const headers = buildHeaders() + const url = getApiUrl() + const body = JSON.stringify({query, variables}) + + return fetch(url, { + method: "POST", + headers, + body + }) + } + ``` + + ## [0.6.0](https://github.com/FortAwesome/fa-icon-chooser/releases/tag/0.6.0) - 2023-07-12 ### Changed @@ -44,4 +111,4 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p ### Added -- Initial version \ No newline at end of file +- Initial version diff --git a/packages/fa-icon-chooser-react/package.json b/packages/fa-icon-chooser-react/package.json index 583574b..cb370e5 100644 --- a/packages/fa-icon-chooser-react/package.json +++ b/packages/fa-icon-chooser-react/package.json @@ -1,7 +1,7 @@ { "name": "@fortawesome/fa-icon-chooser-react", "sideEffects": false, - "version": "0.6.0", + "version": "0.7.0", "license": "MIT", "private": false, "description": "React specific wrapper for @fortawesome/fa-icon-chooser", @@ -35,7 +35,7 @@ "typescript": "^4.3.2" }, "dependencies": { - "@fortawesome/fa-icon-chooser": "0.6.0" + "@fortawesome/fa-icon-chooser": "0.7.0" }, "peerDependencies": { "react": "^16 || ^17 || ^18", diff --git a/packages/fa-icon-chooser/dev/runtime.js b/packages/fa-icon-chooser/dev/runtime.js index 9ae2082..f7e3e58 100644 --- a/packages/fa-icon-chooser/dev/runtime.js +++ b/packages/fa-icon-chooser/dev/runtime.js @@ -1,268 +1,286 @@ // This dev-only module isn't processed by the bundler like the others, // so we can't use a node env var to set this. Just hardcode it in one // place at the top. -const API_URL='https://api.fontawesome.com' +const API_URL = "https://api.fontawesome.com"; const FaIconChooserDevExports = (function () { - let showingIconChooser = false - let localConfig = undefined - const localDevMissingMsg = 'DEV: your local dev config in local.json is required but has not yet been loaded.' + let showingIconChooser = false; + let localConfig = undefined; + const localDevMissingMsg = + "DEV: your local dev config in local.json is required but has not yet been loaded."; function getUrlText(url) { // To simulate a fatal error, uncomment the following line: // return Promise.reject('simulated fatal error') return fetch(url) - .then(response => { - if(response.ok) { - return response.text() - } else { - throw new Error(`DEBUG: bad query for url: ${url}`) - } - }) + .then((response) => { + if (response.ok) { + return response.text(); + } else { + throw new Error(`DEBUG: bad query for url: ${url}`); + } + }); } - function handleQuery(query) { + function handleQuery(query, variables) { return new Promise((resolve, reject) => { const headers = { - 'Content-Type': 'application/json' - } + "Content-Type": "application/json", + }; getAccessToken() - .then(token => { - - if(token) { - headers['Authorization'] = `Bearer ${ token }` - console.log('handleQuery: using fresh access token to issue authorized request') - } else { - console.log('handleQuery: no access token found -- sending an unauthorized request') - } - - return fetch( API_URL, { - method: 'POST', + .then((token) => { + if (token) { + headers["Authorization"] = `Bearer ${token}`; + console.log( + "handleQuery: using fresh access token to issue authorized request", + ); + } else { + console.log( + "handleQuery: no access token found -- sending an unauthorized request", + ); + } + + const cleanedQuery = query.replace(/\s+/g, " "); + + return fetch(API_URL, { + method: "POST", headers, - body: JSON.stringify({ query }) - }, - ) - }) - .then(response => { - if(response.ok) { - response.json() - .then(json => resolve(json)) - .catch(e => reject(e)) - } else { - reject('bad query') - } - }) - .catch(e => reject(e)) - }) + body: JSON.stringify({ + query: cleanedQuery, + variables, + }), + }); + }) + .then((response) => { + if (response.ok) { + response.json() + .then((json) => resolve(json)) + .catch((e) => reject(e)); + } else { + reject("bad query"); + } + }) + .catch((e) => reject(e)); + }); } function handleResult(result) { - const resultElement = document.querySelector("#result") - const preElement = document.createElement('pre') - const text = document.createTextNode(JSON.stringify(result.detail)) - preElement.appendChild(text) - resultElement.appendChild(preElement) + const resultElement = document.querySelector("#result"); + const preElement = document.createElement("pre"); + const text = document.createTextNode(JSON.stringify(result.detail)); + preElement.appendChild(text); + resultElement.appendChild(preElement); } function clearResult() { - document.querySelectorAll("#result pre").forEach(child => child.remove()) + document.querySelectorAll("#result pre").forEach((child) => child.remove()); } function addIconChooser(props) { - const container = document.querySelector('#fa-icon-chooser-container') - const el = document.createElement('fa-icon-chooser') - el.handleQuery = handleQuery - el.getUrlText = getUrlText - el.addEventListener('finish', handleResult) - - const slotFatalErrorHeader = document.createElement('p') - slotFatalErrorHeader.setAttribute('slot', 'fatal-error-heading') - const slotFatalErrorHeaderMsg = document.createTextNode('Fatal Error') - slotFatalErrorHeader.appendChild(slotFatalErrorHeaderMsg) - el.appendChild(slotFatalErrorHeader) - - Object.keys(props).map(prop => { - el.setAttribute(prop, props[prop]) - }) - - container.appendChild(el) + const container = document.querySelector("#fa-icon-chooser-container"); + const el = document.createElement("fa-icon-chooser"); + el.handleQuery = handleQuery; + el.getUrlText = getUrlText; + el.addEventListener("finish", handleResult); + + const slotFatalErrorHeader = document.createElement("p"); + slotFatalErrorHeader.setAttribute("slot", "fatal-error-heading"); + const slotFatalErrorHeaderMsg = document.createTextNode("Fatal Error"); + slotFatalErrorHeader.appendChild(slotFatalErrorHeaderMsg); + el.appendChild(slotFatalErrorHeader); + + Object.keys(props).map((prop) => { + el.setAttribute(prop, props[prop]); + }); + + container.appendChild(el); } function closeIconChooser() { - document.querySelector('fa-icon-chooser').remove() + document.querySelector("fa-icon-chooser").remove(); } function setupHead() { - if(!localConfig) throw new Error(localDevMissingMsg) + if (!localConfig) throw new Error(localDevMissingMsg); // If there's no head config, we have nothing left to do here. - if(!localConfig.head) return + if (!localConfig.head) return; - const { head } = localConfig + const { head } = localConfig; - Object.keys(head).map(elementType => { - const el = document.createElement(elementType) - Object.keys(head[elementType]).map(attr => { - el.setAttribute(attr, head[elementType][attr]) - }) - document.head.appendChild(el) - }) + Object.keys(head).map((elementType) => { + const el = document.createElement(elementType); + Object.keys(head[elementType]).map((attr) => { + el.setAttribute(attr, head[elementType][attr]); + }); + document.head.appendChild(el); + }); } function toggleIconChooser() { - if(showingIconChooser) { - closeIconChooser() + if (showingIconChooser) { + closeIconChooser(); - const toggleIconContainer = document.querySelector('#toggle-icon-container') - if(toggleIconContainer) { + const toggleIconContainer = document.querySelector( + "#toggle-icon-container", + ); + if (toggleIconContainer) { while (toggleIconContainer.firstChild) { - toggleIconContainer.removeChild(toggleIconContainer.firstChild) + toggleIconContainer.removeChild(toggleIconContainer.firstChild); } - const newIcon = document.createElement('i') - newIcon.setAttribute('class', 'fas fa-toggle-off') - toggleIconContainer.appendChild(newIcon) + const newIcon = document.createElement("i"); + newIcon.setAttribute("class", "fas fa-toggle-off"); + toggleIconContainer.appendChild(newIcon); } - clearResult() - showingIconChooser = false + clearResult(); + showingIconChooser = false; } else { - showIconChooser() + showIconChooser(); - const toggleIconContainer = document.querySelector('#toggle-icon-container') - if(toggleIconContainer) { + const toggleIconContainer = document.querySelector( + "#toggle-icon-container", + ); + if (toggleIconContainer) { while (toggleIconContainer.firstChild) { - toggleIconContainer.removeChild(toggleIconContainer.firstChild) + toggleIconContainer.removeChild(toggleIconContainer.firstChild); } - const newIcon = document.createElement('i') - newIcon.setAttribute('class', 'fas fa-toggle-on') - toggleIconContainer.appendChild(newIcon) + const newIcon = document.createElement("i"); + newIcon.setAttribute("class", "fas fa-toggle-on"); + toggleIconContainer.appendChild(newIcon); } - showingIconChooser = true + showingIconChooser = true; } } function loadLocalConfig() { - const failMsg = 'DEV: failed request to get local.json:' - - return fetch('/dev/local.json') - .then(response => { - if(response.ok){ - return response.json() - } else { - return Promise.reject(response) - } - }) - .then(config => { - localConfig = config - }) - .catch(e => { - console.error(failMsg, e) - return Promise.reject(failMsg) - }) + const failMsg = "DEV: failed request to get local.json:"; + + return fetch("/dev/local.json") + .then((response) => { + if (response.ok) { + return response.json(); + } else { + return Promise.reject(response); + } + }) + .then((config) => { + localConfig = config; + }) + .catch((e) => { + console.error(failMsg, e); + return Promise.reject(failMsg); + }); } function getAccessToken() { - const apiToken = localConfig && localConfig.apiToken - if(!apiToken) { + const apiToken = localConfig && localConfig.apiToken; + if (!apiToken) { // If there's no apiToken, then it's not an error to resolve an undefined access token. - return Promise.resolve(undefined) + return Promise.resolve(undefined); } - const tokenJSON = window.localStorage.getItem('token') - const tokenObj = tokenJSON ? JSON.parse(tokenJSON) : undefined - const freshToken = (tokenObj && Math.floor(Date.now() / 1000) <= tokenObj.expiresAtEpochSeconds) + const tokenJSON = window.localStorage.getItem("token"); + const tokenObj = tokenJSON ? JSON.parse(tokenJSON) : undefined; + const freshToken = (tokenObj && + Math.floor(Date.now() / 1000) <= tokenObj.expiresAtEpochSeconds) ? tokenObj.token - : undefined + : undefined; - if(freshToken) return Promise.resolve(freshToken) + if (freshToken) return Promise.resolve(freshToken); return fetch(`${API_URL}/token`, { - method: 'POST', + method: "POST", headers: { - authorization: `Bearer ${ localConfig.apiToken }` - } + authorization: `Bearer ${localConfig.apiToken}`, + }, }) - .then(response => { - if(response.ok) { - response.json() - .then(obj => { - const expiresAtEpochSeconds = Math.floor(Date.now() / 1000) + obj['expires_in'] - - // WARNING: storing an access token in localStorage may not be good enough - // security in other situations. This is a development-only situation - // intended to run on a local development machine, so this seems like - // good enough security for that use case. - window.localStorage.setItem( - 'token', - JSON.stringify({ - token: obj['access_token'], - expiresAtEpochSeconds + .then((response) => { + if (response.ok) { + response.json() + .then((obj) => { + const expiresAtEpochSeconds = Math.floor(Date.now() / 1000) + + obj["expires_in"]; + + // WARNING: storing an access token in localStorage may not be good enough + // security in other situations. This is a development-only situation + // intended to run on a local development machine, so this seems like + // good enough security for that use case. + window.localStorage.setItem( + "token", + JSON.stringify({ + token: obj["access_token"], + expiresAtEpochSeconds, + }), + ); }) - ) - }) - .catch(e => { - throw e - }) - } else { - const msg = 'DEV: unexpected token endpoint response' - console.error(msg, response) - throw new Error(msg) - } - }) - .catch(e => { - throw e - }) + .catch((e) => { + throw e; + }); + } else { + const msg = "DEV: unexpected token endpoint response"; + console.error(msg, response); + throw new Error(msg); + } + }) + .catch((e) => { + throw e; + }); } function showIconChooser() { - if(!localConfig) throw new Error(localDevMissingMsg) - if(!localConfig.props) throw new Error('DEV: missing props key in your local.json') + if (!localConfig) throw new Error(localDevMissingMsg); + if (!localConfig.props) { + throw new Error("DEV: missing props key in your local.json"); + } - const { props } = localConfig + const { props } = localConfig; - if(!props['kit-token']) { - if(!props.version) { - throw new Error('DEV: your local.json must have a props key with either a version subkey or a kit-token subkey') + if (!props["kit-token"]) { + if (!props.version) { + throw new Error( + "DEV: your local.json must have a props key with either a version subkey or a kit-token subkey", + ); } - addIconChooser(props) - return + addIconChooser(props); + return; } getAccessToken() - .then(_token => addIconChooser(props)) - .catch(e => { - throw e - }) + .then((_token) => addIconChooser(props)) + .catch((e) => { + throw e; + }); } function getLocalConfig() { - return localConfig + return localConfig; } loadLocalConfig() - .then(setupHead) - .catch(e => { - throw e - }) + .then(setupHead) + .catch((e) => { + throw e; + }); - document.addEventListener('DOMContentLoaded', function() { - const toggle = document.querySelector('#fa-icon-chooser-toggle') + document.addEventListener("DOMContentLoaded", function () { + const toggle = document.querySelector("#fa-icon-chooser-toggle"); - if(toggle) { - toggle.addEventListener('click', toggleIconChooser) + if (toggle) { + toggle.addEventListener("click", toggleIconChooser); } - }) + }); return { toggleIconChooser, handleQuery, handleResult, getLocalConfig, - getUrlText - } -})() + getUrlText, + }; +})(); -if('undefined' !== typeof module) { - module.exports = FaIconChooserDevExports +if ("undefined" !== typeof module) { + module.exports = FaIconChooserDevExports; } diff --git a/packages/fa-icon-chooser/package-lock.json b/packages/fa-icon-chooser/package-lock.json index a65c1c8..c52580b 100644 --- a/packages/fa-icon-chooser/package-lock.json +++ b/packages/fa-icon-chooser/package-lock.json @@ -1,15 +1,15 @@ { "name": "@fortawesome/fa-icon-chooser", - "version": "0.5.0", + "version": "0.7.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@fortawesome/fa-icon-chooser", - "version": "0.5.0", + "version": "0.7.0", "license": "MIT", "dependencies": { - "@fortawesome/fontawesome-common-types": "^6.4.0", + "@fortawesome/fontawesome-common-types": "^6.5.2", "@stencil/core": "^2.13.0", "lodash": "^4.17.21", "semver": "^6.3.0" @@ -588,9 +588,10 @@ "dev": true }, "node_modules/@fortawesome/fontawesome-common-types": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.4.0.tgz", - "integrity": "sha512-HNii132xfomg5QVZw0HwXXpN22s7VBHQBv9CeOu9tfJnhsWQNd2lmTNi8CSrnw5B+5YOmzu1UoPAyxaXsJ6RgQ==", + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.5.2.tgz", + "integrity": "sha512-gBxPg3aVO6J0kpfHNILc+NMhXnqHumFxOmjYCFfOiLZfwhnnfhtsdA2hfJlDnj+8PjAs6kKQPenOTKj3Rf7zHw==", + "hasInstallScript": true, "engines": { "node": ">=6" } @@ -4949,9 +4950,9 @@ "dev": true }, "@fortawesome/fontawesome-common-types": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.4.0.tgz", - "integrity": "sha512-HNii132xfomg5QVZw0HwXXpN22s7VBHQBv9CeOu9tfJnhsWQNd2lmTNi8CSrnw5B+5YOmzu1UoPAyxaXsJ6RgQ==" + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.5.2.tgz", + "integrity": "sha512-gBxPg3aVO6J0kpfHNILc+NMhXnqHumFxOmjYCFfOiLZfwhnnfhtsdA2hfJlDnj+8PjAs6kKQPenOTKj3Rf7zHw==" }, "@istanbuljs/load-nyc-config": { "version": "1.1.0", diff --git a/packages/fa-icon-chooser/package.json b/packages/fa-icon-chooser/package.json index d1c69b3..172f1b0 100644 --- a/packages/fa-icon-chooser/package.json +++ b/packages/fa-icon-chooser/package.json @@ -1,6 +1,6 @@ { "name": "@fortawesome/fa-icon-chooser", - "version": "0.6.0", + "version": "0.7.0", "description": "Font Awesome Icon Chooser", "main": "dist/index.cjs.js", "private": false, @@ -34,7 +34,7 @@ "format.check": "prettier --check ." }, "dependencies": { - "@fortawesome/fontawesome-common-types": "^6.4.0", + "@fortawesome/fontawesome-common-types": "^6.5.2", "@stencil/core": "^2.13.0", "lodash": "^4.17.21", "semver": "^6.3.0" diff --git a/packages/fa-icon-chooser/src/components.d.ts b/packages/fa-icon-chooser/src/components.d.ts index fdc3038..dc0d2d8 100644 --- a/packages/fa-icon-chooser/src/components.d.ts +++ b/packages/fa-icon-chooser/src/components.d.ts @@ -5,12 +5,13 @@ * It contains typing information for all components that exist in this project. */ import { HTMLStencilElement, JSXBase } from "@stencil/core/internal"; -import { IconDefinition, IconPrefix } from "@fortawesome/fontawesome-common-types"; import { IconChooserResult, IconUpload, UrlTextFetcher } from "./utils/utils"; +import { IconDefinition } from "@fortawesome/fontawesome-common-types"; import { QueryHandler } from "./components/fa-icon-chooser/fa-icon-chooser"; export namespace Components { interface FaIcon { "class": string; + "familyStylePathSegment": string; "getUrlText"?: UrlTextFetcher; "icon"?: IconDefinition; "iconUpload"?: IconUpload; @@ -18,7 +19,7 @@ export namespace Components { "name"?: string; "pro": boolean; "size"?: string; - "stylePrefix"?: IconPrefix; + "stylePrefix"?: string; "svgApi": any; "svgFetchBaseUrl"?: string; } @@ -66,6 +67,7 @@ declare global { declare namespace LocalJSX { interface FaIcon { "class"?: string; + "familyStylePathSegment"?: string; "getUrlText"?: UrlTextFetcher; "icon"?: IconDefinition; "iconUpload"?: IconUpload; @@ -73,7 +75,7 @@ declare namespace LocalJSX { "name"?: string; "pro"?: boolean; "size"?: string; - "stylePrefix"?: IconPrefix; + "stylePrefix"?: string; "svgApi"?: any; "svgFetchBaseUrl"?: string; } diff --git a/packages/fa-icon-chooser/src/components/fa-icon-chooser/fa-icon-chooser.css b/packages/fa-icon-chooser/src/components/fa-icon-chooser/fa-icon-chooser.css index 4f16def..abe5a27 100644 --- a/packages/fa-icon-chooser/src/components/fa-icon-chooser/fa-icon-chooser.css +++ b/packages/fa-icon-chooser/src/components/fa-icon-chooser/fa-icon-chooser.css @@ -6,9 +6,11 @@ --icon-details-preview-line-height: 1; --icon-details-unicode-glyphs-label-min-width: var(--spacing-5xl); --icon-listing-roomy-font-size: var(--font-size-2xl); /* 32px */ - --icon-listing-roomy-gap-x: var(--spacing-xl); /* 36px */ + --icon-listing-roomy-gap-x: var(--spacing-md); /* 16px */ --icon-listing-roomy-gap-y: var(--spacing-md); /* 16px */ --icon-listing-roomy-size: calc(var(--spacing-base) * 8); /* 128px */ + --input-border-color: var(--fa-navy); + --input-border-width: 0.15rem; } :host { @@ -24,71 +26,9 @@ .wrap-search { --input-with-icon-color: var(--fa-navy); - --input-border-color: var(--fa-navy); --input-placeholder-color: var(--fa-md-gravy); } -.icons-style-menu-listing .wrap-icons-style-choice { - position: relative; -} - -.icons-style-menu-listing .input-checkbox-custom { - display: none; -} - -.icons-style-menu-listing .icons-style-choice { - display: flex; - cursor: pointer; - border: 2px solid var(--fa-lt-gravy); - border-radius: var(--border-radius-md); - color: var(--fa-dk-blue); - text-transform: capitalize; - text-align: center; -} - -.icons-style-menu-listing .icons-style-choice:hover { - border-color: var(--fa-dk-blue); - background-color: var(--fa-dk-blue); - color: var(--white); -} - -.icons-style-menu-listing .input-checkbox-custom:checked ~ .icons-style-choice { - color: var(--white); - background-color: var(--fa-navy); - border: 2px solid var(--fa-navy); -} - -.icons-style-menu-listing .input-checkbox-custom:disabled ~ .icons-style-choice { - color: var(--fa-gravy); - background-color: var(--white); - border: 2px solid var(--fa-lt-gravy); - cursor: default; -} - -.wrap-icons-style-choice .disabled-tooltip { - transform: translate(-50%, -50%); - position: absolute; - top: -2.5rem; - left: 50%; - border-radius: var(--border-radius-md); - background-color: rgba(16, 50, 87, 0.9); - padding: var(--spacing-2xs) var(--spacing-sm); - color: var(--white); - text-align: center; - line-height: 1.3; - width: 160%; - display: none; -} - -.wrap-icons-style-choice .input-checkbox-custom:disabled ~ .icons-style-choice:hover + .disabled-tooltip { - display: block; -} - -.icons-style-menu-listing .icons-style-choice > span, -.icons-style-menu-listing .icons-style-choice > .svg-inline--fa { - vertical-align: middle; -} - .wrap-icon-listing { background: var(--fa-lt-gravy); padding: var(--spacing-lg) var(--spacing-md); @@ -137,25 +77,3 @@ button.icon fa-icon { --paragraph-margin-bottom: var(--spacing-md); --paragraph-margin-top: var(--spacing-2xl); } - -.icons-style-menu-listing { - flex-wrap: wrap; -} - -.icons-style-menu-listing .wrap-icons-style-choice:nth-child(4n) { - break-after: always; -} - -/* custom > tablet */ - -@media (min-width: 62.625rem) { - .icons-style-menu-listing { - flex-wrap: nowrap; - } -} - -@media (min-width: 89rem) { - .icons-style-menu-listing .icons-style-choice { - display: flex; - } -} diff --git a/packages/fa-icon-chooser/src/components/fa-icon-chooser/fa-icon-chooser.spec.ts b/packages/fa-icon-chooser/src/components/fa-icon-chooser/fa-icon-chooser.spec.ts index bd99c7b..afaea0b 100644 --- a/packages/fa-icon-chooser/src/components/fa-icon-chooser/fa-icon-chooser.spec.ts +++ b/packages/fa-icon-chooser/src/components/fa-icon-chooser/fa-icon-chooser.spec.ts @@ -1,6 +1,6 @@ import { newSpecPage } from '@stencil/core/testing'; import { FaIconChooser } from './fa-icon-chooser'; -import { defaultIcons } from '../../utils/utils'; +import { buildDefaultIconsSearchResult } from '../../utils/utils'; import { get } from 'lodash'; // TODO: tests @@ -66,6 +66,8 @@ describe('fa-icon-chooser', () => { } }); + expect(foundFaCss).toBe(true); + // the script should have been injected into the outer DOM's head const scriptsInHead = await page.doc.head.querySelectorAll('script'); let foundFaScript = false; @@ -77,35 +79,28 @@ describe('fa-icon-chooser', () => { expect(foundFaScript).toBe(true); + const defaultIcons = buildDefaultIconsSearchResult({ + classic: { + solid: { + prefix: 'fas', + }, + regular: { + prefix: 'far', + }, + brands: { + prefix: 'fab', + }, + }, + }); + // The initial default icons should have be shown get(defaultIcons, 'data.search', []) .filter(i => i.familyStylesByLicense.free.length > 0) - .forEach(({ id }) => { - expect(page.root.shadowRoot.innerHTML).toEqual(expect.stringMatching(new RegExp(` { + const isNonBrandIcon = familyStylesByLicense.free.some(({ family, style }) => 'brands' !== family && 'brands' !== style); + if (isNonBrandIcon) { + expect(page.root.shadowRoot.innerHTML).toEqual(expect.stringMatching(new RegExp(` { - const input = await page.root.shadowRoot.querySelector(`input#icons-style-${style}`); - expect(input['disabled']).toBe(true); - }); - - const enabledStyleFilters = ['solid', 'brands', 'regular']; - enabledStyleFilters.forEach(async style => { - const input = await page.root.shadowRoot.querySelector(`input#icons-style-${style}`); - expect(input['disabled']).toBe(false); - }); - - const checkedStyleFilters = ['solid', 'brands']; - checkedStyleFilters.forEach(async style => { - const input = await page.root.shadowRoot.querySelector(`input#icons-style-${style}`); - expect(input['checked']).toBe(true); - }); - - const uncheckedStyleFilters = ['regular']; - uncheckedStyleFilters.forEach(async style => { - const input = await page.root.shadowRoot.querySelector(`input#icons-style-${style}`); - expect(input['checked']).toBe(false); - }); }); }); diff --git a/packages/fa-icon-chooser/src/components/fa-icon-chooser/fa-icon-chooser.tsx b/packages/fa-icon-chooser/src/components/fa-icon-chooser/fa-icon-chooser.tsx index fc71c03..8b6c93e 100644 --- a/packages/fa-icon-chooser/src/components/fa-icon-chooser/fa-icon-chooser.tsx +++ b/packages/fa-icon-chooser/src/components/fa-icon-chooser/fa-icon-chooser.tsx @@ -1,30 +1,23 @@ -import { Component, Event, Element, EventEmitter, Prop, State, h } from '@stencil/core'; -import { get, size, debounce } from 'lodash'; +import { Component, Element, Event, EventEmitter, h, Prop, State } from '@stencil/core'; +import { capitalize, debounce, find, get, set, size } from 'lodash'; import { - freeCdnBaseUrl, - kitAssetsBaseUrl, + buildDefaultIconsSearchResult, buildIconChooserResult, + CONSOLE_MESSAGE_PREFIX, createFontAwesomeScriptElement, + freeCdnBaseUrl, + IconChooserResult, + IconLookup, IconUpload, - defaultIcons, - familyStyleToPrefix, IconUploadLookup, - IconChooserResult, - UrlTextFetcher, - CONSOLE_MESSAGE_PREFIX, isValidSemver, - IconLookup, + kitAssetsBaseUrl, + UrlTextFetcher, } from '../../utils/utils'; import { faSadTear, faTire } from '../../utils/icons'; import { slotDefaults } from '../../utils/slots'; -import { IconPrefix } from '@fortawesome/fontawesome-common-types'; -import semver from 'semver'; -export type QueryHandler = (document: string) => Promise; - -export type StyleFilters = { - [prefix in IconPrefix]: boolean; -}; +export type QueryHandler = (document: string, variables?: object) => Promise; type KitMetadata = { version: string; @@ -34,8 +27,6 @@ type KitMetadata = { iconUploads: Array | null; }; -const DISPLAY_NONE = { display: 'none' }; - /** * @slot fatal-error-heading - heading for fatal error message * @slot fatal-error-detail - details for fatal error message @@ -48,28 +39,11 @@ const DISPLAY_NONE = { display: 'none' }; * @slot search-field-placeholder - search field placeholder * @slot searching-free - Searching Free * @slot searching-pro - Searching Pro - * @slot light-requires-pro - tooltip for light style requiring Pro - * @slot thin-requires-pro - tooltip for thin style requiring Pro - * @slot duotone-requires-pro - message about requirements for accessing duotone icons - * @slot sharp-solid-requires-pro - message about requirements for accessing sharp solid icons - * @slot sharp-regular-requires-pro - message about requirements for accessing sharp regular icons - * @slot sharp-light-requires-pro - message about requirements for accessing sharp light icons - * @slot uploaded-requires-pro - message about requirements for accessing kit icon uploads * @slot kit-has-no-uploaded-icons - message about a kit having no icon uploads * @slot no-search-results-heading - no search results message heading * @slot no-search-results-detail - no seach results message detail * @slot suggest-icon-upload - message suggesting to try uploading a custom icon to a kit * @slot get-fontawesome-pro - message about getting Font Awesome Pro with link to fontawesome.com - * @slot sharp-solid-style-filter-sr-message - screen reader only message for style filter: sharp solid - * @slot sharp-regular-style-filter-sr-message - screen reader only message for style filter: sharp regular - * @slot sharp-light-style-filter-sr-message - screen reader only message for style filter: sharp light - * @slot solid-style-filter-sr-message - screen reader only message for style filter: solid - * @slot regular-style-filter-sr-message - screen reader only message for style filter: regular - * @slot light-style-filter-sr-message - screen reader only message for style filter: light - * @slot thin-style-filter-sr-message - screen reader only message for style filter: thin - * @slot duotone-style-filter-sr-message - screen reader only message for style filter: duotone - * @slot brands-style-filter-sr-message - screen reader only message for style filter: brands - * @slot uploaded-style-filter-sr-message - screen reader only message for style filter: uploaded */ @Component({ tag: 'fa-icon-chooser', @@ -80,27 +54,31 @@ export class FaIconChooser { /** * The host element for this component's Shadow DOM. */ - @Element() host: HTMLElement; + @Element() + host: HTMLElement; /** * A kit token identifying a kit in which to find icons. Takes precedent over * version prop if both are present. */ - @Prop() kitToken?: string; + @Prop() + kitToken?: string; /** * Version to use for finding and loading icons when kitToken is not provided. * Must be a valid semantic version, as parsed by the [semver NPM](https://www.npmjs.com/package/semver), * like 5.5.13 or 6.0.0-beta1. */ - @Prop() version?: string; + @Prop() + version?: string; /** * Placeholder text for search form. * * Use this to provide translatable text. */ - @Prop() searchInputPlaceholder?: string; + @Prop() + searchInputPlaceholder?: string; /** * Required callback function which is responsible for taking a given GraphQL @@ -115,14 +93,16 @@ export class FaIconChooser { * drive the Icon Chooser, it will be necessary to authorize GraphQL API requests * sent to api.fontawesome.com with the [`kits_read` scope](https://fontawesome.com/v5.15/how-to-use/graphql-api/auth/scopes). */ - @Prop() handleQuery: QueryHandler; + @Prop() + handleQuery: QueryHandler; /** * Callback function that returns the text body of a response that * corresponds to an HTTP GET request for the given URL. For example, it * would be the result of [Response.text()](https://developer.mozilla.org/en-US/docs/Web/API/Response/text). */ - @Prop() getUrlText: UrlTextFetcher; + @Prop() + getUrlText: UrlTextFetcher; /** * Clients of the Icon Chooser should listen for this event in order to handle @@ -136,34 +116,55 @@ export class FaIconChooser { }) finish: EventEmitter; - @State() query: string = ''; - - @State() isQuerying: boolean = false; - - @State() isInitialLoading: boolean = false; - - @State() hasQueried: boolean = false; - - @State() icons: IconLookup[] = []; - - @State() styleFilterEnabled: boolean = false; - - @State() styleFilters: StyleFilters = { - fas: false, - far: false, - fad: false, - fat: false, - fab: false, - fal: false, - fak: false, - fass: false, - fasr: false, - fasl: false, + @State() + query: string = ''; + + @State() + isQuerying: boolean = false; + + @State() + isInitialLoading: boolean = false; + + @State() + hasQueried: boolean = false; + + @State() + icons: IconLookup[] = []; + + @State() + kitMetadata: KitMetadata; + + @State() + fatalError: boolean = false; + + // familyStyles starts with only the values that would be present in any + // release, whether Pro or Free. After resolving an initial metadata query, + // it will be updated to include the familyStyles appropriate for the active + // version and license of Font Awesome. + @State() + familyStyles: object = { + classic: { + solid: { + prefix: 'fas', + }, + regular: { + prefix: 'far', + }, + brands: { + prefix: 'fab', + }, + }, }; - @State() kitMetadata: KitMetadata; + // This should be populated as a reverse lookup when updating familyStyles. + @State() + prefixToFamilyStyle: object = {}; - @State() fatalError: boolean = false; + @State() + selectedFamily: string = 'classic'; + + @State() + selectedStyle: string = 'solid'; svgApi?: any; @@ -175,22 +176,94 @@ export class FaIconChooser { activeSlotDefaults: any = {}; - constructor() { - this.toggleStyleFilter = this.toggleStyleFilter.bind(this); + familyNameToLabel(name: string): string { + return name; + } + + styleNameToLabel(name: string): string { + return name; + } + + getFamilies(): string[] { + return Object.keys(this.familyStyles); + } + + selectFamily(e: any): void { + const fam = e.target.value; + if ('string' === typeof fam && 'object' === typeof this.familyStyles[fam]) { + this.selectedFamily = fam; + const styles = this.getStylesForSelectedFamily(); + this.selectedStyle = styles[0]; + } + } + + selectStyle(e: any): void { + const style = e.target.value; + if ('string' === typeof style && 'string' === typeof this.selectedFamily && 'object' === typeof this.familyStyles[this.selectedFamily]) { + this.selectedStyle = style; + } + } + + getPrefixForFamilyStyle(family: string, style: string): string | undefined { + return get(this.familyStyles, [family, style, 'prefix']); + } + + getSelectedPrefix(): string | undefined { + return this.getPrefixForFamilyStyle(this.selectedFamily, this.selectedStyle); + } + + getStylesForSelectedFamily(): string[] { + if ('string' === typeof this.selectedFamily && 'object' === typeof this.familyStyles[this.selectedFamily]) { + return Object.keys(this.familyStyles[this.selectedFamily]); + } else { + return []; + } + } + + buildFamilyStyleReverseLookup(): void { + const acc = {}; + + for (const family in this.familyStyles) { + for (const style in this.familyStyles[family]) { + acc[this.familyStyles[family][style].prefix] = { family, style }; + } + } + + this.prefixToFamilyStyle = acc; + } + + prefixToFamilyStylePathSegment(prefix: string): string | undefined { + const family = get(this.prefixToFamilyStyle, [prefix, 'family']); + const style = get(this.prefixToFamilyStyle, [prefix, 'style']); + + if (!family || !style) { + return; + } + + if ('duotone' === family && 'solid' === style) { + return 'duotone'; + } + + return 'classic' === family ? style : `${family}-${style}`; } async loadKitMetadata() { const response = await this.handleQuery( ` - query { + query KitMetadata($token: String!) { me { - kit(token:"${this.kitToken}") { + kit(token: $token) { version technologySelected licenseSelected name release { version + familyStyles { + family + style + prefix + } } iconUploads { name @@ -198,12 +271,13 @@ export class FaIconChooser { version width height - path + pathData } } } } `, + { token: this.kitToken }, ); if (get(response, 'errors')) { @@ -213,12 +287,30 @@ export class FaIconChooser { const kit = get(response, 'data.me.kit'); this.kitMetadata = kit; + this.updateFamilyStyles(get(kit, 'release.familyStyles', [])); + + const kitFamilyStyles = []; + const iconUploads = get(response, 'data.me.kit.iconUploads', []); + + if (find(iconUploads, i => i.pathData.length === 1)) { + kitFamilyStyles.push({ family: 'kit', style: 'custom', prefix: 'fak' }); + } + + if (find(iconUploads, i => i.pathData.length > 1)) { + kitFamilyStyles.push({ family: 'kit-duotone', style: 'custom', prefix: 'fakd' }); + } + + if (kitFamilyStyles.length > 0) { + this.updateFamilyStyles(kitFamilyStyles); + } } - activateDefaultStyleFilters() { - this.styleFilterEnabled = true; - this.styleFilters.fas = true; - this.styleFilters.fab = true; + updateFamilyStyles(familyStyles: Array) { + for (const fs of familyStyles) { + set(this.familyStyles, [fs.family, fs.style, 'prefix'], fs.prefix); + } + + this.buildFamilyStyleReverseLookup(); } resolvedVersion() { @@ -253,6 +345,8 @@ export class FaIconChooser { } componentWillLoad() { + this.buildFamilyStyleReverseLookup(); + if (!this.kitToken && !isValidSemver(this.version)) { console.error(`${CONSOLE_MESSAGE_PREFIX}: either a kit-token or valid semantic version is required.`, this); this.fatalError = true; @@ -298,16 +392,10 @@ export class FaIconChooser { const css = document.createTextNode(dom.css()); style.appendChild(css); this.host.shadowRoot.appendChild(style); - this.defaultIcons = defaultIcons; + this.defaultIcons = buildDefaultIconsSearchResult(this.familyStyles); this.setIcons(this.defaultIcons, this.iconUploadsAsIconUploadLookups()); - this.activateDefaultStyleFilters(); - - if (this.mayHaveIconUploads() && size(get(this, 'kitMetadata.iconUploads')) > 0) { - this.styleFilters.fak = true; - } - this.commonFaIconProps = { svgApi: get(window, 'FontAwesome'), pro: this.pro(), @@ -332,8 +420,8 @@ export class FaIconChooser { const response = await this.handleQuery( ` - query { - search(version:"${this.resolvedVersion()}", query: "${query}", first: 100) { + query Search($version: String!, $query: String!) { + search(version: $version, query: $query, first: 100) { id label familyStylesByLicense { @@ -348,6 +436,7 @@ export class FaIconChooser { } } }`, + { version: this.resolvedVersion(), query }, ); const filteredIconUploads = this.iconUploadsAsIconUploadLookups().filter(({ iconName }) => { @@ -369,7 +458,8 @@ export class FaIconChooser { iconUploadsAsIconUploadLookups(): Array { return get(this, 'kitMetadata.iconUploads', []).map(i => { - return { prefix: 'fak', iconName: i.name, iconUpload: i }; + const [prefix, pathData] = i.pathData.length > 1 ? ['fakd', i.pathData] : ['fak', i.pathData[0]]; + return { prefix, iconName: i.name, iconUpload: { ...i, pathData } }; }); } @@ -379,10 +469,11 @@ export class FaIconChooser { const familyStyles = this.pro() ? familyStylesByLicense.pro : familyStylesByLicense.free; - familyStyles.map(familyStyle => { + familyStyles.map(fs => { + const prefix = this.getPrefixForFamilyStyle(fs.family, fs.style); acc.push({ iconName: id, - prefix: familyStyleToPrefix(familyStyle), + prefix, }); }); @@ -398,52 +489,13 @@ export class FaIconChooser { }, 500); filteredIcons(): Array { - if (!this.styleFilterEnabled) return this.icons; + const selectedPrefix = this.getSelectedPrefix(); - return this.icons.filter(({ prefix }) => this.styleFilters[prefix]); - } - - resetStyleFilter(): void { - Object.keys(this.styleFilters).forEach(style => { - this.styleFilters[style] = false; - }); - - this.styleFilterEnabled = false; - } - - isOnlyEnabledStyleFilter(style: string): boolean { - if (this.styleFilters[style]) { - const foundAnotherEnabledStyleFilter = !!Object.keys(this.styleFilters).find(styleFilter => { - if (styleFilter === style) return false; // the current style doesn't count - - return this.styleFilters[styleFilter]; - }); - - return !foundAnotherEnabledStyleFilter; + if (!selectedPrefix) { + return []; } - return false; - } - - showCheckedStyleIcon(style: string) { - return this.styleFilterEnabled && this.styleFilters[style]; - } - - toggleStyleFilter(style: string): void { - if (this.styleFilterEnabled) { - // If we're turning "off" the last style filter, this has the effect - // if disabling the master style filter as well. - if (this.isOnlyEnabledStyleFilter(style)) { - this.styleFilters = { ...this.styleFilters, [style]: !this.styleFilters[style] }; - this.styleFilterEnabled = false; - } else { - // simply toggle this style - this.styleFilters = { ...this.styleFilters, [style]: !this.styleFilters[style] }; - } - } else { - this.styleFilters = { ...this.styleFilters, [style]: true }; - this.styleFilterEnabled = true; - } + return this.icons.filter(({ prefix }) => prefix === selectedPrefix); } isV6() { @@ -451,22 +503,6 @@ export class FaIconChooser { return version && version[0] === '6'; } - isDuotoneAvailable() { - return this.pro() && !!this.resolvedVersion().match('(5.[1-9][0-9]+.)|^6.'); - } - - isSharpSolidAvailable() { - return this.pro() && semver.satisfies(this.resolvedVersion(), '>=6.2.0'); - } - - isSharpLightAvailable() { - return this.pro() && semver.satisfies(this.resolvedVersion(), '>=6.4.0'); - } - - isSharpRegularAvailable() { - return this.pro() && semver.satisfies(this.resolvedVersion(), '>=6.3.0'); - } - mayHaveIconUploads() { return this.pro(); } @@ -475,7 +511,7 @@ export class FaIconChooser { return size(get(this, 'kitMetadata.iconUploads')); } - onKeyUp(e: any): void { + onSearchInputChange(e: any): void { this.query = e.target.value; if (size(this.query) === 0) { this.setIcons(this.defaultIcons, this.iconUploadsAsIconUploadLookups()); @@ -489,15 +525,14 @@ export class FaIconChooser { e.stopPropagation(); } - render() { - const falDisabled = !this.pro(); - const faslDisabled = !this.isSharpLightAvailable(); - const fassDisabled = !this.isSharpSolidAvailable(); - const fasrDisabled = !this.isSharpRegularAvailable(); - const fatDisabled = !(this.isV6() && this.pro()); - const fadDisabled = !this.isDuotoneAvailable(); - const fakDisabled = !this.mayHaveIconUploads(); + labelForFamilyOrStyle(labelOrFamily: string): string { + return labelOrFamily + .split('-') + .map(term => capitalize(term)) + .join(' '); + } + render() { if (this.fatalError) { return (
@@ -525,392 +560,52 @@ export class FaIconChooser { -
+
-
-
- this.toggleStyleFilter('fas')} - type="checkbox" - name="icons-style" - class="input-checkbox-custom" - > - -
-
- this.toggleStyleFilter('far')} - type="checkbox" - name="icons-style" - class="input-checkbox-custom" - > - -
-
- this.toggleStyleFilter('fal')} - type="checkbox" - name="icons-style" - class="input-checkbox-custom" - > - - {this.slot('light-requires-pro')} -
-
- this.toggleStyleFilter('fat')} - type="checkbox" - name="icons-style" - class="input-checkbox-custom" - > - - {this.slot('thin-requires-pro')} -
-
- this.toggleStyleFilter('fad')} - type="checkbox" - name="icons-style" - class="input-checkbox-custom" - > - - {this.slot('duotone-requires-pro')} -
-
- this.toggleStyleFilter('fass')} - type="checkbox" - name="icons-style" - class="input-checkbox-custom" - > - - {this.slot('sharp-solid-requires-pro')} -
-
- this.toggleStyleFilter('fasr')} - type="checkbox" - name="icons-style" - class="input-checkbox-custom" - > - - {this.slot('sharp-regular-requires-pro')} -
-
- this.toggleStyleFilter('fasl')} - type="checkbox" - name="icons-style" - class="input-checkbox-custom" - > - - {this.slot('sharp-light-requires-pro')} -
-
- this.toggleStyleFilter('fab')} - type="checkbox" - name="icons-style" - class="input-checkbox-custom" - > - + +
+
+
-
- this.toggleStyleFilter('fak')} - type="checkbox" - name="icons-style" - class="input-checkbox-custom" - > - - {this.slot('uploaded-requires-pro')} + +
+
-

+

{this.pro() ? this.slot('searching-pro') : this.slot('searching-free')} {this.resolvedVersion()}

- {!this.isQuerying && this.mayHaveIconUploads() && !this.hasIconUploads() && this.styleFilterEnabled && this.styleFilters.fak && ( + {!this.isQuerying && this.mayHaveIconUploads() && !this.hasIconUploads() && ['kit', 'kit-duotone'].includes(this.selectedFamily) && (

{this.slot('kit-has-no-uploaded-icons')}

@@ -934,7 +629,14 @@ export class FaIconChooser { {this.filteredIcons().map(iconLookup => (
diff --git a/packages/fa-icon-chooser/src/components/fa-icon-chooser/readme.md b/packages/fa-icon-chooser/src/components/fa-icon-chooser/readme.md index 55bb006..08a6944 100644 --- a/packages/fa-icon-chooser/src/components/fa-icon-chooser/readme.md +++ b/packages/fa-icon-chooser/src/components/fa-icon-chooser/readme.md @@ -4,13 +4,13 @@ ## Properties -| Property | Attribute | Description | Type | Default | -| ------------------------ | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------ | ----------- | -| `getUrlText` | -- | Callback function that returns the text body of a response that corresponds to an HTTP GET request for the given URL. For example, it would be the result of [Response.text()](https://developer.mozilla.org/en-US/docs/Web/API/Response/text). | `(url: string) => Promise` | `undefined` | -| `handleQuery` | -- | Required callback function which is responsible for taking a given GraphQL query document and returns a Promise that resolves to a JavaScript object corresponding to the body of the associated network request, same as what would be produced by [Response.json()](https://developer.mozilla.org/en-US/docs/Web/API/Response/json). The query document is compliant with the GraphQL API at [api.fontawesome.com](https://fontawesome.com/v5.15/how-to-use/graphql-api/intro/getting-started). The implementation is responsible for handling any authorization that may be necessary to fulfill the request. For example, any time a kit is used to drive the Icon Chooser, it will be necessary to authorize GraphQL API requests sent to api.fontawesome.com with the [`kits_read` scope](https://fontawesome.com/v5.15/how-to-use/graphql-api/auth/scopes). | `(document: string) => Promise` | `undefined` | -| `kitToken` | `kit-token` | A kit token identifying a kit in which to find icons. Takes precedent over version prop if both are present. | `string` | `undefined` | -| `searchInputPlaceholder` | `search-input-placeholder` | Placeholder text for search form. Use this to provide translatable text. | `string` | `undefined` | -| `version` | `version` | Version to use for finding and loading icons when kitToken is not provided. Must be a valid semantic version, as parsed by the [semver NPM](https://www.npmjs.com/package/semver), like 5.5.13 or 6.0.0-beta1. | `string` | `undefined` | +| Property | Attribute | Description | Type | Default | +| ------------------------ | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------- | ----------- | +| `getUrlText` | -- | Callback function that returns the text body of a response that corresponds to an HTTP GET request for the given URL. For example, it would be the result of [Response.text()](https://developer.mozilla.org/en-US/docs/Web/API/Response/text). | `(url: string) => Promise` | `undefined` | +| `handleQuery` | -- | Required callback function which is responsible for taking a given GraphQL query document and returns a Promise that resolves to a JavaScript object corresponding to the body of the associated network request, same as what would be produced by [Response.json()](https://developer.mozilla.org/en-US/docs/Web/API/Response/json). The query document is compliant with the GraphQL API at [api.fontawesome.com](https://fontawesome.com/v5.15/how-to-use/graphql-api/intro/getting-started). The implementation is responsible for handling any authorization that may be necessary to fulfill the request. For example, any time a kit is used to drive the Icon Chooser, it will be necessary to authorize GraphQL API requests sent to api.fontawesome.com with the [`kits_read` scope](https://fontawesome.com/v5.15/how-to-use/graphql-api/auth/scopes). | `(document: string, variables?: object) => Promise` | `undefined` | +| `kitToken` | `kit-token` | A kit token identifying a kit in which to find icons. Takes precedent over version prop if both are present. | `string` | `undefined` | +| `searchInputPlaceholder` | `search-input-placeholder` | Placeholder text for search form. Use this to provide translatable text. | `string` | `undefined` | +| `version` | `version` | Version to use for finding and loading icons when kitToken is not provided. Must be a valid semantic version, as parsed by the [semver NPM](https://www.npmjs.com/package/semver), like 5.5.13 or 6.0.0-beta1. | `string` | `undefined` | ## Events @@ -20,37 +20,24 @@ ## Slots -| Slot | Description | -| --------------------------------------- | ------------------------------------------------------------------- | -| `"brands-style-filter-sr-message"` | screen reader only message for style filter: brands | -| `"duotone-requires-pro"` | message about requirements for accessing duotone icons | -| `"duotone-style-filter-sr-message"` | screen reader only message for style filter: duotone | -| `"fatal-error-detail"` | details for fatal error message | -| `"fatal-error-heading"` | heading for fatal error message | -| `"get-fontawesome-pro"` | message about getting Font Awesome Pro with link to fontawesome.com | -| `"initial-loading-view-detail"` | detail for initial loading view | -| `"initial-loading-view-heading"` | heading for initial loading view | -| `"kit-has-no-uploaded-icons"` | message about a kit having no icon uploads | -| `"light-requires-pro"` | tooltip for light style requiring Pro | -| `"light-style-filter-sr-message"` | screen reader only message for style filter: light | -| `"no-search-results-detail"` | no seach results message detail | -| `"no-search-results-heading"` | no search results message heading | -| `"regular-style-filter-sr-message"` | screen reader only message for style filter: regular | -| `"search-field-label-free"` | Search Font Awesome Free Icons | -| `"search-field-label-pro"` | Search Font Awesome Pro Icons | -| `"search-field-placeholder"` | search field placeholder | -| `"searching-free"` | Searching Free | -| `"searching-pro"` | Searching Pro | -| `"sharp-solid-requires-pro"` | message about requirements for accessing sharp solid icons | -| `"sharp-solid-style-filter-sr-message"` | screen reader only message for style filter: sharp solid | -| `"solid-style-filter-sr-message"` | screen reader only message for style filter: solid | -| `"start-view-detail"` | detail for message on default view before search | -| `"start-view-heading"` | heading for message on default view before search | -| `"suggest-icon-upload"` | message suggesting to try uploading a custom icon to a kit | -| `"thin-requires-pro"` | tooltip for thin style requiring Pro | -| `"thin-style-filter-sr-message"` | screen reader only message for style filter: thin | -| `"uploaded-requires-pro"` | message about requirements for accessing kit icon uploads | -| `"uploaded-style-filter-sr-message"` | screen reader only message for style filter: uploaded | +| Slot | Description | +| -------------------------------- | ------------------------------------------------------------------- | +| `"fatal-error-detail"` | details for fatal error message | +| `"fatal-error-heading"` | heading for fatal error message | +| `"get-fontawesome-pro"` | message about getting Font Awesome Pro with link to fontawesome.com | +| `"initial-loading-view-detail"` | detail for initial loading view | +| `"initial-loading-view-heading"` | heading for initial loading view | +| `"kit-has-no-uploaded-icons"` | message about a kit having no icon uploads | +| `"no-search-results-detail"` | no seach results message detail | +| `"no-search-results-heading"` | no search results message heading | +| `"search-field-label-free"` | Search Font Awesome Free Icons | +| `"search-field-label-pro"` | Search Font Awesome Pro Icons | +| `"search-field-placeholder"` | search field placeholder | +| `"searching-free"` | Searching Free | +| `"searching-pro"` | Searching Pro | +| `"start-view-detail"` | detail for message on default view before search | +| `"start-view-heading"` | heading for message on default view before search | +| `"suggest-icon-upload"` | message suggesting to try uploading a custom icon to a kit | ## Dependencies diff --git a/packages/fa-icon-chooser/src/components/fa-icon/fa-icon.tsx b/packages/fa-icon-chooser/src/components/fa-icon/fa-icon.tsx index c98cc86..022dc80 100644 --- a/packages/fa-icon-chooser/src/components/fa-icon/fa-icon.tsx +++ b/packages/fa-icon-chooser/src/components/fa-icon/fa-icon.tsx @@ -1,6 +1,6 @@ import { Component, Host, Prop, State, h } from '@stencil/core'; -import { IconUpload, PREFIX_TO_STYLE, parseSvgText, UrlTextFetcher, CONSOLE_MESSAGE_PREFIX } from '../../utils/utils'; -import { IconDefinition, IconPrefix } from '@fortawesome/fontawesome-common-types'; +import { IconUpload, parseSvgText, UrlTextFetcher, CONSOLE_MESSAGE_PREFIX } from '../../utils/utils'; +import { IconDefinition } from '@fortawesome/fontawesome-common-types'; import { get } from 'lodash'; /** @@ -15,7 +15,9 @@ import { get } from 'lodash'; export class FaIcon { @Prop() name?: string; - @Prop() stylePrefix?: IconPrefix; + @Prop() stylePrefix?: string; + + @Prop() familyStylePathSegment: string; @Prop() svgApi: any; @@ -42,9 +44,9 @@ export class FaIcon { componentWillLoad() { if (this.iconUpload) { this.iconDefinition = { - prefix: 'fak', + prefix: this.stylePrefix, iconName: this.iconUpload.name, - icon: [parseInt(`${this.iconUpload.width}`), parseInt(`${this.iconUpload.height}`), [], this.iconUpload.unicode.toString(16), this.iconUpload.path], + icon: [parseInt(`${this.iconUpload.width}`), parseInt(`${this.iconUpload.height}`), [], this.iconUpload.unicode.toString(16), this.iconUpload.pathData], }; return; @@ -66,6 +68,11 @@ export class FaIcon { return; } + if (!this.familyStylePathSegment) { + console.error(`${CONSOLE_MESSAGE_PREFIX}: fa-icon: the 'familyStylePathSegment' prop is required to render this icon but not provided.`, this); + return; + } + const { findIconDefinition } = this.svgApi; const iconDefinition = @@ -97,7 +104,7 @@ export class FaIcon { this.loading = true; - const iconUrl = `${this.svgFetchBaseUrl}/${PREFIX_TO_STYLE[this.stylePrefix]}/${this.name}.svg?token=${this.kitToken}`; + const iconUrl = `${this.svgFetchBaseUrl}/${this.familyStylePathSegment}/${this.name}.svg?token=${this.kitToken}`; const library = get(this, 'svgApi.library'); diff --git a/packages/fa-icon-chooser/src/components/fa-icon/readme.md b/packages/fa-icon-chooser/src/components/fa-icon/readme.md index 48d212d..33636f8 100644 --- a/packages/fa-icon-chooser/src/components/fa-icon/readme.md +++ b/packages/fa-icon-chooser/src/components/fa-icon/readme.md @@ -10,19 +10,20 @@ may change as suits the needs of the Icon Chooser. ## Properties -| Property | Attribute | Description | Type | Default | -| ----------------- | -------------------- | ----------- | -------------------------------------------------------------------------------------------------- | ----------- | -| `class` | `class` | | `string` | `undefined` | -| `getUrlText` | -- | | `(url: string) => Promise` | `undefined` | -| `icon` | -- | | `IconDefinition` | `undefined` | -| `iconUpload` | -- | | `{ name: string; unicode: number; version: number; width: string; height: string; path: string; }` | `undefined` | -| `kitToken` | `kit-token` | | `string` | `undefined` | -| `name` | `name` | | `string` | `undefined` | -| `pro` | `pro` | | `boolean` | `false` | -| `size` | `size` | | `string` | `undefined` | -| `stylePrefix` | `style-prefix` | | `"fab" \| "fad" \| "fak" \| "fal" \| "far" \| "fas" \| "fass" \| "fat"` | `undefined` | -| `svgApi` | `svg-api` | | `any` | `undefined` | -| `svgFetchBaseUrl` | `svg-fetch-base-url` | | `string` | `undefined` | +| Property | Attribute | Description | Type | Default | +| ------------------------ | --------------------------- | ----------- | ------------------------------------------------------------------------------------------------------ | ----------- | +| `class` | `class` | | `string` | `undefined` | +| `familyStylePathSegment` | `family-style-path-segment` | | `string` | `undefined` | +| `getUrlText` | -- | | `(url: string) => Promise` | `undefined` | +| `icon` | -- | | `IconDefinition` | `undefined` | +| `iconUpload` | -- | | `{ name: string; unicode: number; version: number; width: string; height: string; pathData: string; }` | `undefined` | +| `kitToken` | `kit-token` | | `string` | `undefined` | +| `name` | `name` | | `string` | `undefined` | +| `pro` | `pro` | | `boolean` | `false` | +| `size` | `size` | | `string` | `undefined` | +| `stylePrefix` | `style-prefix` | | `string` | `undefined` | +| `svgApi` | `svg-api` | | `any` | `undefined` | +| `svgFetchBaseUrl` | `svg-fetch-base-url` | | `string` | `undefined` | ## Dependencies diff --git a/packages/fa-icon-chooser/src/components/fa-icon/test/fa-icon.spec.tsx b/packages/fa-icon-chooser/src/components/fa-icon/test/fa-icon.spec.tsx index 1da4d92..4b7d694 100644 --- a/packages/fa-icon-chooser/src/components/fa-icon/test/fa-icon.spec.tsx +++ b/packages/fa-icon-chooser/src/components/fa-icon/test/fa-icon.spec.tsx @@ -56,7 +56,7 @@ describe('fa-icon', () => { version: 1, width: '123', height: '456', - path: 'M100 100h50v50z', + pathData: ['M100 100h50v50z'], }, }); diff --git a/packages/fa-icon-chooser/src/utils/defaultIconsSearchResult.json b/packages/fa-icon-chooser/src/utils/defaultIconsSearchResult.json index 9f65df3..a919ce3 100644 --- a/packages/fa-icon-chooser/src/utils/defaultIconsSearchResult.json +++ b/packages/fa-icon-chooser/src/utils/defaultIconsSearchResult.json @@ -9,16 +9,7 @@ { "family": "classic", "style": "solid" }, { "family": "classic", "style": "regular" } ], - "pro": [ - { "family": "classic", "style": "solid" }, - { "family": "classic", "style": "regular" }, - { "family": "classic", "style": "light" }, - { "family": "classic", "style": "thin" }, - { "family": "duotone", "style": "solid" }, - { "family": "sharp", "style": "solid" }, - { "family": "sharp", "style": "regular" }, - { "family": "sharp", "style": "light" } - ] + "pro": "ALL" } }, { @@ -34,16 +25,7 @@ "label": "Check", "familyStylesByLicense": { "free": [{ "family": "classic", "style": "solid" }], - "pro": [ - { "family": "classic", "style": "solid" }, - { "family": "classic", "style": "regular" }, - { "family": "classic", "style": "light" }, - { "family": "classic", "style": "thin" }, - { "family": "duotone", "style": "solid" }, - { "family": "sharp", "style": "solid" }, - { "family": "sharp", "style": "regular" }, - { "family": "sharp", "style": "light" } - ] + "pro": "ALL" } }, { @@ -51,16 +33,7 @@ "label": "Phone", "familyStylesByLicense": { "free": [{ "family": "classic", "style": "solid" }], - "pro": [ - { "family": "classic", "style": "solid" }, - { "family": "classic", "style": "regular" }, - { "family": "classic", "style": "light" }, - { "family": "classic", "style": "thin" }, - { "family": "duotone", "style": "solid" }, - { "family": "sharp", "style": "solid" }, - { "family": "sharp", "style": "regular" }, - { "family": "sharp", "style": "light" } - ] + "pro": "ALL" } }, { @@ -68,16 +41,7 @@ "label": "plus", "familyStylesByLicense": { "free": [{ "family": "classic", "style": "solid" }], - "pro": [ - { "family": "classic", "style": "solid" }, - { "family": "classic", "style": "regular" }, - { "family": "classic", "style": "light" }, - { "family": "classic", "style": "thin" }, - { "family": "duotone", "style": "solid" }, - { "family": "sharp", "style": "solid" }, - { "family": "sharp", "style": "regular" }, - { "family": "sharp", "style": "light" } - ] + "pro": "ALL" } }, { @@ -93,16 +57,7 @@ "label": "User Secret", "familyStylesByLicense": { "free": [{ "family": "classic", "style": "solid" }], - "pro": [ - { "family": "classic", "style": "solid" }, - { "family": "classic", "style": "regular" }, - { "family": "classic", "style": "light" }, - { "family": "classic", "style": "thin" }, - { "family": "duotone", "style": "solid" }, - { "family": "sharp", "style": "solid" }, - { "family": "sharp", "style": "regular" }, - { "family": "sharp", "style": "light" } - ] + "pro": "ALL" } }, { @@ -110,16 +65,7 @@ "label": "Info", "familyStylesByLicense": { "free": [{ "family": "classic", "style": "solid" }], - "pro": [ - { "family": "classic", "style": "solid" }, - { "family": "classic", "style": "regular" }, - { "family": "classic", "style": "light" }, - { "family": "classic", "style": "thin" }, - { "family": "duotone", "style": "solid" }, - { "family": "sharp", "style": "solid" }, - { "family": "sharp", "style": "regular" }, - { "family": "sharp", "style": "light" } - ] + "pro": "ALL" } }, { @@ -127,16 +73,7 @@ "label": "List", "familyStylesByLicense": { "free": [{ "family": "classic", "style": "solid" }], - "pro": [ - { "family": "classic", "style": "solid" }, - { "family": "classic", "style": "regular" }, - { "family": "classic", "style": "light" }, - { "family": "classic", "style": "thin" }, - { "family": "duotone", "style": "solid" }, - { "family": "sharp", "style": "solid" }, - { "family": "sharp", "style": "regular" }, - { "family": "sharp", "style": "light" } - ] + "pro": "ALL" } }, { @@ -147,16 +84,7 @@ { "family": "classic", "style": "solid" }, { "family": "classic", "style": "regular" } ], - "pro": [ - { "family": "classic", "style": "solid" }, - { "family": "classic", "style": "regular" }, - { "family": "classic", "style": "light" }, - { "family": "classic", "style": "thin" }, - { "family": "duotone", "style": "solid" }, - { "family": "sharp", "style": "solid" }, - { "family": "sharp", "style": "regular" }, - { "family": "sharp", "style": "light" } - ] + "pro": "ALL" } }, { @@ -164,16 +92,7 @@ "label": "Add to Shopping Cart", "familyStylesByLicense": { "free": [{ "family": "classic", "style": "solid" }], - "pro": [ - { "family": "classic", "style": "solid" }, - { "family": "classic", "style": "regular" }, - { "family": "classic", "style": "light" }, - { "family": "classic", "style": "thin" }, - { "family": "duotone", "style": "solid" }, - { "family": "sharp", "style": "solid" }, - { "family": "sharp", "style": "regular" }, - { "family": "sharp", "style": "light" } - ] + "pro": "ALL" } }, { @@ -181,16 +100,7 @@ "label": "book", "familyStylesByLicense": { "free": [{ "family": "classic", "style": "solid" }], - "pro": [ - { "family": "classic", "style": "solid" }, - { "family": "classic", "style": "regular" }, - { "family": "classic", "style": "light" }, - { "family": "classic", "style": "thin" }, - { "family": "duotone", "style": "solid" }, - { "family": "sharp", "style": "solid" }, - { "family": "sharp", "style": "regular" }, - { "family": "sharp", "style": "light" } - ] + "pro": "ALL" } }, { @@ -201,16 +111,7 @@ { "family": "classic", "style": "solid" }, { "family": "classic", "style": "regular" } ], - "pro": [ - { "family": "classic", "style": "solid" }, - { "family": "classic", "style": "regular" }, - { "family": "classic", "style": "light" }, - { "family": "classic", "style": "thin" }, - { "family": "duotone", "style": "solid" }, - { "family": "sharp", "style": "solid" }, - { "family": "sharp", "style": "regular" }, - { "family": "sharp", "style": "light" } - ] + "pro": "ALL" } }, { @@ -221,16 +122,7 @@ { "family": "classic", "style": "solid" }, { "family": "classic", "style": "regular" } ], - "pro": [ - { "family": "classic", "style": "solid" }, - { "family": "classic", "style": "regular" }, - { "family": "classic", "style": "light" }, - { "family": "classic", "style": "thin" }, - { "family": "duotone", "style": "solid" }, - { "family": "sharp", "style": "solid" }, - { "family": "sharp", "style": "regular" }, - { "family": "sharp", "style": "light" } - ] + "pro": "ALL" } }, { @@ -238,16 +130,7 @@ "label": "Trash", "familyStylesByLicense": { "free": [{ "family": "classic", "style": "solid" }], - "pro": [ - { "family": "classic", "style": "solid" }, - { "family": "classic", "style": "regular" }, - { "family": "classic", "style": "light" }, - { "family": "classic", "style": "thin" }, - { "family": "duotone", "style": "solid" }, - { "family": "sharp", "style": "solid" }, - { "family": "sharp", "style": "regular" }, - { "family": "sharp", "style": "light" } - ] + "pro": "ALL" } }, { @@ -255,16 +138,7 @@ "label": "Download", "familyStylesByLicense": { "free": [{ "family": "classic", "style": "solid" }], - "pro": [ - { "family": "classic", "style": "solid" }, - { "family": "classic", "style": "regular" }, - { "family": "classic", "style": "light" }, - { "family": "classic", "style": "thin" }, - { "family": "duotone", "style": "solid" }, - { "family": "sharp", "style": "solid" }, - { "family": "sharp", "style": "regular" }, - { "family": "sharp", "style": "light" } - ] + "pro": "ALL" } }, { @@ -280,16 +154,7 @@ "label": "Cross", "familyStylesByLicense": { "free": [{ "family": "classic", "style": "solid" }], - "pro": [ - { "family": "classic", "style": "solid" }, - { "family": "classic", "style": "regular" }, - { "family": "classic", "style": "light" }, - { "family": "classic", "style": "thin" }, - { "family": "duotone", "style": "solid" }, - { "family": "sharp", "style": "solid" }, - { "family": "sharp", "style": "regular" }, - { "family": "sharp", "style": "light" } - ] + "pro": "ALL" } }, { @@ -297,16 +162,7 @@ "label": "Meteor", "familyStylesByLicense": { "free": [{ "family": "classic", "style": "solid" }], - "pro": [ - { "family": "classic", "style": "solid" }, - { "family": "classic", "style": "regular" }, - { "family": "classic", "style": "light" }, - { "family": "classic", "style": "thin" }, - { "family": "duotone", "style": "solid" }, - { "family": "sharp", "style": "solid" }, - { "family": "sharp", "style": "regular" }, - { "family": "sharp", "style": "light" } - ] + "pro": "ALL" } }, { @@ -322,16 +178,7 @@ "label": "play", "familyStylesByLicense": { "free": [{ "family": "classic", "style": "solid" }], - "pro": [ - { "family": "classic", "style": "solid" }, - { "family": "classic", "style": "regular" }, - { "family": "classic", "style": "light" }, - { "family": "classic", "style": "thin" }, - { "family": "duotone", "style": "solid" }, - { "family": "sharp", "style": "solid" }, - { "family": "sharp", "style": "regular" }, - { "family": "sharp", "style": "light" } - ] + "pro": "ALL" } }, { @@ -339,16 +186,7 @@ "label": "lock", "familyStylesByLicense": { "free": [{ "family": "classic", "style": "solid" }], - "pro": [ - { "family": "classic", "style": "solid" }, - { "family": "classic", "style": "regular" }, - { "family": "classic", "style": "light" }, - { "family": "classic", "style": "thin" }, - { "family": "duotone", "style": "solid" }, - { "family": "sharp", "style": "solid" }, - { "family": "sharp", "style": "regular" }, - { "family": "sharp", "style": "light" } - ] + "pro": "ALL" } }, { @@ -356,16 +194,7 @@ "label": "Car and Building", "familyStylesByLicense": { "free": [], - "pro": [ - { "family": "classic", "style": "solid" }, - { "family": "classic", "style": "regular" }, - { "family": "classic", "style": "light" }, - { "family": "classic", "style": "thin" }, - { "family": "duotone", "style": "solid" }, - { "family": "sharp", "style": "solid" }, - { "family": "sharp", "style": "regular" }, - { "family": "sharp", "style": "light" } - ] + "pro": "ALL" } }, { @@ -373,16 +202,7 @@ "label": "Question", "familyStylesByLicense": { "free": [{ "family": "classic", "style": "solid" }], - "pro": [ - { "family": "classic", "style": "solid" }, - { "family": "classic", "style": "regular" }, - { "family": "classic", "style": "light" }, - { "family": "classic", "style": "thin" }, - { "family": "duotone", "style": "solid" }, - { "family": "sharp", "style": "solid" }, - { "family": "sharp", "style": "regular" }, - { "family": "sharp", "style": "light" } - ] + "pro": "ALL" } }, { @@ -398,16 +218,7 @@ "label": "Video", "familyStylesByLicense": { "free": [{ "family": "classic", "style": "solid" }], - "pro": [ - { "family": "classic", "style": "solid" }, - { "family": "classic", "style": "regular" }, - { "family": "classic", "style": "light" }, - { "family": "classic", "style": "thin" }, - { "family": "duotone", "style": "solid" }, - { "family": "sharp", "style": "solid" }, - { "family": "sharp", "style": "regular" }, - { "family": "sharp", "style": "light" } - ] + "pro": "ALL" } }, { @@ -418,16 +229,7 @@ { "family": "classic", "style": "solid" }, { "family": "classic", "style": "regular" } ], - "pro": [ - { "family": "classic", "style": "solid" }, - { "family": "classic", "style": "regular" }, - { "family": "classic", "style": "light" }, - { "family": "classic", "style": "thin" }, - { "family": "duotone", "style": "solid" }, - { "family": "sharp", "style": "solid" }, - { "family": "sharp", "style": "regular" }, - { "family": "sharp", "style": "light" } - ] + "pro": "ALL" } }, { @@ -435,16 +237,7 @@ "label": "Street View", "familyStylesByLicense": { "free": [{ "family": "classic", "style": "solid" }], - "pro": [ - { "family": "classic", "style": "solid" }, - { "family": "classic", "style": "regular" }, - { "family": "classic", "style": "light" }, - { "family": "classic", "style": "thin" }, - { "family": "duotone", "style": "solid" }, - { "family": "sharp", "style": "solid" }, - { "family": "sharp", "style": "regular" }, - { "family": "sharp", "style": "light" } - ] + "pro": "ALL" } }, { @@ -452,16 +245,7 @@ "label": "Share", "familyStylesByLicense": { "free": [{ "family": "classic", "style": "solid" }], - "pro": [ - { "family": "classic", "style": "solid" }, - { "family": "classic", "style": "regular" }, - { "family": "classic", "style": "light" }, - { "family": "classic", "style": "thin" }, - { "family": "duotone", "style": "solid" }, - { "family": "sharp", "style": "solid" }, - { "family": "sharp", "style": "regular" }, - { "family": "sharp", "style": "light" } - ] + "pro": "ALL" } }, { @@ -469,16 +253,7 @@ "label": "Code", "familyStylesByLicense": { "free": [{ "family": "classic", "style": "solid" }], - "pro": [ - { "family": "classic", "style": "solid" }, - { "family": "classic", "style": "regular" }, - { "family": "classic", "style": "light" }, - { "family": "classic", "style": "thin" }, - { "family": "duotone", "style": "solid" }, - { "family": "sharp", "style": "solid" }, - { "family": "sharp", "style": "regular" }, - { "family": "sharp", "style": "light" } - ] + "pro": "ALL" } }, { @@ -486,16 +261,7 @@ "label": "House", "familyStylesByLicense": { "free": [], - "pro": [ - { "family": "classic", "style": "solid" }, - { "family": "classic", "style": "regular" }, - { "family": "classic", "style": "light" }, - { "family": "classic", "style": "thin" }, - { "family": "duotone", "style": "solid" }, - { "family": "sharp", "style": "solid" }, - { "family": "sharp", "style": "regular" }, - { "family": "sharp", "style": "light" } - ] + "pro": "ALL" } }, { @@ -506,16 +272,7 @@ { "family": "classic", "style": "solid" }, { "family": "classic", "style": "regular" } ], - "pro": [ - { "family": "classic", "style": "solid" }, - { "family": "classic", "style": "regular" }, - { "family": "classic", "style": "light" }, - { "family": "classic", "style": "thin" }, - { "family": "duotone", "style": "solid" }, - { "family": "sharp", "style": "solid" }, - { "family": "sharp", "style": "regular" }, - { "family": "sharp", "style": "light" } - ] + "pro": "ALL" } }, { @@ -523,16 +280,7 @@ "label": "Coffee", "familyStylesByLicense": { "free": [{ "family": "classic", "style": "solid" }], - "pro": [ - { "family": "classic", "style": "solid" }, - { "family": "classic", "style": "regular" }, - { "family": "classic", "style": "light" }, - { "family": "classic", "style": "thin" }, - { "family": "duotone", "style": "solid" }, - { "family": "sharp", "style": "solid" }, - { "family": "sharp", "style": "regular" }, - { "family": "sharp", "style": "light" } - ] + "pro": "ALL" } }, { @@ -540,16 +288,7 @@ "label": "Computer Speaker", "familyStylesByLicense": { "free": [], - "pro": [ - { "family": "classic", "style": "solid" }, - { "family": "classic", "style": "regular" }, - { "family": "classic", "style": "light" }, - { "family": "classic", "style": "thin" }, - { "family": "duotone", "style": "solid" }, - { "family": "sharp", "style": "solid" }, - { "family": "sharp", "style": "regular" }, - { "family": "sharp", "style": "light" } - ] + "pro": "ALL" } }, { @@ -557,16 +296,7 @@ "label": "Bars", "familyStylesByLicense": { "free": [{ "family": "classic", "style": "solid" }], - "pro": [ - { "family": "classic", "style": "solid" }, - { "family": "classic", "style": "regular" }, - { "family": "classic", "style": "light" }, - { "family": "classic", "style": "thin" }, - { "family": "duotone", "style": "solid" }, - { "family": "sharp", "style": "solid" }, - { "family": "sharp", "style": "regular" }, - { "family": "sharp", "style": "light" } - ] + "pro": "ALL" } }, { @@ -585,16 +315,7 @@ { "family": "classic", "style": "solid" }, { "family": "classic", "style": "regular" } ], - "pro": [ - { "family": "classic", "style": "solid" }, - { "family": "classic", "style": "regular" }, - { "family": "classic", "style": "light" }, - { "family": "classic", "style": "thin" }, - { "family": "duotone", "style": "solid" }, - { "family": "sharp", "style": "solid" }, - { "family": "sharp", "style": "regular" }, - { "family": "sharp", "style": "light" } - ] + "pro": "ALL" } }, { @@ -605,16 +326,7 @@ { "family": "classic", "style": "solid" }, { "family": "classic", "style": "regular" } ], - "pro": [ - { "family": "classic", "style": "solid" }, - { "family": "classic", "style": "regular" }, - { "family": "classic", "style": "light" }, - { "family": "classic", "style": "thin" }, - { "family": "duotone", "style": "solid" }, - { "family": "sharp", "style": "solid" }, - { "family": "sharp", "style": "regular" }, - { "family": "sharp", "style": "light" } - ] + "pro": "ALL" } }, { @@ -625,16 +337,7 @@ { "family": "classic", "style": "solid" }, { "family": "classic", "style": "regular" } ], - "pro": [ - { "family": "classic", "style": "solid" }, - { "family": "classic", "style": "regular" }, - { "family": "classic", "style": "light" }, - { "family": "classic", "style": "thin" }, - { "family": "duotone", "style": "solid" }, - { "family": "sharp", "style": "solid" }, - { "family": "sharp", "style": "regular" }, - { "family": "sharp", "style": "light" } - ] + "pro": "ALL" } }, { @@ -642,16 +345,7 @@ "label": "key", "familyStylesByLicense": { "free": [{ "family": "classic", "style": "solid" }], - "pro": [ - { "family": "classic", "style": "solid" }, - { "family": "classic", "style": "regular" }, - { "family": "classic", "style": "light" }, - { "family": "classic", "style": "thin" }, - { "family": "duotone", "style": "solid" }, - { "family": "sharp", "style": "solid" }, - { "family": "sharp", "style": "regular" }, - { "family": "sharp", "style": "light" } - ] + "pro": "ALL" } }, { @@ -659,16 +353,7 @@ "label": "Bomb", "familyStylesByLicense": { "free": [{ "family": "classic", "style": "solid" }], - "pro": [ - { "family": "classic", "style": "solid" }, - { "family": "classic", "style": "regular" }, - { "family": "classic", "style": "light" }, - { "family": "classic", "style": "thin" }, - { "family": "duotone", "style": "solid" }, - { "family": "sharp", "style": "solid" }, - { "family": "sharp", "style": "regular" }, - { "family": "sharp", "style": "light" } - ] + "pro": "ALL" } }, { @@ -676,14 +361,7 @@ "label": "camera", "familyStylesByLicense": { "free": [{ "family": "classic", "style": "solid" }], - "pro": [ - { "family": "classic", "style": "solid" }, - { "family": "classic", "style": "regular" }, - { "family": "classic", "style": "light" }, - { "family": "classic", "style": "thin" }, - { "family": "duotone", "style": "solid" }, - { "family": "sharp", "style": "solid" } - ] + "pro": "ALL" } }, { @@ -691,16 +369,7 @@ "label": "Text", "familyStylesByLicense": { "free": [], - "pro": [ - { "family": "classic", "style": "solid" }, - { "family": "classic", "style": "regular" }, - { "family": "classic", "style": "light" }, - { "family": "classic", "style": "thin" }, - { "family": "duotone", "style": "solid" }, - { "family": "sharp", "style": "solid" }, - { "family": "sharp", "style": "regular" }, - { "family": "sharp", "style": "light" } - ] + "pro": "ALL" } }, { @@ -708,16 +377,7 @@ "label": "Umbrella Beach", "familyStylesByLicense": { "free": [{ "family": "classic", "style": "solid" }], - "pro": [ - { "family": "classic", "style": "solid" }, - { "family": "classic", "style": "regular" }, - { "family": "classic", "style": "light" }, - { "family": "classic", "style": "thin" }, - { "family": "duotone", "style": "solid" }, - { "family": "sharp", "style": "solid" }, - { "family": "sharp", "style": "regular" }, - { "family": "sharp", "style": "light" } - ] + "pro": "ALL" } }, { @@ -728,16 +388,7 @@ { "family": "classic", "style": "solid" }, { "family": "classic", "style": "regular" } ], - "pro": [ - { "family": "classic", "style": "solid" }, - { "family": "classic", "style": "regular" }, - { "family": "classic", "style": "light" }, - { "family": "classic", "style": "thin" }, - { "family": "duotone", "style": "solid" }, - { "family": "sharp", "style": "solid" }, - { "family": "sharp", "style": "regular" }, - { "family": "sharp", "style": "light" } - ] + "pro": "ALL" } }, { @@ -745,16 +396,7 @@ "label": "print", "familyStylesByLicense": { "free": [{ "family": "classic", "style": "solid" }], - "pro": [ - { "family": "classic", "style": "solid" }, - { "family": "classic", "style": "regular" }, - { "family": "classic", "style": "light" }, - { "family": "classic", "style": "thin" }, - { "family": "duotone", "style": "solid" }, - { "family": "sharp", "style": "solid" }, - { "family": "sharp", "style": "regular" }, - { "family": "sharp", "style": "light" } - ] + "pro": "ALL" } }, { @@ -762,16 +404,7 @@ "label": "Sort", "familyStylesByLicense": { "free": [{ "family": "classic", "style": "solid" }], - "pro": [ - { "family": "classic", "style": "solid" }, - { "family": "classic", "style": "regular" }, - { "family": "classic", "style": "light" }, - { "family": "classic", "style": "thin" }, - { "family": "duotone", "style": "solid" }, - { "family": "sharp", "style": "solid" }, - { "family": "sharp", "style": "regular" }, - { "family": "sharp", "style": "light" } - ] + "pro": "ALL" } }, { @@ -782,16 +415,7 @@ { "family": "classic", "style": "solid" }, { "family": "classic", "style": "regular" } ], - "pro": [ - { "family": "classic", "style": "solid" }, - { "family": "classic", "style": "regular" }, - { "family": "classic", "style": "light" }, - { "family": "classic", "style": "thin" }, - { "family": "duotone", "style": "solid" }, - { "family": "sharp", "style": "solid" }, - { "family": "sharp", "style": "regular" }, - { "family": "sharp", "style": "light" } - ] + "pro": "ALL" } }, { @@ -799,16 +423,7 @@ "label": "Wired Network", "familyStylesByLicense": { "free": [{ "family": "classic", "style": "solid" }], - "pro": [ - { "family": "classic", "style": "solid" }, - { "family": "classic", "style": "regular" }, - { "family": "classic", "style": "light" }, - { "family": "classic", "style": "thin" }, - { "family": "duotone", "style": "solid" }, - { "family": "sharp", "style": "solid" }, - { "family": "sharp", "style": "regular" }, - { "family": "sharp", "style": "light" } - ] + "pro": "ALL" } }, { @@ -816,16 +431,7 @@ "label": "pencil", "familyStylesByLicense": { "free": [], - "pro": [ - { "family": "classic", "style": "solid" }, - { "family": "classic", "style": "regular" }, - { "family": "classic", "style": "light" }, - { "family": "classic", "style": "thin" }, - { "family": "duotone", "style": "solid" }, - { "family": "sharp", "style": "solid" }, - { "family": "sharp", "style": "regular" }, - { "family": "sharp", "style": "light" } - ] + "pro": "ALL" } }, { @@ -833,16 +439,7 @@ "label": "quote-right", "familyStylesByLicense": { "free": [{ "family": "classic", "style": "solid" }], - "pro": [ - { "family": "classic", "style": "solid" }, - { "family": "classic", "style": "regular" }, - { "family": "classic", "style": "light" }, - { "family": "classic", "style": "thin" }, - { "family": "duotone", "style": "solid" }, - { "family": "sharp", "style": "solid" }, - { "family": "sharp", "style": "regular" }, - { "family": "sharp", "style": "light" } - ] + "pro": "ALL" } }, { @@ -850,16 +447,7 @@ "label": "truck", "familyStylesByLicense": { "free": [{ "family": "classic", "style": "solid" }], - "pro": [ - { "family": "classic", "style": "solid" }, - { "family": "classic", "style": "regular" }, - { "family": "classic", "style": "light" }, - { "family": "classic", "style": "thin" }, - { "family": "duotone", "style": "solid" }, - { "family": "sharp", "style": "solid" }, - { "family": "sharp", "style": "regular" }, - { "family": "sharp", "style": "light" } - ] + "pro": "ALL" } }, { @@ -867,16 +455,7 @@ "label": "Pepper Hot", "familyStylesByLicense": { "free": [{ "family": "classic", "style": "solid" }], - "pro": [ - { "family": "classic", "style": "solid" }, - { "family": "classic", "style": "regular" }, - { "family": "classic", "style": "light" }, - { "family": "classic", "style": "thin" }, - { "family": "duotone", "style": "solid" }, - { "family": "sharp", "style": "solid" }, - { "family": "sharp", "style": "regular" }, - { "family": "sharp", "style": "light" } - ] + "pro": "ALL" } }, { @@ -884,16 +463,7 @@ "label": "File Export", "familyStylesByLicense": { "free": [{ "family": "classic", "style": "solid" }], - "pro": [ - { "family": "classic", "style": "solid" }, - { "family": "classic", "style": "regular" }, - { "family": "classic", "style": "light" }, - { "family": "classic", "style": "thin" }, - { "family": "duotone", "style": "solid" }, - { "family": "sharp", "style": "solid" }, - { "family": "sharp", "style": "regular" }, - { "family": "sharp", "style": "light" } - ] + "pro": "ALL" } }, { @@ -901,16 +471,7 @@ "label": "table", "familyStylesByLicense": { "free": [{ "family": "classic", "style": "solid" }], - "pro": [ - { "family": "classic", "style": "solid" }, - { "family": "classic", "style": "regular" }, - { "family": "classic", "style": "light" }, - { "family": "classic", "style": "thin" }, - { "family": "duotone", "style": "solid" }, - { "family": "sharp", "style": "solid" }, - { "family": "sharp", "style": "regular" }, - { "family": "sharp", "style": "light" } - ] + "pro": "ALL" } }, { @@ -918,16 +479,7 @@ "label": "File Invoice", "familyStylesByLicense": { "free": [{ "family": "classic", "style": "solid" }], - "pro": [ - { "family": "classic", "style": "solid" }, - { "family": "classic", "style": "regular" }, - { "family": "classic", "style": "light" }, - { "family": "classic", "style": "thin" }, - { "family": "duotone", "style": "solid" }, - { "family": "sharp", "style": "solid" }, - { "family": "sharp", "style": "regular" }, - { "family": "sharp", "style": "light" } - ] + "pro": "ALL" } }, { @@ -935,16 +487,7 @@ "label": "Moon with Stars", "familyStylesByLicense": { "free": [], - "pro": [ - { "family": "classic", "style": "solid" }, - { "family": "classic", "style": "regular" }, - { "family": "classic", "style": "light" }, - { "family": "classic", "style": "thin" }, - { "family": "duotone", "style": "solid" }, - { "family": "sharp", "style": "solid" }, - { "family": "sharp", "style": "regular" }, - { "family": "sharp", "style": "light" } - ] + "pro": "ALL" } }, { @@ -952,16 +495,7 @@ "label": "TTY", "familyStylesByLicense": { "free": [{ "family": "classic", "style": "solid" }], - "pro": [ - { "family": "classic", "style": "solid" }, - { "family": "classic", "style": "regular" }, - { "family": "classic", "style": "light" }, - { "family": "classic", "style": "thin" }, - { "family": "duotone", "style": "solid" }, - { "family": "sharp", "style": "solid" }, - { "family": "sharp", "style": "regular" }, - { "family": "sharp", "style": "light" } - ] + "pro": "ALL" } }, { @@ -972,16 +506,7 @@ { "family": "classic", "style": "solid" }, { "family": "classic", "style": "regular" } ], - "pro": [ - { "family": "classic", "style": "solid" }, - { "family": "classic", "style": "regular" }, - { "family": "classic", "style": "light" }, - { "family": "classic", "style": "thin" }, - { "family": "duotone", "style": "solid" }, - { "family": "sharp", "style": "solid" }, - { "family": "sharp", "style": "regular" }, - { "family": "sharp", "style": "light" } - ] + "pro": "ALL" } }, { @@ -989,16 +514,7 @@ "label": "Office Phone", "familyStylesByLicense": { "free": [], - "pro": [ - { "family": "classic", "style": "solid" }, - { "family": "classic", "style": "regular" }, - { "family": "classic", "style": "light" }, - { "family": "classic", "style": "thin" }, - { "family": "duotone", "style": "solid" }, - { "family": "sharp", "style": "solid" }, - { "family": "sharp", "style": "regular" }, - { "family": "sharp", "style": "light" } - ] + "pro": "ALL" } }, { @@ -1006,16 +522,7 @@ "label": "Video Slash", "familyStylesByLicense": { "free": [{ "family": "classic", "style": "solid" }], - "pro": [ - { "family": "classic", "style": "solid" }, - { "family": "classic", "style": "regular" }, - { "family": "classic", "style": "light" }, - { "family": "classic", "style": "thin" }, - { "family": "duotone", "style": "solid" }, - { "family": "sharp", "style": "solid" }, - { "family": "sharp", "style": "regular" }, - { "family": "sharp", "style": "light" } - ] + "pro": "ALL" } }, { @@ -1023,16 +530,7 @@ "label": "Person and Empty Dolly", "familyStylesByLicense": { "free": [], - "pro": [ - { "family": "classic", "style": "solid" }, - { "family": "classic", "style": "regular" }, - { "family": "classic", "style": "light" }, - { "family": "classic", "style": "thin" }, - { "family": "duotone", "style": "solid" }, - { "family": "sharp", "style": "solid" }, - { "family": "sharp", "style": "regular" }, - { "family": "sharp", "style": "light" } - ] + "pro": "ALL" } }, { @@ -1040,16 +538,7 @@ "label": "Signature", "familyStylesByLicense": { "free": [{ "family": "classic", "style": "solid" }], - "pro": [ - { "family": "classic", "style": "solid" }, - { "family": "classic", "style": "regular" }, - { "family": "classic", "style": "light" }, - { "family": "classic", "style": "thin" }, - { "family": "duotone", "style": "solid" }, - { "family": "sharp", "style": "solid" }, - { "family": "sharp", "style": "regular" }, - { "family": "sharp", "style": "light" } - ] + "pro": "ALL" } }, { @@ -1057,16 +546,7 @@ "label": "Store", "familyStylesByLicense": { "free": [{ "family": "classic", "style": "solid" }], - "pro": [ - { "family": "classic", "style": "solid" }, - { "family": "classic", "style": "regular" }, - { "family": "classic", "style": "light" }, - { "family": "classic", "style": "thin" }, - { "family": "duotone", "style": "solid" }, - { "family": "sharp", "style": "solid" }, - { "family": "sharp", "style": "regular" }, - { "family": "sharp", "style": "light" } - ] + "pro": "ALL" } }, { @@ -1074,16 +554,7 @@ "label": "Projector", "familyStylesByLicense": { "free": [], - "pro": [ - { "family": "classic", "style": "solid" }, - { "family": "classic", "style": "regular" }, - { "family": "classic", "style": "light" }, - { "family": "classic", "style": "thin" }, - { "family": "duotone", "style": "solid" }, - { "family": "sharp", "style": "solid" }, - { "family": "sharp", "style": "regular" }, - { "family": "sharp", "style": "light" } - ] + "pro": "ALL" } }, { @@ -1091,16 +562,7 @@ "label": "Loveseat", "familyStylesByLicense": { "free": [], - "pro": [ - { "family": "classic", "style": "solid" }, - { "family": "classic", "style": "regular" }, - { "family": "classic", "style": "light" }, - { "family": "classic", "style": "thin" }, - { "family": "duotone", "style": "solid" }, - { "family": "sharp", "style": "solid" }, - { "family": "sharp", "style": "regular" }, - { "family": "sharp", "style": "light" } - ] + "pro": "ALL" } }, { @@ -1108,16 +570,7 @@ "label": "arrow-right", "familyStylesByLicense": { "free": [{ "family": "classic", "style": "solid" }], - "pro": [ - { "family": "classic", "style": "solid" }, - { "family": "classic", "style": "regular" }, - { "family": "classic", "style": "light" }, - { "family": "classic", "style": "thin" }, - { "family": "duotone", "style": "solid" }, - { "family": "sharp", "style": "solid" }, - { "family": "sharp", "style": "regular" }, - { "family": "sharp", "style": "light" } - ] + "pro": "ALL" } }, { @@ -1125,16 +578,7 @@ "label": "Music", "familyStylesByLicense": { "free": [{ "family": "classic", "style": "solid" }], - "pro": [ - { "family": "classic", "style": "solid" }, - { "family": "classic", "style": "regular" }, - { "family": "classic", "style": "light" }, - { "family": "classic", "style": "thin" }, - { "family": "duotone", "style": "solid" }, - { "family": "sharp", "style": "solid" }, - { "family": "sharp", "style": "regular" }, - { "family": "sharp", "style": "light" } - ] + "pro": "ALL" } }, { @@ -1142,16 +586,7 @@ "label": "Icons", "familyStylesByLicense": { "free": [{ "family": "classic", "style": "solid" }], - "pro": [ - { "family": "classic", "style": "solid" }, - { "family": "classic", "style": "regular" }, - { "family": "classic", "style": "light" }, - { "family": "classic", "style": "thin" }, - { "family": "duotone", "style": "solid" }, - { "family": "sharp", "style": "solid" }, - { "family": "sharp", "style": "regular" }, - { "family": "sharp", "style": "light" } - ] + "pro": "ALL" } }, { @@ -1159,16 +594,7 @@ "label": "Power Off", "familyStylesByLicense": { "free": [{ "family": "classic", "style": "solid" }], - "pro": [ - { "family": "classic", "style": "solid" }, - { "family": "classic", "style": "regular" }, - { "family": "classic", "style": "light" }, - { "family": "classic", "style": "thin" }, - { "family": "duotone", "style": "solid" }, - { "family": "sharp", "style": "solid" }, - { "family": "sharp", "style": "regular" }, - { "family": "sharp", "style": "light" } - ] + "pro": "ALL" } }, { @@ -1176,16 +602,7 @@ "label": "WiFi", "familyStylesByLicense": { "free": [{ "family": "classic", "style": "solid" }], - "pro": [ - { "family": "classic", "style": "solid" }, - { "family": "classic", "style": "regular" }, - { "family": "classic", "style": "light" }, - { "family": "classic", "style": "thin" }, - { "family": "duotone", "style": "solid" }, - { "family": "sharp", "style": "solid" }, - { "family": "sharp", "style": "regular" }, - { "family": "sharp", "style": "light" } - ] + "pro": "ALL" } }, { @@ -1193,16 +610,7 @@ "label": "Vials", "familyStylesByLicense": { "free": [{ "family": "classic", "style": "solid" }], - "pro": [ - { "family": "classic", "style": "solid" }, - { "family": "classic", "style": "regular" }, - { "family": "classic", "style": "light" }, - { "family": "classic", "style": "thin" }, - { "family": "duotone", "style": "solid" }, - { "family": "sharp", "style": "solid" }, - { "family": "sharp", "style": "regular" }, - { "family": "sharp", "style": "light" } - ] + "pro": "ALL" } }, { @@ -1210,16 +618,7 @@ "label": "Door Open", "familyStylesByLicense": { "free": [{ "family": "classic", "style": "solid" }], - "pro": [ - { "family": "classic", "style": "solid" }, - { "family": "classic", "style": "regular" }, - { "family": "classic", "style": "light" }, - { "family": "classic", "style": "thin" }, - { "family": "duotone", "style": "solid" }, - { "family": "sharp", "style": "solid" }, - { "family": "sharp", "style": "regular" }, - { "family": "sharp", "style": "light" } - ] + "pro": "ALL" } }, { @@ -1227,16 +626,7 @@ "label": "shield", "familyStylesByLicense": { "free": [], - "pro": [ - { "family": "classic", "style": "solid" }, - { "family": "classic", "style": "regular" }, - { "family": "classic", "style": "light" }, - { "family": "classic", "style": "thin" }, - { "family": "duotone", "style": "solid" }, - { "family": "sharp", "style": "solid" }, - { "family": "sharp", "style": "regular" }, - { "family": "sharp", "style": "light" } - ] + "pro": "ALL" } }, { @@ -1244,16 +634,7 @@ "label": "Line Height", "familyStylesByLicense": { "free": [], - "pro": [ - { "family": "classic", "style": "solid" }, - { "family": "classic", "style": "regular" }, - { "family": "classic", "style": "light" }, - { "family": "classic", "style": "thin" }, - { "family": "duotone", "style": "solid" }, - { "family": "sharp", "style": "solid" }, - { "family": "sharp", "style": "regular" }, - { "family": "sharp", "style": "light" } - ] + "pro": "ALL" } }, { @@ -1261,16 +642,7 @@ "label": "Expand", "familyStylesByLicense": { "free": [{ "family": "classic", "style": "solid" }], - "pro": [ - { "family": "classic", "style": "solid" }, - { "family": "classic", "style": "regular" }, - { "family": "classic", "style": "light" }, - { "family": "classic", "style": "thin" }, - { "family": "duotone", "style": "solid" }, - { "family": "sharp", "style": "solid" }, - { "family": "sharp", "style": "regular" }, - { "family": "sharp", "style": "light" } - ] + "pro": "ALL" } }, { @@ -1278,16 +650,7 @@ "label": "Walkie Talkie", "familyStylesByLicense": { "free": [], - "pro": [ - { "family": "classic", "style": "solid" }, - { "family": "classic", "style": "regular" }, - { "family": "classic", "style": "light" }, - { "family": "classic", "style": "thin" }, - { "family": "duotone", "style": "solid" }, - { "family": "sharp", "style": "solid" }, - { "family": "sharp", "style": "regular" }, - { "family": "sharp", "style": "light" } - ] + "pro": "ALL" } }, { @@ -1295,16 +658,7 @@ "label": "Mailbox", "familyStylesByLicense": { "free": [], - "pro": [ - { "family": "classic", "style": "solid" }, - { "family": "classic", "style": "regular" }, - { "family": "classic", "style": "light" }, - { "family": "classic", "style": "thin" }, - { "family": "duotone", "style": "solid" }, - { "family": "sharp", "style": "solid" }, - { "family": "sharp", "style": "regular" }, - { "family": "sharp", "style": "light" } - ] + "pro": "ALL" } } ] diff --git a/packages/fa-icon-chooser/src/utils/slots.tsx b/packages/fa-icon-chooser/src/utils/slots.tsx index a273e2a..451c511 100644 --- a/packages/fa-icon-chooser/src/utils/slots.tsx +++ b/packages/fa-icon-chooser/src/utils/slots.tsx @@ -30,20 +30,6 @@ slotDefaults['searching-free'] = "You're searching Font Awesome Free icons in ve slotDefaults['searching-pro'] = "You're searching Font Awesome Pro icons in version"; -slotDefaults['light-requires-pro'] = 'You need to use a Pro kit to get Light icons.'; - -slotDefaults['thin-requires-pro'] = 'You need to use a Pro kit with Version 6 to get Thin icons.'; - -slotDefaults['duotone-requires-pro'] = 'You need to use a Pro kit with Version 5.10 or later to get Duotone icons.'; - -slotDefaults['sharp-solid-requires-pro'] = 'You need to use a Pro kit with Version 6.2.0 or later to get Sharp Solid icons.'; - -slotDefaults['sharp-regular-requires-pro'] = 'You need to use a Pro kit with Version 6.3.0 or later to get Sharp Regular icons.'; - -slotDefaults['sharp-light-requires-pro'] = 'You need to use a Pro kit with Version 6.4.0 or later to get Sharp Light icons.'; - -slotDefaults['uploaded-requires-pro'] = 'You need to use a Pro kit to get Uploaded icons.'; - slotDefaults['kit-has-no-uploaded-icons'] = 'This kit contains no uploaded icons.'; slotDefaults['no-search-results-heading'] = "Sorry, we couldn't find anything for that."; @@ -56,7 +42,7 @@ slotDefaults['suggest-icon-upload'] = ( upload your own icon {' '} - to a Pro kit! + to a Pro Kit! ); @@ -69,23 +55,3 @@ slotDefaults['get-fontawesome-pro'] = ( for more icons and styles! ); - -slotDefaults['sharp-solid-style-filter-sr-message'] = 'Show sharp solid style icons'; - -slotDefaults['sharp-regular-style-filter-sr-message'] = 'Show sharp regular style icons'; - -slotDefaults['sharp-light-style-filter-sr-message'] = 'Show sharp light style icons'; - -slotDefaults['solid-style-filter-sr-message'] = 'Show solid style icons'; - -slotDefaults['regular-style-filter-sr-message'] = 'Show regular style icons'; - -slotDefaults['light-style-filter-sr-message'] = 'Show light style icons'; - -slotDefaults['thin-style-filter-sr-message'] = 'Show thin style icons'; - -slotDefaults['duotone-style-filter-sr-message'] = 'Show duotone style icons'; - -slotDefaults['brands-style-filter-sr-message'] = 'Show brands style icons'; - -slotDefaults['uploaded-style-filter-sr-message'] = 'Show your uploaded icons'; diff --git a/packages/fa-icon-chooser/src/utils/utils.ts b/packages/fa-icon-chooser/src/utils/utils.ts index 97f0ecf..5fa2f7c 100644 --- a/packages/fa-icon-chooser/src/utils/utils.ts +++ b/packages/fa-icon-chooser/src/utils/utils.ts @@ -1,6 +1,6 @@ -import defaultIconsSearchResult from './defaultIconsSearchResult.json'; +import defaultIconsSearchResultTemplate from './defaultIconsSearchResult.json'; import { valid as validSemver } from 'semver'; -import { IconPrefix } from '@fortawesome/fontawesome-common-types'; +import { cloneDeep, get, set } from 'lodash'; const FREE_CDN_URL = 'https://use.fontawesome.com'; const PRO_KIT_ASSET_URL = 'https://ka-p.fontawesome.com'; @@ -8,65 +8,47 @@ const FREE_KIT_ASSET_URL = 'https://ka-f.fontawesome.com'; export type UrlTextFetcher = (url: string) => Promise; -export const defaultIcons: any = defaultIconsSearchResult; +// Given a set of familyStyles, replace the term "ALL" in the defaultIconsSearchResult +// asset. This is to allow that static query result to dynamically include +// new familyStyles, as they are released and made available via the GraphQL API. +// It rests on the assumption that each (non-brands) icon in that static default query is +// available in all Pro familyStyles. +export function buildDefaultIconsSearchResult(familyStyles: object): object { + const allNonBrandsFamilyStyles = []; + + for (const family in familyStyles) { + for (const style in familyStyles[family]) { + if ('brands' !== style && 'brands' !== family && 'custom' !== style) { + allNonBrandsFamilyStyles.push({ family, style }); + } + } + } -// This does not represent a list of proper Font Awesome style names, just an internal representation -// to faciliate lookups to/from style/prefix within this package. -export type IconStyle = 'solid' | 'duotone' | 'regular' | 'light' | 'thin' | 'kit' | 'brands' | 'sharp-solid' | 'sharp-regular' | 'sharp-light'; + const defaultIconsSearchResult = cloneDeep(defaultIconsSearchResultTemplate); -export type IconStyleToPrefix = { - [style in IconStyle]: string; -}; - -export type IconPrefixToStyle = { - [prefix in IconPrefix]: IconStyle; -}; + const icons = get(defaultIconsSearchResult, 'data.search', []); -export type IconFamily = 'classic' | 'sharp' | 'duotone'; + for (const i of icons) { + if ('ALL' === get(i, 'familyStylesByLicense.pro')) { + set(i, 'familyStylesByLicense.pro', allNonBrandsFamilyStyles); + } + } -export type FamilyStyle = { - family: IconFamily; - style: IconStyle; -}; + return defaultIconsSearchResult; +} export interface IconLookup { - prefix: IconPrefix; + prefix: string; iconName: string; } -export const STYLE_TO_PREFIX: IconStyleToPrefix = { - 'solid': 'fas', - 'duotone': 'fad', - 'regular': 'far', - 'light': 'fal', - 'thin': 'fat', - 'kit': 'fak', - 'brands': 'fab', - 'sharp-solid': 'fass', - 'sharp-regular': 'fasr', - 'sharp-light': 'fasl', -}; - -export const PREFIX_TO_STYLE: IconPrefixToStyle = { - fas: 'solid', - fad: 'duotone', - far: 'regular', - fal: 'light', - fat: 'thin', - fak: 'kit', - fab: 'brands', - fass: 'sharp-solid', - fasr: 'sharp-regular', - fasl: 'sharp-light', -}; - export type IconUpload = { name: string; unicode: number; version: number; width: string; height: string; - path: string; + pathData: string; }; export interface IconUploadLookup extends IconLookup { @@ -132,7 +114,9 @@ export async function createFontAwesomeScriptElement( const assetUrl = kitToken ? `${baseUrl}/releases/v${version}/js/${license}.min.js?token=${kitToken}` : `${baseUrl}/releases/v${version}/js/all.js`; try { - if ('function' !== typeof getUrlText) throw new Error("Font Awesome Icon Chooser: expected getUrlText to be a function but it wasn't"); + if ('function' !== typeof getUrlText) { + throw new Error("Font Awesome Icon Chooser: expected getUrlText to be a function but it wasn't"); + } const scriptContent = await getUrlText(assetUrl); const script = document.createElement('SCRIPT'); @@ -159,20 +143,4 @@ export function isValidSemver(val: string): boolean { return !!validSemver(val); } -export function familyStyleToPrefix(familyStyle: FamilyStyle): IconPrefix | null { - if ('classic' === familyStyle.family) { - return STYLE_TO_PREFIX[familyStyle.style as string]; - } else if ('sharp' === familyStyle.family && 'solid' === familyStyle.style) { - return 'fass'; - } else if ('sharp' === familyStyle.family && 'regular' === familyStyle.style) { - return 'fasr'; - } else if ('sharp' === familyStyle.family && 'light' === familyStyle.style) { - return 'fasl'; - } else if ('duotone' === familyStyle.family && 'solid' === familyStyle.style) { - return 'fad'; - } else { - return null; - } -} - export const Fragment = (_props, children) => [...children];