diff --git a/com.woltlab.wcf/templates/shared_fontAwesomeJavaScript.tpl b/com.woltlab.wcf/templates/shared_fontAwesomeJavaScript.tpl index d26e8e65231..64e34f5e764 100644 --- a/com.woltlab.wcf/templates/shared_fontAwesomeJavaScript.tpl +++ b/com.woltlab.wcf/templates/shared_fontAwesomeJavaScript.tpl @@ -1,12 +1,6 @@ diff --git a/ts/WoltLabSuite/Core/Ui/Style/FontAwesome.ts b/ts/WoltLabSuite/Core/Ui/Style/FontAwesome.ts index 5aa87487d65..d87d35d4626 100644 --- a/ts/WoltLabSuite/Core/Ui/Style/FontAwesome.ts +++ b/ts/WoltLabSuite/Core/Ui/Style/FontAwesome.ts @@ -7,94 +7,86 @@ * @woltlabExcludeBundle tiny */ -import { DialogCallbackObject, DialogCallbackSetup } from "../Dialog/Data"; -import * as Language from "../../Language"; -import UiDialog from "../Dialog"; +import { getPhrase } from "WoltLabSuite/Core/Language"; import UiItemListFilter from "../ItemList/Filter"; +import { dialogFactory } from "WoltLabSuite/Core/Component/Dialog"; type CallbackSelect = (icon: string, forceSolid: boolean) => void; +type IconData = { icon: string; forceSolid: boolean }; + +function createIconList(): HTMLElement { + const ul = document.createElement("ul"); + ul.classList.add("fontAwesome__icons"); + ul.id = "fontAwesomeIcons"; + + const icons: string[] = []; + window.getFontAwesome7Metadata().forEach(([, hasRegular], name) => { + if (hasRegular) { + icons.push( + `
  • `, + ); + } + + icons.push( + `
  • `, + ); + }); -class UiStyleFontAwesome implements DialogCallbackObject { - private callback?: CallbackSelect = undefined; - private iconList?: HTMLElement = undefined; - private itemListFilter?: UiItemListFilter = undefined; - - open(callback: CallbackSelect): void { - this.callback = callback; - - UiDialog.open(this); - } - - /** - * Selects an icon, notifies the callback and closes the dialog. - */ - protected click(event: MouseEvent): void { - event.preventDefault(); + ul.innerHTML = icons.join(""); - const target = event.target as HTMLElement; - const item = target.closest("li") as HTMLLIElement; - const icon = item.querySelector("fa-icon")!; + return ul; +} - UiDialog.close(this); +let content: HTMLElement | undefined = undefined; +function getContent(): HTMLElement { + if (content === undefined) { + const iconList = createIconList(); + iconList.addEventListener("click", (event) => { + event.preventDefault(); + + const { target } = event; + if (!(target instanceof HTMLButtonElement)) { + return; + } + + const icon = target.querySelector("fa-icon")!; + const selectedEvent = new CustomEvent("font-awesome:selected", { + bubbles: true, + detail: { + icon: icon.name, + forceSolid: icon.solid, + }, + }); + iconList.dispatchEvent(selectedEvent); + }); - this.callback!(icon.name, icon.solid); + content = document.createElement("div"); + content.id = "fontAwesomeSelection"; + content.append(iconList); } - _dialogSetup(): ReturnType { - return { - id: "fontAwesomeSelection", - options: { - onSetup: () => { - this.iconList = document.getElementById("fontAwesomeIcons") as HTMLElement; - - const icons: string[] = []; - window.getFontAwesome7Metadata().forEach(([, hasRegular], name) => { - if (hasRegular) { - icons.push(`
  • ${name}
  • `); - } - - icons.push(`
  • ${name}
  • `); - }); - - // build icons - this.iconList.innerHTML = icons.join(""); - - this.iconList.addEventListener("click", (ev) => this.click(ev)); - - this.itemListFilter = new UiItemListFilter("fontAwesomeIcons", { - callbackPrepareItem: (item) => { - const small = item.querySelector("small") as HTMLElement; - const text = small.textContent.trim(); - - return { - item, - span: small, - text, - }; - }, - enableVisibilityFilter: false, - filterPosition: "top", - }); - }, - onShow: () => { - this.itemListFilter!.reset(); - }, - title: Language.get("wcf.global.fontAwesome.selectIcon"), - }, - source: '', - }; - } + return content; } -let uiStyleFontAwesome: UiStyleFontAwesome; - -/** - * Sets the list of available icons, must be invoked prior to any call - * to the `open()` method. - */ -export function setup(): void { - if (!uiStyleFontAwesome) { - uiStyleFontAwesome = new UiStyleFontAwesome(); +let itemListFilter: UiItemListFilter | undefined = undefined; +function setupListeners(): void { + if (itemListFilter === undefined) { + itemListFilter = new UiItemListFilter("fontAwesomeIcons", { + callbackPrepareItem: (item) => { + const small = item.querySelector("small") as HTMLElement; + const text = small.textContent.trim(); + + return { + item, + span: small, + text, + }; + }, + enableVisibilityFilter: false, + filterPosition: "top", + }); + } else { + itemListFilter.reset(); } } @@ -103,11 +95,18 @@ export function setup(): void { * invoked with the selection icon's name as the only argument. */ export function open(callback: CallbackSelect): void { - if (!uiStyleFontAwesome) { - throw new Error( - "Missing icon data, please include the template before calling this method using `{include file='shared_fontAwesomeJavaScript'}`.", - ); - } + const dialog = dialogFactory().fromElement(getContent()).asConfirmation(); + dialog.addEventListener( + "font-awesome:selected", + (event: CustomEvent) => { + dialog.close(); + + callback(event.detail.icon, event.detail.forceSolid); + }, + { once: true }, + ); + + dialog.show(getPhrase("wcf.global.fontAwesome.selectIcon")); - uiStyleFontAwesome.open(callback); + setupListeners(); } diff --git a/wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Style/FontAwesome.js b/wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Style/FontAwesome.js index 409e125b59e..76f7d0ba805 100644 --- a/wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Style/FontAwesome.js +++ b/wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Style/FontAwesome.js @@ -6,80 +6,70 @@ * @license GNU Lesser General Public License * @woltlabExcludeBundle tiny */ -define(["require", "exports", "tslib", "../../Language", "../Dialog", "../ItemList/Filter"], function (require, exports, tslib_1, Language, Dialog_1, Filter_1) { +define(["require", "exports", "tslib", "WoltLabSuite/Core/Language", "../ItemList/Filter", "WoltLabSuite/Core/Component/Dialog"], function (require, exports, tslib_1, Language_1, Filter_1, Dialog_1) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); - exports.setup = setup; exports.open = open; - Language = tslib_1.__importStar(Language); - Dialog_1 = tslib_1.__importDefault(Dialog_1); Filter_1 = tslib_1.__importDefault(Filter_1); - class UiStyleFontAwesome { - callback = undefined; - iconList = undefined; - itemListFilter = undefined; - open(callback) { - this.callback = callback; - Dialog_1.default.open(this); - } - /** - * Selects an icon, notifies the callback and closes the dialog. - */ - click(event) { - event.preventDefault(); - const target = event.target; - const item = target.closest("li"); - const icon = item.querySelector("fa-icon"); - Dialog_1.default.close(this); - this.callback(icon.name, icon.solid); - } - _dialogSetup() { - return { - id: "fontAwesomeSelection", - options: { - onSetup: () => { - this.iconList = document.getElementById("fontAwesomeIcons"); - const icons = []; - window.getFontAwesome7Metadata().forEach(([, hasRegular], name) => { - if (hasRegular) { - icons.push(`
  • ${name}
  • `); - } - icons.push(`
  • ${name}
  • `); - }); - // build icons - this.iconList.innerHTML = icons.join(""); - this.iconList.addEventListener("click", (ev) => this.click(ev)); - this.itemListFilter = new Filter_1.default("fontAwesomeIcons", { - callbackPrepareItem: (item) => { - const small = item.querySelector("small"); - const text = small.textContent.trim(); - return { - item, - span: small, - text, - }; - }, - enableVisibilityFilter: false, - filterPosition: "top", - }); - }, - onShow: () => { - this.itemListFilter.reset(); + function createIconList() { + const ul = document.createElement("ul"); + ul.classList.add("fontAwesome__icons"); + ul.id = "fontAwesomeIcons"; + const icons = []; + window.getFontAwesome7Metadata().forEach(([, hasRegular], name) => { + if (hasRegular) { + icons.push(`
  • `); + } + icons.push(`
  • `); + }); + ul.innerHTML = icons.join(""); + return ul; + } + let content = undefined; + function getContent() { + if (content === undefined) { + const iconList = createIconList(); + iconList.addEventListener("click", (event) => { + event.preventDefault(); + const { target } = event; + if (!(target instanceof HTMLButtonElement)) { + return; + } + const icon = target.querySelector("fa-icon"); + const selectedEvent = new CustomEvent("font-awesome:selected", { + bubbles: true, + detail: { + icon: icon.name, + forceSolid: icon.solid, }, - title: Language.get("wcf.global.fontAwesome.selectIcon"), - }, - source: '
      ', - }; + }); + iconList.dispatchEvent(selectedEvent); + }); + content = document.createElement("div"); + content.id = "fontAwesomeSelection"; + content.append(iconList); } + return content; } - let uiStyleFontAwesome; - /** - * Sets the list of available icons, must be invoked prior to any call - * to the `open()` method. - */ - function setup() { - if (!uiStyleFontAwesome) { - uiStyleFontAwesome = new UiStyleFontAwesome(); + let itemListFilter = undefined; + function setupListeners() { + if (itemListFilter === undefined) { + itemListFilter = new Filter_1.default("fontAwesomeIcons", { + callbackPrepareItem: (item) => { + const small = item.querySelector("small"); + const text = small.textContent.trim(); + return { + item, + span: small, + text, + }; + }, + enableVisibilityFilter: false, + filterPosition: "top", + }); + } + else { + itemListFilter.reset(); } } /** @@ -87,9 +77,12 @@ define(["require", "exports", "tslib", "../../Language", "../Dialog", "../ItemLi * invoked with the selection icon's name as the only argument. */ function open(callback) { - if (!uiStyleFontAwesome) { - throw new Error("Missing icon data, please include the template before calling this method using `{include file='shared_fontAwesomeJavaScript'}`."); - } - uiStyleFontAwesome.open(callback); + const dialog = (0, Dialog_1.dialogFactory)().fromElement(getContent()).asConfirmation(); + dialog.addEventListener("font-awesome:selected", (event) => { + dialog.close(); + callback(event.detail.icon, event.detail.forceSolid); + }, { once: true }); + dialog.show((0, Language_1.getPhrase)("wcf.global.fontAwesome.selectIcon")); + setupListeners(); } }); diff --git a/wcfsetup/install/files/style/ui/fontAwesome.scss b/wcfsetup/install/files/style/ui/fontAwesome.scss index ebedd715abb..df98cb8351a 100644 --- a/wcfsetup/install/files/style/ui/fontAwesome.scss +++ b/wcfsetup/install/files/style/ui/fontAwesome.scss @@ -1,34 +1,35 @@ -.fontAwesomeIcons { +.fontAwesome__icons { border: 1px solid var(--wcfContentBorderInner); + display: grid; + grid-auto-flow: row; + grid-template-columns: repeat(3, 150px); max-height: 540px; overflow: auto; @media only screen and (min-height: 700px) and (max-height: 800px) { max-height: 450px; } +} - > li { - display: inline-flex; - flex-wrap: wrap; - justify-content: center; - padding: 10px 0; - width: 150px; +.fontAwesome__icon { + align-items: center; + display: flex; + flex-direction: column; + padding: 10px 0; + width: 100%; +} - &:hover { - background-color: var(--wcfButtonBackgroundActive); - color: var(--wcfButtonTextActive); - cursor: pointer; +.fontAwesome__icon__name { + color: var(--wcfContentDimmedText); +} - > small { - color: inherit; - cursor: pointer; - } - } +@media (hover: hover) { + .fontAwesome__icon:hover { + background-color: var(--wcfButtonBackgroundActive); + color: var(--wcfButtonTextActive); - > small { - color: var(--wcfContentDimmedText); - flex: 0 0 100%; - text-align: center; + .fontAwesome__icon__name { + color: inherit; } } }