From 592a9011334dae0032f75717d3a0e93c58c00c16 Mon Sep 17 00:00:00 2001 From: Nelson Rodrigues Date: Sun, 3 Mar 2024 20:50:26 +0000 Subject: [PATCH] Add new "shortname" field to the Stylesheet (WIP) --- .eslintrc.json | 9 +- .vscode/settings.json | 5 +- dist/options.css | 107 ++++++++++++++++-- src/Stylesheet.ts | 15 +++ src/common/If.tsx | 16 +++ src/options/Options.tsx | 15 +-- src/options/OptionsReducer.test.ts | 49 ++++---- src/options/OptionsReducer.ts | 69 ++++++----- src/options/components/EditModal.tsx | 54 +++++++++ .../{ => components}/StylesheetForm.tsx | 6 +- .../{ => components}/StylesheetItem.tsx | 6 +- .../components/StylesheetItemTableRow.tsx | 55 +++++++++ .../{ => components}/StylesheetList.tsx | 6 +- .../components/StylesheetListTable.tsx | 84 ++++++++++++++ src/popup/PopupSearch.tsx | 4 +- src/popup/StylesheetItem.tsx | 9 +- src/popup/StylesheetList.tsx | 21 ++-- src/storage.ts | 15 ++- src/types.d.ts | 5 +- src/utils.ts | 31 +++-- 20 files changed, 467 insertions(+), 114 deletions(-) create mode 100644 src/Stylesheet.ts create mode 100644 src/common/If.tsx create mode 100644 src/options/components/EditModal.tsx rename src/options/{ => components}/StylesheetForm.tsx (93%) rename src/options/{ => components}/StylesheetItem.tsx (97%) create mode 100644 src/options/components/StylesheetItemTableRow.tsx rename src/options/{ => components}/StylesheetList.tsx (85%) create mode 100644 src/options/components/StylesheetListTable.tsx diff --git a/.eslintrc.json b/.eslintrc.json index cf33d40..57e5981 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -49,6 +49,13 @@ } ], "newline-before-return": "error", + "no-unused-vars": "off", + "@typescript-eslint/no-unused-vars": [ + "warn", + { + "argsIgnorePattern": "^_" + } + ], "object-curly-newline": [ "error", { @@ -59,7 +66,7 @@ "error", "always" ], - "object-property-newline": "error", + // "object-property-newline": "error", "padding-line-between-statements": [ "error", { diff --git a/.vscode/settings.json b/.vscode/settings.json index 881772b..514ec74 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -2,8 +2,9 @@ "files.eol": "\n", "eslint.enable": true, "eslint.run": "onSave", + "editor.formatOnSave": false, "editor.codeActionsOnSave": { - "source.fixAll": true, - "source.organizeImports": true + "source.fixAll": "explicit", + "source.organizeImports": "explicit" } } diff --git a/dist/options.css b/dist/options.css index 274571f..4864b77 100644 --- a/dist/options.css +++ b/dist/options.css @@ -1,4 +1,6 @@ :root { + --main-width: 800px; + /* Color */ --color-primary: #F03738; --color-primary-shade-1: #F98484; @@ -89,8 +91,9 @@ body { } .column { - max-width: 650px; + max-width: var(--main-width); margin: auto; + padding-inline: var(--space-4); } /* Header */ @@ -99,6 +102,7 @@ header { background-color: var(--color-neutral-7); color: var(--color-neutral-0); padding: var(--space-3); + padding-inline: 0; position: fixed; top: 0; left: 0; @@ -178,15 +182,15 @@ main { /* Buttons > Small */ .button--small { + display: inline-flex; + justify-content: center; + align-items: center; min-width: 32px; height: 32px; border-radius: var(--border-radius); padding-inline: var(--space-2); margin-left: var(--space-2); position: relative; - display: flex; - justify-content: center; - align-items: center; outline: 0; cursor: pointer; box-shadow: none; @@ -212,6 +216,35 @@ main { height: auto; } +/* Table */ + +table { + width: 100%; + border-collapse: collapse; +} + +table td, +table th { + padding: var(--space-2); +} + +table th { + font-size: 12px; + text-transform: uppercase; + border-bottom: 1px solid var(--color-neutral-2); +} + +table tbody tr td { + border-bottom: 1px solid var(--color-neutral-2); + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; +} + +table tr:last-child td { + border-bottom: 0; +} + /* Stylesheets Form */ .stylesheets-form { @@ -245,16 +278,18 @@ main { /* Stylesheets List */ .stylesheets-list { + padding: var(--space-2); margin-top: var(--space-6); width: 100%; + text-align: left; + background-color: var(--color-neutral-0); + border-radius: var(--border-radius); + box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.1), 0 1px 5px 0 rgba(0, 0, 0, 0.12); } /* Stylesheet Item */ .stylesheet { - display: flex; - justify-content: space-between; - align-items: center; gap: var(--space-2); height: 50px; padding: var(--space-2); @@ -305,3 +340,61 @@ main { text-align: left; font-size: 12px; } + +.whitespace-nowrap { + white-space: nowrap; +} + +.modal { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + display: flex; + justify-content: center; + align-items: center; + flex-direction: column; + z-index: 10; + opacity: 0; + pointer-events: none; +} + +.modal__overlay { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: rgba(0, 0, 0, 0.3); +} + +.modal__main { + background-color: #FFF; + padding: var(--space-4); + position: relative; + z-index: 1; + opacity: 0; + border-radius: var(--border-radius); +} + +.modal--show { + opacity: 1; + pointer-events: all; +} + +.modal--show .modal__main { + animation: fadeUp 300ms ease-out forwards; +} + +@keyframes fadeUp { + from { + transform: translateY(20px); + opacity: 0; + } + + to { + transform: translateY(0); + opacity: 1; + } +} diff --git a/src/Stylesheet.ts b/src/Stylesheet.ts new file mode 100644 index 0000000..8a44624 --- /dev/null +++ b/src/Stylesheet.ts @@ -0,0 +1,15 @@ +import { getStylesheetName } from "./utils"; + +export class Stylesheet { + url: string; + shortname: string; + + constructor (url: string, shortname = "") { + this.url = url; + this.shortname = shortname; + } + + get name () { + return this.shortname || getStylesheetName(this.url); + } +} diff --git a/src/common/If.tsx b/src/common/If.tsx new file mode 100644 index 0000000..e824682 --- /dev/null +++ b/src/common/If.tsx @@ -0,0 +1,16 @@ +interface IProps { + children: JSX.Element | JSX.Element[] | string; + condition: boolean; +} + +function If (props: IProps) { + const { children, condition } = props; + + if (condition) { + return <>{children}; + } + + return null; +} + +export default If; diff --git a/src/options/Options.tsx b/src/options/Options.tsx index c33046b..3213b3e 100644 --- a/src/options/Options.tsx +++ b/src/options/Options.tsx @@ -1,9 +1,10 @@ import { useEffect, useReducer } from "react"; +import { Stylesheet } from "../Stylesheet"; import { updateStorage } from "../storage"; import { StorageData } from "../types"; import { OptionsReducer } from "./OptionsReducer"; -import { StylesheetForm } from "./StylesheetForm"; -import { StylesheetList } from "./StylesheetList"; +import { StylesheetForm } from "./components/StylesheetForm"; +import { StylesheetListTable } from "./components/StylesheetListTable"; interface IProps { initialState: StorageData; @@ -23,25 +24,25 @@ function Options (props: IProps) { }); }; - const updateStylesheet = (url: string, newURL: string) => { + const updateStylesheet = (prevStylesheet: Stylesheet, newStylesheet: Stylesheet) => { setState({ type: "update", - url: url, - newURL: newURL, + prevStyleheet: prevStylesheet, + newStylesheet: newStylesheet }); }; const removeStylesheet = (url: string) => { setState({ type: "remove", - url: url, + url: url }); }; return ( <> - { const urlToAdd = "http://127.0.0.1:3000/public/css/theme-A.css"; const updatedState = OptionsReducer(states.empty, { type: "add", - url: urlToAdd + url: urlToAdd }); - + expect(updatedState).toStrictEqual(states.oneStylesheet); }); - + test("Renames a stylesheet", () => { - const urlToRename = "http://127.0.0.1:3000/public/css/theme-A.css"; - const newUrl = "http://127.0.0.1:3000/public/css/theme-B.css"; + const prevStylesheet = new Stylesheet("http://127.0.0.1:3000/public/css/theme-A.css"); + const newStylesheet = new Stylesheet("http://127.0.0.1:3000/public/css/theme-B.css"); const updatedState = OptionsReducer(states.oneStylesheet, { type: "update", - url: urlToRename, - newURL: newUrl + prevStyleheet: prevStylesheet, + newStylesheet: newStylesheet }); - + expect(updatedState).toStrictEqual(states.renamedStyleSheet); }); }); @@ -84,9 +85,9 @@ describe("Removing stylesheets", () => { const urlToRemove = "http://127.0.0.1:3000/public/css/theme-A.css"; const updatedState = OptionsReducer(states.oneStylesheet, { type: "remove", - url: urlToRemove + url: urlToRemove }); - + expect(updatedState).toStrictEqual(states.empty); }); @@ -94,9 +95,9 @@ describe("Removing stylesheets", () => { const urlToRemove = "http://127.0.0.1:3000/public/css/theme-A.css"; const updatedState = OptionsReducer(states.oneInjectedStylesheet, { type: "remove", - url: urlToRemove + url: urlToRemove }); - + expect(updatedState).toStrictEqual(states.empty); }); @@ -104,9 +105,9 @@ describe("Removing stylesheets", () => { const urlToRemove = "http://127.0.0.1:3000/public/css/theme-A.css"; const updatedState = OptionsReducer(states.multipleTabsInjected, { type: "remove", - url: urlToRemove + url: urlToRemove }); - + expect(updatedState).toStrictEqual(states.empty); }); @@ -114,9 +115,9 @@ describe("Removing stylesheets", () => { const urlToRemove = "http://127.0.0.1:3000/public/css/theme-B.css"; const updatedState = OptionsReducer(states.multipleInjectedStylesheets, { type: "remove", - url: urlToRemove + url: urlToRemove }); - + expect(updatedState).toStrictEqual(states.oneInjectedStylesheet); }); }); diff --git a/src/options/OptionsReducer.ts b/src/options/OptionsReducer.ts index ebd248d..290b366 100644 --- a/src/options/OptionsReducer.ts +++ b/src/options/OptionsReducer.ts @@ -1,9 +1,10 @@ +import { Stylesheet } from "../Stylesheet"; import { InjectedTabs, StorageData } from "../types"; -import { validateURL } from "../utils"; +import { cond, validateURL } from "../utils"; type Action = | { type: "add"; url: string; } - | { type: "update"; url: string; newURL: string; } + | { type: "update"; prevStyleheet: Stylesheet; newStylesheet: Stylesheet; } | { type: "remove"; url: string; }; export function OptionsReducer (state: StorageData, action: Action): StorageData { @@ -12,7 +13,7 @@ export function OptionsReducer (state: StorageData, action: Action): StorageData return add(state, action.url); case "update": - return update(state, action.url, action.newURL); + return update(state, action.prevStyleheet, action.newStylesheet); case "remove": return remove(state, action.url); @@ -23,54 +24,68 @@ export function OptionsReducer (state: StorageData, action: Action): StorageData } function add (state: StorageData, url: string): StorageData { - const urlExists = state.stylesheets.find((stylesheet) => stylesheet === url); + const urlExists = state.stylesheets.find((stylesheet: Stylesheet) => stylesheet.url === url); const isValid = validateURL(url); if (urlExists || !isValid) { return state; } - const updatedState = { + return { ...state, - stylesheets: [ ...state.stylesheets, url ] + stylesheets: [ + ...state.stylesheets, + (new Stylesheet(url)) + ] }; - - return updatedState; } -function update (state: StorageData, url: string, newURL: string): StorageData { - const stylesheets = state.stylesheets.map((stylesheetURL) => { - return stylesheetURL === url ? newURL : stylesheetURL; - }); +function update (state: StorageData, prevStylesheet: Stylesheet, newStylesheet: Stylesheet): StorageData { + const isDuplicated = state.stylesheets.find((item) => { return item.url === newStylesheet.url; }) && prevStylesheet.url !== newStylesheet.url; - const updatedState = { - ...state, - stylesheets - }; + // If the new URL already exists, do nothing + if (isDuplicated) { + return state; + } + + const updateStylesheet = (item: Stylesheet) => cond((item.url === prevStylesheet.url), newStylesheet, item); + const stylesheets = state.stylesheets.map(updateStylesheet); + const injected = updateInjectedURL(state.injected, prevStylesheet.url, newStylesheet.url); - return updatedState; + return { stylesheets, injected }; } function remove (state: StorageData, url: string): StorageData { - const stylesheets = state.stylesheets.filter((stylesheet) => stylesheet !== url); + const stylesheets = state.stylesheets.filter((item: Stylesheet) => item.url !== url); const injected = removeInjectedURL(state.injected, url); - const updatedState = { - stylesheets, - injected - }; - return updatedState; + return { stylesheets, injected }; +} + +function updateInjectedURL (injected: InjectedTabs, urlToUpdate: string, newURL: string): InjectedTabs { + const updatedTabs = Object + .entries(injected) + .map(([ tabId, urlList ]) => { + return [ + tabId, + urlList.map((url: string) => cond((url === urlToUpdate), newURL, url)) + ]; + }) + .filter(([ _tabId, urlList ]) => urlList.length > 0); + + return Object.fromEntries(updatedTabs); } function removeInjectedURL (injected: InjectedTabs, urlToRemove: string): InjectedTabs { const updatedTabs = Object .entries(injected) .map(([ tabId, urlList ]) => { - return [ tabId, urlList.filter((url: string) => url !== urlToRemove) ]; + return [ + tabId, + urlList.filter((url: string) => url !== urlToRemove) + ]; }) - .filter(([ _tabId, urlList ]) => { - return urlList.length > 0; - }); + .filter(([ _tabId, urlList ]) => urlList.length > 0); return Object.fromEntries(updatedTabs); } diff --git a/src/options/components/EditModal.tsx b/src/options/components/EditModal.tsx new file mode 100644 index 0000000..1a04c43 --- /dev/null +++ b/src/options/components/EditModal.tsx @@ -0,0 +1,54 @@ +import { ChangeEvent, FormEvent, useState } from "react"; +import { Stylesheet } from "../../Stylesheet"; +import { assign } from "../../utils"; + +interface IProps { + stylesheet: Stylesheet; + onUpdate: (stylesheet: Stylesheet) => unknown; + onCancel: () => unknown; +} + +export function EditModal (props: IProps) { + const { stylesheet, onUpdate, onCancel } = props; + const [ editStylesheet, setEditStylesheet ] = useState(stylesheet); + + const isValid = () => { + return true; + }; + + const onSave = (ev: FormEvent) => { + ev.preventDefault(); + isValid() && onUpdate(editStylesheet); + }; + + const onEdit = (key: string, value: string) => setEditStylesheet(assign(editStylesheet, key, value)); + const onEditURL = (ev: ChangeEvent) => onEdit("url", ev.target.value); + const onEditShortname = (ev: ChangeEvent) => onEdit("shortname", ev.target.value); + + return ( +
+
+ +
+
+
+
+ + +
+ +
+ + +
+ +
+ + +
+
+
+
+
+ ); +} diff --git a/src/options/StylesheetForm.tsx b/src/options/components/StylesheetForm.tsx similarity index 93% rename from src/options/StylesheetForm.tsx rename to src/options/components/StylesheetForm.tsx index 16387bf..fcedc9e 100644 --- a/src/options/StylesheetForm.tsx +++ b/src/options/components/StylesheetForm.tsx @@ -1,5 +1,5 @@ import { FormEvent, useState } from "react"; -import { setCSSClasses, validateURL } from "../utils"; +import { getClassName, validateURL } from "../../utils"; const isFirefox = /Firefox\/\d{1,2}/.test(navigator.userAgent); const mixedContentURL = "https://developer.mozilla.org/en-US/docs/Web/Security/Mixed_content#Warnings_in_Web_Console"; @@ -35,12 +35,12 @@ export function StylesheetForm (props: IProps) { const inputClassName = isFormValid ? "" : "not-valid"; - const errorClassName = setCSSClasses([ + const errorClassName = getClassName([ "text-error", isFormValid ? "hidden" : "", ]); - const mixedContentClassName = setCSSClasses([ + const mixedContentClassName = getClassName([ "text-help", isFirefox ? "" : "hidden", ]); diff --git a/src/options/StylesheetItem.tsx b/src/options/components/StylesheetItem.tsx similarity index 97% rename from src/options/StylesheetItem.tsx rename to src/options/components/StylesheetItem.tsx index 0bb761b..d1a35a3 100644 --- a/src/options/StylesheetItem.tsx +++ b/src/options/components/StylesheetItem.tsx @@ -1,14 +1,14 @@ import { FormEvent, useRef, useState } from "react"; -import { validateURL } from "../utils"; +import { validateURL } from "../../utils"; interface IProps { - url: string; + stylesheet: string; onRemove: (url: string) => unknown; onUpdate: (url: string, newURL: string) => unknown; } export function StylesheetItem (props: IProps) { - const { url, onRemove, onUpdate } = props; + const { stylesheet: url, onRemove, onUpdate } = props; const [ isEdit, setIsEdit ] = useState(false); const [ isFormValid, setValidForm ] = useState(true); const textInputRef = useRef(null); diff --git a/src/options/components/StylesheetItemTableRow.tsx b/src/options/components/StylesheetItemTableRow.tsx new file mode 100644 index 0000000..f620a6e --- /dev/null +++ b/src/options/components/StylesheetItemTableRow.tsx @@ -0,0 +1,55 @@ +import { Stylesheet } from "../../Stylesheet"; +import If from "../../common/If"; + +interface IProps { + stylesheet: Stylesheet; + onEdit: (stylesheet: Stylesheet) => unknown; + onRemove: (url: string) => unknown; +} + +const iconPencil = ; +const iconX = ; + +export function StylesheetItemTableRow (props: IProps) { + const { stylesheet, onEdit, onRemove } = props; + + const handleRemove = () => onRemove(stylesheet.url); + const handleEdit = () => onEdit(stylesheet); + + return ( + <> + + + + {stylesheet.url} + + + + + + {stylesheet.shortname} + + + + + + + + + + + + + + ); +} diff --git a/src/options/StylesheetList.tsx b/src/options/components/StylesheetList.tsx similarity index 85% rename from src/options/StylesheetList.tsx rename to src/options/components/StylesheetList.tsx index 914ad01..abfc808 100644 --- a/src/options/StylesheetList.tsx +++ b/src/options/components/StylesheetList.tsx @@ -1,4 +1,4 @@ -import { setCSSClasses } from "../utils"; +import { getClassName } from "../../utils"; import { StylesheetItem } from "./StylesheetItem"; interface IProps { @@ -13,13 +13,13 @@ export function StylesheetList (props: IProps) { const stylesheetsList = list.map((stylesheet, index) => ); - const messageClassName = setCSSClasses([ + const messageClassName = getClassName([ "stylesheets-message", (list.length > 0 ? "hidden" : "") ]); diff --git a/src/options/components/StylesheetListTable.tsx b/src/options/components/StylesheetListTable.tsx new file mode 100644 index 0000000..0544a8f --- /dev/null +++ b/src/options/components/StylesheetListTable.tsx @@ -0,0 +1,84 @@ +import { useState } from "react"; +import { Stylesheet } from "../../Stylesheet"; +import If from "../../common/If"; +import { getClassName } from "../../utils"; +import { EditModal } from "./EditModal"; +import { StylesheetItemTableRow } from "./StylesheetItemTableRow"; + +interface IProps { + list: Stylesheet[]; + onRemove: (url: string) => unknown; + onUpdate: (prevStylesheet: Stylesheet, newStylesheet: Stylesheet) => unknown; +} + +export function StylesheetListTable (props: IProps) { + const { list, onRemove, onUpdate } = props; + const [ isEdit, setIsEdit ] = useState(false); + const [ editStylesheet, setEditStylesheet ] = useState(); + + const handleEdit = (stylesheet: Stylesheet) => { + setEditStylesheet(stylesheet); + setIsEdit(true); + }; + + const handleCancel = () => setIsEdit(false); + + const handleUpdate = (newStylesheet: Stylesheet) => { + if (editStylesheet) { + onUpdate(editStylesheet, newStylesheet); + setIsEdit(false); + } + }; + + const stylesheetsListTableContents = list.map((stylesheet, index) => + + ); + + const messageClassName = getClassName([ + "stylesheets-message", + (list.length > 0 ? "hidden" : "") + ]); + + const showEditModal = () => { + if (editStylesheet && isEdit) { + return ( + + ); + } + + return <>; + }; + + return ( + <> +
No stylesheets added yet.
+ 0}> +
+ + + + + + + + + + {stylesheetsListTableContents} + +
URLShort Name
+ + {showEditModal()} +
+
+ + ); +} diff --git a/src/popup/PopupSearch.tsx b/src/popup/PopupSearch.tsx index 58d3804..9da1fa5 100644 --- a/src/popup/PopupSearch.tsx +++ b/src/popup/PopupSearch.tsx @@ -1,5 +1,5 @@ import { useRef } from "react"; -import { setCSSClasses } from "../utils"; +import { getClassName } from "../utils"; interface IProps { search: string; @@ -23,7 +23,7 @@ export function PopupSearch (props: IProps) { } }; - const iconClassName = setCSSClasses([ + const iconClassName = getClassName([ "icon-cross", search.length > 0 ? "" : "hidden", ]); diff --git a/src/popup/StylesheetItem.tsx b/src/popup/StylesheetItem.tsx index 768c5a7..c6fd698 100644 --- a/src/popup/StylesheetItem.tsx +++ b/src/popup/StylesheetItem.tsx @@ -1,7 +1,8 @@ -import { getStylesheetName, setCSSClasses } from "../utils"; +import { Stylesheet } from "../Stylesheet"; +import { getClassName } from "../utils"; interface IProps { - stylesheet: string; + stylesheet: Stylesheet; isSelected: boolean; isHidden: boolean; selectionOrder: string | null; @@ -11,9 +12,9 @@ interface IProps { export function StylesheetItem (props: IProps) { const { stylesheet, isSelected, selectionOrder, isHidden, onActiveToggle } = props; const handleActiveChange = () => onActiveToggle(!isSelected); - const stylesheetName = getStylesheetName(stylesheet); + const stylesheetName = stylesheet.name; - const className = setCSSClasses([ + const className = getClassName([ "stylesheet", isHidden ? "hidden" : "", isSelected ? "stylesheet--active" : "", diff --git a/src/popup/StylesheetList.tsx b/src/popup/StylesheetList.tsx index 9f6db5c..54ed840 100644 --- a/src/popup/StylesheetList.tsx +++ b/src/popup/StylesheetList.tsx @@ -1,13 +1,13 @@ +import { Stylesheet } from "../Stylesheet"; import { + getClassName, getSelectionOrder, - getStylesheetName, - maxSelectionCount, - setCSSClasses + maxSelectionCount } from "../utils"; import { StylesheetItem } from "./StylesheetItem"; interface IProps { - list: string[]; + list: Stylesheet[]; activeList: string[]; search: string; onSelectionChange: (isActive: boolean, url: string) => unknown; @@ -19,14 +19,13 @@ export function StylesheetList (props: IProps) { const searchIsEmpty = search.trim().length === 0; const searchRegex = new RegExp(search.trim(), "gi"); - const stylesheets = list.map((stylesheet: string, index: number) => { - const isSelected = activeList.includes(stylesheet); - const selectionOrder = getSelectionOrder(stylesheet, activeList); - const name = getStylesheetName(stylesheet); - const isFiltered = !searchIsEmpty && name.match(searchRegex) === null; + const stylesheets = list.map((stylesheet: Stylesheet, index: number) => { + const isSelected = activeList.includes(stylesheet.url); + const selectionOrder = getSelectionOrder(stylesheet.url, activeList); + const isFiltered = !searchIsEmpty && stylesheet.name.match(searchRegex) === null; const handleActiveChange = (isActive: boolean) => { - return onSelectionChange(isActive, stylesheet); + return onSelectionChange(isActive, stylesheet.url); }; return ( @@ -41,7 +40,7 @@ export function StylesheetList (props: IProps) { ); }); - const listClassName = setCSSClasses([ + const listClassName = getClassName([ "stylesheets-list", activeList.length > maxSelectionCount ? "stylesheets-list--emoji" : "", ]); diff --git a/src/storage.ts b/src/storage.ts index 9bf3d66..784a0ee 100644 --- a/src/storage.ts +++ b/src/storage.ts @@ -1,4 +1,5 @@ -import { StorageData, Stylesheets } from "./types"; +import { Stylesheet } from "./Stylesheet"; +import { StorageData } from "./types"; import { env, sortByName } from "./utils"; export async function loadStorage (): Promise { @@ -15,10 +16,12 @@ export function updateStorage (data: StorageData): Promise { return env.storage.local.set({ SuperCSSInject: data }); } -function importStylesheets (stylesheets: { url: string }[] | string[]): Stylesheets { - return stylesheets.map((stylesheet: { url: string } | string) => { - const isString = typeof stylesheet === "string"; - - return isString ? stylesheet : stylesheet.url; +function importStylesheets (stylesheets: Stylesheet[] | string[]): Stylesheet[] { + return stylesheets.map((stylesheet: Stylesheet | string) => { + if (typeof stylesheet === "string") { + return new Stylesheet(stylesheet); + } + + return new Stylesheet(stylesheet.url, stylesheet.shortname); }).sort(sortByName); } diff --git a/src/types.d.ts b/src/types.d.ts index e9ec214..9a7ec61 100644 --- a/src/types.d.ts +++ b/src/types.d.ts @@ -1,13 +1,12 @@ export type Tab = chrome.tabs.Tab | undefined; export type TabId = number | undefined; -export type Stylesheets = string[]; export interface InjectedTabs { - [id: number]: Stylesheets | undefined; + [id: number]: string[] | undefined; } export interface StorageData { - stylesheets: Stylesheets; + stylesheets: Stylesheet[]; injected: InjectedTabs; } diff --git a/src/utils.ts b/src/utils.ts index 92e5eba..742d096 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,5 +1,6 @@ +import { Stylesheet } from "./Stylesheet"; import { loadStorage } from "./storage"; -import { Stylesheets, Tab } from "./types"; +import { Tab } from "./types"; /** * Alias for accessing the browser extension API. @@ -26,13 +27,13 @@ export function getStylesheetName (url: string) { /** * Sorts URLs by the last part, aka the filename. * - * @param urlA URL string - * @param urlB URL string + * @param stylesheetA Stylesheet + * @param stylesheetB Stylesheet * @returns Order between the two strings */ -export function sortByName (urlA: string, urlB: string) { - const nameA = getStylesheetName(urlA).toLowerCase(); - const nameB = getStylesheetName(urlB).toLowerCase(); +export function sortByName (stylesheetA: Stylesheet, stylesheetB: Stylesheet) { + const nameA = stylesheetA.name.toLowerCase(); + const nameB = stylesheetB.name.toLowerCase(); if (nameA < nameB) { return -1; @@ -48,11 +49,11 @@ export function sortByName (urlA: string, urlB: string) { * * @returns Object with list of stylesheets active per browser tab */ -export async function getInjectedStylesheets (tabId: number): Promise { - const storage = await loadStorage(); +// export async function getInjectedStylesheets (tabId: number): Promise { +// const storage = await loadStorage(); - return storage.injected[tabId] || []; -} +// return storage.injected[tabId] || []; +// } /** * Tries to get the current active browser tab (if any). @@ -80,7 +81,7 @@ export async function getCurrentTab (): Promise { * @param classes An array of CSS classes * @returns A string with CSS classes separated by spaces */ -export function setCSSClasses (classes: string[]): string { +export function getClassName (classes: string[]): string { return classes.join(" ").trim(); } @@ -182,3 +183,11 @@ export function sendInjectMessageToTab (tabId: number, urlList: string[]) { urlList: urlList }); } + +export function cond (cond: boolean, trueValue: A, falseValue: B) { + return cond ? trueValue : falseValue; +} + +export function assign (obj: T, key: string, value: unknown) { + return { ...obj, [key]: value }; +}