mirror of
https://github.com/notion-enhancer/notion-enhancer.git
synced 2025-04-10 15:39:01 +00:00
feat(menu): profile deletion via confirmation popup
This commit is contained in:
parent
23834475c0
commit
72332acc58
@ -693,13 +693,13 @@ const styleAccents = () => {
|
|||||||
"rgb(211, 79, 67)",
|
"rgb(211, 79, 67)",
|
||||||
"rgb(205, 73, 69)",
|
"rgb(205, 73, 69)",
|
||||||
],
|
],
|
||||||
|
secondaryHover = cssVariable({
|
||||||
|
name: "accent-secondary_hover",
|
||||||
|
value: "rgba(235, 87, 87, 0.1)",
|
||||||
|
}),
|
||||||
secondaryContrast = cssVariable({
|
secondaryContrast = cssVariable({
|
||||||
name: "accent-secondary_contrast",
|
name: "accent-secondary_contrast",
|
||||||
value: "white",
|
value: "white",
|
||||||
}),
|
|
||||||
secondaryTransparent = cssVariable({
|
|
||||||
name: "accent-secondary_transparent",
|
|
||||||
value: "rgba(235, 87, 87, 0.1)",
|
|
||||||
});
|
});
|
||||||
overrideStyle({
|
overrideStyle({
|
||||||
property: "color",
|
property: "color",
|
||||||
@ -746,7 +746,7 @@ const styleAccents = () => {
|
|||||||
});
|
});
|
||||||
overrideStyle({
|
overrideStyle({
|
||||||
property: "background",
|
property: "background",
|
||||||
variable: secondaryTransparent,
|
variable: secondaryHover,
|
||||||
specificity: ["value"],
|
specificity: ["value"],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ import { fileURLToPath } from "node:url";
|
|||||||
const dependencies = {
|
const dependencies = {
|
||||||
"htm.min.js": "https://unpkg.com/htm@3.1.1/mini/index.js",
|
"htm.min.js": "https://unpkg.com/htm@3.1.1/mini/index.js",
|
||||||
"twind.min.js": "https://unpkg.com/@twind/cdn@1.0.7/cdn.global.js",
|
"twind.min.js": "https://unpkg.com/@twind/cdn@1.0.7/cdn.global.js",
|
||||||
"lucide.min.js": "https://unpkg.com/lucide@0.104.0/dist/umd/lucide.min.js",
|
"lucide.min.js": "https://unpkg.com/lucide@0.105.0/dist/umd/lucide.min.js",
|
||||||
"coloris.min.js":
|
"coloris.min.js":
|
||||||
"https://cdn.jsdelivr.net/gh/mdbassit/Coloris@latest/dist/coloris.min.js",
|
"https://cdn.jsdelivr.net/gh/mdbassit/Coloris@latest/dist/coloris.min.js",
|
||||||
"coloris.min.css":
|
"coloris.min.css":
|
||||||
|
@ -39,38 +39,35 @@ const sendMessage = (channel, message) => {
|
|||||||
const initDatabase = (namespace, fallbacks = {}) => {
|
const initDatabase = (namespace, fallbacks = {}) => {
|
||||||
if (Array.isArray(namespace)) namespace = namespace.join("__");
|
if (Array.isArray(namespace)) namespace = namespace.join("__");
|
||||||
namespace = namespace ? namespace + "__" : "";
|
namespace = namespace ? namespace + "__" : "";
|
||||||
|
const namespaceify = (key) =>
|
||||||
|
key.startsWith(namespace) ? key : namespace + key;
|
||||||
return {
|
return {
|
||||||
get: async (key) => {
|
get: async (key) => {
|
||||||
const fallback = fallbacks[key];
|
const fallback = fallbacks[key];
|
||||||
key = key.startsWith(namespace) ? key : namespace + key;
|
key = namespaceify(key);
|
||||||
return new Promise((res, _rej) => {
|
return (await chrome.storage.local.get([key]))[key] ?? fallback;
|
||||||
chrome.storage.local.get([key], ({ [key]: value }) => {
|
|
||||||
return res(value ?? fallback);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
set: async (key, value) => {
|
set: (key, value) => {
|
||||||
key = key.startsWith(namespace) ? key : namespace + key;
|
key = namespaceify(key);
|
||||||
return new Promise((res, _rej) => {
|
return chrome.storage.local.set({ [key]: value });
|
||||||
chrome.storage.local.set({ [key]: value }, () => res(true));
|
},
|
||||||
});
|
remove: (keys) => {
|
||||||
|
keys = Array.isArray(keys) ? keys : [keys];
|
||||||
|
keys = keys.map(namespaceify);
|
||||||
|
return chrome.storage.local.remove(keys);
|
||||||
},
|
},
|
||||||
export: async () => {
|
export: async () => {
|
||||||
const obj = await new Promise((res, _rej) => {
|
const obj = await chrome.storage.local.get();
|
||||||
chrome.storage.local.get((value) => res(value));
|
|
||||||
});
|
|
||||||
if (!namespace) return obj;
|
if (!namespace) return obj;
|
||||||
const entries = Object.entries(obj)
|
const entries = Object.entries(obj)
|
||||||
.filter(([key]) => key.startsWith(namespace))
|
.filter(([key]) => key.startsWith(namespace))
|
||||||
.map(([key, value]) => [key.slice(namespace.length), value]);
|
.map(([key, value]) => [key.slice(namespace.length), value]);
|
||||||
return Object.fromEntries(entries);
|
return Object.fromEntries(entries);
|
||||||
},
|
},
|
||||||
import: async (obj) => {
|
import: (obj) => {
|
||||||
const entries = Object.entries(obj) //
|
const entries = Object.entries(obj) //
|
||||||
.map(([key, value]) => [namespace + key, value]);
|
.map(([key, value]) => [namespace + key, value]);
|
||||||
return new Promise((res, _rej) => {
|
return chrome.storage.local.set(Object.fromEntries(entries));
|
||||||
chrome.storage.local.set(Object.fromEntries(entries), () => res(true));
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -50,6 +50,8 @@ let __db;
|
|||||||
const initDatabase = (namespace, fallbacks = {}) => {
|
const initDatabase = (namespace, fallbacks = {}) => {
|
||||||
if (Array.isArray(namespace)) namespace = namespace.join("__");
|
if (Array.isArray(namespace)) namespace = namespace.join("__");
|
||||||
namespace = namespace ? namespace + "__" : "";
|
namespace = namespace ? namespace + "__" : "";
|
||||||
|
const namespaceify = (key) =>
|
||||||
|
key.startsWith(namespace) ? key : namespace + key;
|
||||||
|
|
||||||
// schema:
|
// schema:
|
||||||
// - ("profileIds") = $profileId[]
|
// - ("profileIds") = $profileId[]
|
||||||
@ -68,11 +70,11 @@ const initDatabase = (namespace, fallbacks = {}) => {
|
|||||||
init.run();
|
init.run();
|
||||||
__db = db;
|
__db = db;
|
||||||
|
|
||||||
// prettier-ignore
|
|
||||||
const insert = db.prepare(`INSERT INTO ${table} (key, value) VALUES (?, ?)`),
|
const insert = db.prepare(`INSERT INTO ${table} (key, value) VALUES (?, ?)`),
|
||||||
// prettier-ignore
|
|
||||||
update = db.prepare(`UPDATE ${table} SET value = ? WHERE key = ?`),
|
update = db.prepare(`UPDATE ${table} SET value = ? WHERE key = ?`),
|
||||||
select = db.prepare(`SELECT * FROM ${table} WHERE key = ? LIMIT 1`),
|
select = db.prepare(`SELECT * FROM ${table} WHERE key = ? LIMIT 1`),
|
||||||
|
remove = db.prepare(`DELETE FROM ${table} WHERE key = ?`),
|
||||||
|
removeMany = db.transaction((arr) => arr.forEach((key) => remove.run(key))),
|
||||||
dump = db.prepare(`SELECT * FROM ${table}`),
|
dump = db.prepare(`SELECT * FROM ${table}`),
|
||||||
populate = db.transaction((obj) => {
|
populate = db.transaction((obj) => {
|
||||||
for (const key in obj) {
|
for (const key in obj) {
|
||||||
@ -85,7 +87,7 @@ const initDatabase = (namespace, fallbacks = {}) => {
|
|||||||
// wrap methods in promises for consistency w/ chrome.storage
|
// wrap methods in promises for consistency w/ chrome.storage
|
||||||
get: (key) => {
|
get: (key) => {
|
||||||
const fallback = fallbacks[key];
|
const fallback = fallbacks[key];
|
||||||
key = key.startsWith(namespace) ? key : namespace + key;
|
key = namespaceify(key);
|
||||||
try {
|
try {
|
||||||
const value = JSON.parse(select.get(key)?.value);
|
const value = JSON.parse(select.get(key)?.value);
|
||||||
return Promise.resolve(value ?? fallback);
|
return Promise.resolve(value ?? fallback);
|
||||||
@ -93,13 +95,19 @@ const initDatabase = (namespace, fallbacks = {}) => {
|
|||||||
return Promise.resolve(fallback);
|
return Promise.resolve(fallback);
|
||||||
},
|
},
|
||||||
set: (key, value) => {
|
set: (key, value) => {
|
||||||
key = key.startsWith(namespace) ? key : namespace + key;
|
key = namespaceify(key);
|
||||||
value = JSON.stringify(value);
|
value = JSON.stringify(value);
|
||||||
if (select.get(key) === undefined) {
|
if (select.get(key) === undefined) {
|
||||||
insert.run(key, value);
|
insert.run(key, value);
|
||||||
} else update.run(value, key);
|
} else update.run(value, key);
|
||||||
return Promise.resolve(true);
|
return Promise.resolve(true);
|
||||||
},
|
},
|
||||||
|
remove: (keys) => {
|
||||||
|
keys = Array.isArray(keys) ? keys : [keys];
|
||||||
|
keys = keys.map(namespaceify);
|
||||||
|
removeMany(keys);
|
||||||
|
return Promise.resolve(true);
|
||||||
|
},
|
||||||
export: () => {
|
export: () => {
|
||||||
const entries = dump
|
const entries = dump
|
||||||
.all()
|
.all()
|
||||||
|
@ -51,7 +51,16 @@ const getProfile = async () => {
|
|||||||
enabledMods = initDatabase([await getProfile(), "enabledMods"]);
|
enabledMods = initDatabase([await getProfile(), "enabledMods"]);
|
||||||
return Boolean(await enabledMods.get(id));
|
return Boolean(await enabledMods.get(id));
|
||||||
},
|
},
|
||||||
optionDefaults = async (id) => {
|
setEnabled = async (id, enabled) => {
|
||||||
|
const { initDatabase } = globalThis.__enhancerApi;
|
||||||
|
// prettier-ignore
|
||||||
|
return await initDatabase([
|
||||||
|
await getProfile(),
|
||||||
|
"enabledMods"
|
||||||
|
]).set(id, enabled);
|
||||||
|
};
|
||||||
|
|
||||||
|
const optionDefaults = async (id) => {
|
||||||
const mod = (await getMods()).find((mod) => mod.id === id),
|
const mod = (await getMods()).find((mod) => mod.id === id),
|
||||||
optionEntries = mod.options
|
optionEntries = mod.options
|
||||||
.map((opt) => {
|
.map((opt) => {
|
||||||
@ -64,6 +73,10 @@ const getProfile = async () => {
|
|||||||
})
|
})
|
||||||
.filter((opt) => opt);
|
.filter((opt) => opt);
|
||||||
return Object.fromEntries(optionEntries);
|
return Object.fromEntries(optionEntries);
|
||||||
|
},
|
||||||
|
modDatabase = async (id) => {
|
||||||
|
const { initDatabase } = globalThis.__enhancerApi;
|
||||||
|
return initDatabase([await getProfile(), id], await optionDefaults(id));
|
||||||
};
|
};
|
||||||
|
|
||||||
globalThis.__enhancerApi ??= {};
|
globalThis.__enhancerApi ??= {};
|
||||||
@ -75,5 +88,7 @@ Object.assign(globalThis.__enhancerApi, {
|
|||||||
getIntegrations,
|
getIntegrations,
|
||||||
getProfile,
|
getProfile,
|
||||||
isEnabled,
|
isEnabled,
|
||||||
|
setEnabled,
|
||||||
optionDefaults,
|
optionDefaults,
|
||||||
|
modDatabase,
|
||||||
});
|
});
|
||||||
|
@ -9,7 +9,7 @@ import { setState, useState, getState } from "./state.mjs";
|
|||||||
// generic
|
// generic
|
||||||
|
|
||||||
function _Button(
|
function _Button(
|
||||||
{ type, size, icon, primary, class: cls = "", ...props },
|
{ type, size, variant, icon, class: cls = "", ...props },
|
||||||
...children
|
...children
|
||||||
) {
|
) {
|
||||||
const { html } = globalThis.__enhancerApi,
|
const { html } = globalThis.__enhancerApi,
|
||||||
@ -20,10 +20,14 @@ function _Button(
|
|||||||
return html`<${type}
|
return html`<${type}
|
||||||
class="flex gap-[8px] items-center px-[12px] shrink-0
|
class="flex gap-[8px] items-center px-[12px] shrink-0
|
||||||
rounded-[4px] ${size === "sm" ? "h-[28px]" : "h-[32px]"}
|
rounded-[4px] ${size === "sm" ? "h-[28px]" : "h-[32px]"}
|
||||||
transition duration-[20ms] ${primary
|
transition duration-[20ms] ${variant === "primary"
|
||||||
? `text-[color:var(--theme--accent-primary\\_contrast)]
|
? `text-[color:var(--theme--accent-primary\\_contrast)]
|
||||||
font-medium bg-[color:var(--theme--accent-primary)]
|
font-medium bg-[color:var(--theme--accent-primary)]
|
||||||
hover:bg-[color:var(--theme--accent-primary\\_hover)]`
|
hover:bg-[color:var(--theme--accent-primary\\_hover)]`
|
||||||
|
: variant === "secondary"
|
||||||
|
? `text-[color:var(--theme--accent-secondary)]
|
||||||
|
border-(& [color:var(--theme--accent-secondary)])
|
||||||
|
hover:bg-[color:var(--theme--accent-secondary\\_hover)]`
|
||||||
: `border-(& [color:var(--theme--fg-border)])
|
: `border-(& [color:var(--theme--fg-border)])
|
||||||
hover:bg-[color:var(--theme--bg-hover)]`} ${cls}"
|
hover:bg-[color:var(--theme--bg-hover)]`} ${cls}"
|
||||||
...${props}
|
...${props}
|
||||||
@ -211,6 +215,79 @@ function View({ id }, ...children) {
|
|||||||
return $el;
|
return $el;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function Popup(
|
||||||
|
{ for: $trigger, onopen, onclose, onbeforeclose, ...props },
|
||||||
|
...children
|
||||||
|
) {
|
||||||
|
const { html } = globalThis.__enhancerApi,
|
||||||
|
$popup = html`<div
|
||||||
|
class="notion-enhancer--menu-popup
|
||||||
|
group absolute top-0 left-0 w-full h-full
|
||||||
|
flex flex-col justify-center items-end
|
||||||
|
pointer-events-none z-20"
|
||||||
|
...${props}
|
||||||
|
>
|
||||||
|
<div class="relative right-[100%]">
|
||||||
|
<div
|
||||||
|
class="bg-[color:var(--theme--bg-secondary)]
|
||||||
|
w-[250px] max-w-[calc(100vw-24px)] max-h-[70vh]
|
||||||
|
py-[6px] px-[4px] drop-shadow-xl overflow-y-auto
|
||||||
|
transition duration-[200ms] opacity-0 scale-95 rounded-[4px]
|
||||||
|
group-open:(pointer-events-auto opacity-100 scale-100)"
|
||||||
|
>
|
||||||
|
${children}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>`;
|
||||||
|
|
||||||
|
const { onclick, onkeydown } = $trigger,
|
||||||
|
enableTabbing = () => {
|
||||||
|
$popup
|
||||||
|
.querySelectorAll("[tabindex]")
|
||||||
|
.forEach(($el) => ($el.tabIndex = 0));
|
||||||
|
},
|
||||||
|
disableTabbing = () => {
|
||||||
|
$popup
|
||||||
|
.querySelectorAll("[tabindex]")
|
||||||
|
.forEach(($el) => ($el.tabIndex = -1));
|
||||||
|
},
|
||||||
|
openPopup = () => {
|
||||||
|
$popup.setAttribute("open", true);
|
||||||
|
enableTabbing();
|
||||||
|
onopen?.();
|
||||||
|
setState({ popupOpen: true });
|
||||||
|
},
|
||||||
|
closePopup = () => {
|
||||||
|
$popup.removeAttribute("open");
|
||||||
|
disableTabbing();
|
||||||
|
onbeforeclose?.();
|
||||||
|
setTimeout(() => {
|
||||||
|
onclose?.();
|
||||||
|
setState({ popupOpen: false });
|
||||||
|
}, 200);
|
||||||
|
};
|
||||||
|
disableTabbing();
|
||||||
|
$trigger.onclick = (event) => {
|
||||||
|
onclick?.(event);
|
||||||
|
openPopup();
|
||||||
|
};
|
||||||
|
$trigger.onkeydown = (event) => {
|
||||||
|
onkeydown?.(event);
|
||||||
|
if (event.key === "Enter") openPopup();
|
||||||
|
};
|
||||||
|
useState(["rerender"], () => {
|
||||||
|
if ($popup.hasAttribute("open")) closePopup();
|
||||||
|
});
|
||||||
|
document.addEventListener("click", (event) => {
|
||||||
|
if (!$popup.hasAttribute("open")) return;
|
||||||
|
if ($popup.contains(event.target) || $popup === event.target) return;
|
||||||
|
if ($trigger.contains(event.target) || $trigger === event.target) return;
|
||||||
|
closePopup();
|
||||||
|
});
|
||||||
|
|
||||||
|
return $popup;
|
||||||
|
}
|
||||||
|
|
||||||
// input
|
// input
|
||||||
|
|
||||||
function Input({
|
function Input({
|
||||||
@ -443,66 +520,26 @@ function Select({ values, _get, _set, ...props }) {
|
|||||||
></div>`,
|
></div>`,
|
||||||
$options = values.map((value) => {
|
$options = values.map((value) => {
|
||||||
return html`<${SelectOption} ...${{ value, _get, _set }} />`;
|
return html`<${SelectOption} ...${{ value, _get, _set }} />`;
|
||||||
}),
|
|
||||||
$popup = html`<div
|
|
||||||
class="group absolute top-0 left-0
|
|
||||||
flex flex-col justify-center items-end
|
|
||||||
pointer-events-none w-full h-full"
|
|
||||||
>
|
|
||||||
<div class="relative right-[100%]">
|
|
||||||
<div
|
|
||||||
class="bg-[color:var(--theme--bg-secondary)]
|
|
||||||
w-[250px] max-w-[calc(100vw-24px)] max-h-[70vh]
|
|
||||||
py-[6px] px-[4px] drop-shadow-xl overflow-y-auto
|
|
||||||
transition duration-[200ms] opacity-0 scale-95 rounded-[4px]
|
|
||||||
group-open:(pointer-events-auto opacity-100 scale-100)"
|
|
||||||
>
|
|
||||||
${$options}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>`;
|
|
||||||
|
|
||||||
const { onclick, onkeydown } = $select,
|
|
||||||
openPopup = () => {
|
|
||||||
$popup.setAttribute("open", true);
|
|
||||||
$options.forEach(($opt) => ($opt.tabIndex = 0));
|
|
||||||
setState({ popupOpen: true });
|
|
||||||
},
|
|
||||||
closePopup = (value) => {
|
|
||||||
$popup.removeAttribute("open");
|
|
||||||
$options.forEach(($opt) => ($opt.tabIndex = -1));
|
|
||||||
$select.style.width = `${$select.offsetWidth}px`;
|
|
||||||
$select.style.background = "transparent";
|
|
||||||
if (value) $select.innerText = value;
|
|
||||||
setTimeout(() => {
|
|
||||||
$select.style.width = "";
|
|
||||||
$select.style.background = "";
|
|
||||||
setState({ popupOpen: false });
|
|
||||||
}, 200);
|
|
||||||
};
|
|
||||||
$select.onclick = (event) => {
|
|
||||||
onclick?.(event);
|
|
||||||
openPopup();
|
|
||||||
};
|
|
||||||
$select.onkeydown = (event) => {
|
|
||||||
onkeydown?.(event);
|
|
||||||
if (event.key === "Enter") openPopup();
|
|
||||||
};
|
|
||||||
useState(["rerender"], () => {
|
|
||||||
_get?.().then((value) => {
|
|
||||||
if ($popup.hasAttribute("open")) {
|
|
||||||
closePopup(value);
|
|
||||||
} else $select.innerText = value;
|
|
||||||
});
|
});
|
||||||
});
|
useState(["rerender"], () => {
|
||||||
document.addEventListener("click", (event) => {
|
_get?.().then((value) => ($select.innerText = value));
|
||||||
if (!$popup.hasAttribute("open")) return;
|
|
||||||
if ($popup.contains(event.target) || event.target === $select) return;
|
|
||||||
closePopup();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return html`<div class="notion-enhancer--menu-select relative">
|
return html`<div class="notion-enhancer--menu-select relative">
|
||||||
${$select}${$popup}
|
${$select}
|
||||||
|
<${Popup}
|
||||||
|
for=${$select}
|
||||||
|
onbeforeclose=${() => {
|
||||||
|
$select.style.width = `${$select.offsetWidth}px`;
|
||||||
|
$select.style.background = "transparent";
|
||||||
|
}}
|
||||||
|
onclose=${() => {
|
||||||
|
$select.style.width = "";
|
||||||
|
$select.style.background = "";
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
${$options}
|
||||||
|
<//>
|
||||||
<i
|
<i
|
||||||
class="i-chevron-down pointer-events-none
|
class="i-chevron-down pointer-events-none
|
||||||
absolute right-[6px] top-[6px] w-[16px] h-[16px]
|
absolute right-[6px] top-[6px] w-[16px] h-[16px]
|
||||||
@ -515,6 +552,7 @@ function SelectOption({ value, _get, _set, ...props }) {
|
|||||||
const { html } = globalThis.__enhancerApi,
|
const { html } = globalThis.__enhancerApi,
|
||||||
$selected = html`<i class="ml-auto i-check w-[16px] h-[16px]"></i>`,
|
$selected = html`<i class="ml-auto i-check w-[16px] h-[16px]"></i>`,
|
||||||
$option = html`<div
|
$option = html`<div
|
||||||
|
tabindex="0"
|
||||||
role="button"
|
role="button"
|
||||||
class="select-none cursor-pointer rounded-[3px]
|
class="select-none cursor-pointer rounded-[3px]
|
||||||
flex items-center w-full h-[28px] px-[12px] leading-[1.2]
|
flex items-center w-full h-[28px] px-[12px] leading-[1.2]
|
||||||
@ -608,7 +646,10 @@ function Checkbox({ _get, _set, ...props }) {
|
|||||||
return html`<label tabindex="0" class="cursor-pointer">
|
return html`<label tabindex="0" class="cursor-pointer">
|
||||||
${$input}
|
${$input}
|
||||||
<div class="flex items-center h-[16px] transition duration-[200ms]">
|
<div class="flex items-center h-[16px] transition duration-[200ms]">
|
||||||
<i class="i-check w-[14px] h-[14px]"></i>
|
<i
|
||||||
|
class="i-check w-[14px] h-[14px]
|
||||||
|
text-[color:var(--theme--accent-primary\\_contrast)]"
|
||||||
|
></i>
|
||||||
</div>
|
</div>
|
||||||
</label>`;
|
</label>`;
|
||||||
}
|
}
|
||||||
@ -778,6 +819,7 @@ function Profile({
|
|||||||
setActive,
|
setActive,
|
||||||
exportJson,
|
exportJson,
|
||||||
importJson,
|
importJson,
|
||||||
|
deleteProfile,
|
||||||
...props
|
...props
|
||||||
}) {
|
}) {
|
||||||
const { html } = globalThis.__enhancerApi,
|
const { html } = globalThis.__enhancerApi,
|
||||||
@ -812,6 +854,35 @@ function Profile({
|
|||||||
$a.remove();
|
$a.remove();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const $delete = html`<${Icon} icon="x" />`,
|
||||||
|
$name = html`<mark></mark>`,
|
||||||
|
$confirmation = html`<${Popup}
|
||||||
|
for=${$delete}
|
||||||
|
onopen=${async () => ($name.innerText = await getName())}
|
||||||
|
>
|
||||||
|
<p class="text-[14px] pt-[2px] px-[8px]">
|
||||||
|
Are you sure you want to delete the profile ${$name} permanently?
|
||||||
|
</p>
|
||||||
|
<div class="flex flex-col gap-[8px] pt-[8px] pb-[6px] px-[8px]">
|
||||||
|
<${Button}
|
||||||
|
tabindex="0"
|
||||||
|
icon="trash"
|
||||||
|
class="justify-center"
|
||||||
|
variant="secondary"
|
||||||
|
onclick=${() => deleteProfile()}
|
||||||
|
>
|
||||||
|
Delete
|
||||||
|
<//>
|
||||||
|
<${Button}
|
||||||
|
tabindex="0"
|
||||||
|
class="justify-center"
|
||||||
|
onclick=${() => setState({ rerender: true })}
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
<//>
|
||||||
|
</div>
|
||||||
|
<//>`;
|
||||||
|
|
||||||
return html`<li class="flex items-center my-[14px] gap-[8px]" ...${props}>
|
return html`<li class="flex items-center my-[14px] gap-[8px]" ...${props}>
|
||||||
<${Checkbox}
|
<${Checkbox}
|
||||||
...${{ _get: isActive, _set: setActive }}
|
...${{ _get: isActive, _set: setActive }}
|
||||||
@ -834,8 +905,8 @@ function Profile({
|
|||||||
/>
|
/>
|
||||||
Import
|
Import
|
||||||
<//>
|
<//>
|
||||||
<${Button} size="sm" icon="upload" onclick=${downloadProfile}> Export <//>
|
<${Button} size="sm" icon="upload" onclick=${downloadProfile}>Export<//>
|
||||||
<${Icon} icon="x" />
|
<div class="relative">${$delete}${$confirmation}</div>
|
||||||
</li>`;
|
</li>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -94,7 +94,7 @@ body > #skeleton .row-group .shimmer {
|
|||||||
height: 11px;
|
height: 11px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.notion-enhancer--menu-description mark {
|
mark {
|
||||||
color: inherit;
|
color: inherit;
|
||||||
padding: 2px 4px;
|
padding: 2px 4px;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
|
@ -4,13 +4,7 @@
|
|||||||
* (https://notion-enhancer.github.io/) under the MIT license
|
* (https://notion-enhancer.github.io/) under the MIT license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {
|
import { getState, setState, useState } from "./state.mjs";
|
||||||
getState,
|
|
||||||
setState,
|
|
||||||
useState,
|
|
||||||
setEnabled,
|
|
||||||
modDatabase,
|
|
||||||
} from "./state.mjs";
|
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
Description,
|
Description,
|
||||||
@ -72,7 +66,7 @@ const renderSidebar = (items, categories) => {
|
|||||||
return $sidebar;
|
return $sidebar;
|
||||||
},
|
},
|
||||||
renderList = async (id, mods, description) => {
|
renderList = async (id, mods, description) => {
|
||||||
const { html, isEnabled } = globalThis.__enhancerApi;
|
const { html, isEnabled, setEnabled } = globalThis.__enhancerApi;
|
||||||
mods = mods.map(async (mod) => {
|
mods = mods.map(async (mod) => {
|
||||||
const _get = () => isEnabled(mod.id),
|
const _get = () => isEnabled(mod.id),
|
||||||
_set = async (enabled) => {
|
_set = async (enabled) => {
|
||||||
@ -86,7 +80,7 @@ const renderSidebar = (items, categories) => {
|
|||||||
<//>`;
|
<//>`;
|
||||||
},
|
},
|
||||||
renderOptions = async (mod) => {
|
renderOptions = async (mod) => {
|
||||||
const { html, platform, getProfile } = globalThis.__enhancerApi;
|
const { html, platform, modDatabase } = globalThis.__enhancerApi;
|
||||||
let options = mod.options.reduce((options, opt) => {
|
let options = mod.options.reduce((options, opt) => {
|
||||||
if (!opt.key && (opt.type !== "heading" || !opt.label)) return options;
|
if (!opt.key && (opt.type !== "heading" || !opt.label)) return options;
|
||||||
if (opt.platforms && !opt.platforms.includes(platform)) return options;
|
if (opt.platforms && !opt.platforms.includes(platform)) return options;
|
||||||
@ -116,14 +110,26 @@ const renderSidebar = (items, categories) => {
|
|||||||
db = initDatabase(),
|
db = initDatabase(),
|
||||||
$list = html`<ul></ul>`,
|
$list = html`<ul></ul>`,
|
||||||
renderProfile = (id) => {
|
renderProfile = (id) => {
|
||||||
const profile = initDatabase([id]);
|
const profile = initDatabase([id]),
|
||||||
|
isActive = async () => id === (await getProfile()),
|
||||||
|
deleteProfile = async () => {
|
||||||
|
const keys = Object.keys(await profile.export());
|
||||||
|
profileIds.splice(profileIds.indexOf(id), 1);
|
||||||
|
await db.set("profileIds", profileIds);
|
||||||
|
await profile.remove(keys);
|
||||||
|
if (isActive()) {
|
||||||
|
await db.remove("activeProfile");
|
||||||
|
setState({ databaseUpdated: true });
|
||||||
|
}
|
||||||
|
setState({ rerender: true });
|
||||||
|
};
|
||||||
return html`<${Profile}
|
return html`<${Profile}
|
||||||
id=${id}
|
id=${id}
|
||||||
getName=${async () =>
|
getName=${async () =>
|
||||||
(await profile.get("profileName")) ??
|
(await profile.get("profileName")) ??
|
||||||
(id === "default" ? "default" : "")}
|
(id === "default" ? "default" : "")}
|
||||||
setName=${(name) => profile.set("profileName", name)}
|
setName=${(name) => profile.set("profileName", name)}
|
||||||
isActive=${async () => id === (await getProfile())}
|
isActive=${isActive}
|
||||||
setActive=${async () => {
|
setActive=${async () => {
|
||||||
await db.set("activeProfile", id);
|
await db.set("activeProfile", id);
|
||||||
setState({ rerender: true, databaseUpdated: true });
|
setState({ rerender: true, databaseUpdated: true });
|
||||||
@ -133,10 +139,12 @@ const renderSidebar = (items, categories) => {
|
|||||||
try {
|
try {
|
||||||
await profile.import(JSON.parse(json));
|
await profile.import(JSON.parse(json));
|
||||||
setState({ rerender: true, databaseUpdated: true });
|
setState({ rerender: true, databaseUpdated: true });
|
||||||
|
// success
|
||||||
} catch {
|
} catch {
|
||||||
// error
|
// error
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
|
deleteProfile=${deleteProfile}
|
||||||
/>`;
|
/>`;
|
||||||
},
|
},
|
||||||
refreshProfiles = async () => {
|
refreshProfiles = async () => {
|
||||||
@ -164,9 +172,6 @@ const renderSidebar = (items, categories) => {
|
|||||||
};
|
};
|
||||||
useState(["rerender"], () => refreshProfiles());
|
useState(["rerender"], () => refreshProfiles());
|
||||||
|
|
||||||
// todo: deleting profiles inc. clearing db keys,
|
|
||||||
// throwing errors on invalid json upload
|
|
||||||
|
|
||||||
const $input = html`<${Input}
|
const $input = html`<${Input}
|
||||||
size="md"
|
size="md"
|
||||||
type="text"
|
type="text"
|
||||||
@ -198,7 +203,7 @@ const renderSidebar = (items, categories) => {
|
|||||||
</div>`;
|
</div>`;
|
||||||
},
|
},
|
||||||
renderMods = async (mods) => {
|
renderMods = async (mods) => {
|
||||||
const { html, isEnabled } = globalThis.__enhancerApi;
|
const { html, isEnabled, setEnabled } = globalThis.__enhancerApi;
|
||||||
mods = mods
|
mods = mods
|
||||||
.filter((mod) => {
|
.filter((mod) => {
|
||||||
return mod.options?.filter((opt) => opt.type !== "heading").length;
|
return mod.options?.filter((opt) => opt.type !== "heading").length;
|
||||||
@ -325,8 +330,8 @@ const render = async () => {
|
|||||||
<//>`;
|
<//>`;
|
||||||
});
|
});
|
||||||
const $reload = html`<${Button}
|
const $reload = html`<${Button}
|
||||||
primary
|
|
||||||
class="ml-auto"
|
class="ml-auto"
|
||||||
|
variant="primary"
|
||||||
icon="refresh-cw"
|
icon="refresh-cw"
|
||||||
onclick=${() => reloadApp()}
|
onclick=${() => reloadApp()}
|
||||||
style="display: none"
|
style="display: none"
|
||||||
|
@ -21,18 +21,4 @@ const _state = {},
|
|||||||
callback(getState(keys));
|
callback(getState(keys));
|
||||||
};
|
};
|
||||||
|
|
||||||
const setEnabled = async (id, enabled) => {
|
export { setState, useState, getState };
|
||||||
const { getProfile, initDatabase } = globalThis.__enhancerApi;
|
|
||||||
// prettier-ignore
|
|
||||||
return await initDatabase([
|
|
||||||
await getProfile(),
|
|
||||||
"enabledMods"
|
|
||||||
]).set(id, enabled);
|
|
||||||
},
|
|
||||||
modDatabase = async (id) => {
|
|
||||||
const { getProfile, initDatabase } = globalThis.__enhancerApi,
|
|
||||||
{ optionDefaults } = globalThis.__enhancerApi;
|
|
||||||
return initDatabase([await getProfile(), id], await optionDefaults(id));
|
|
||||||
};
|
|
||||||
|
|
||||||
export { setState, useState, getState, setEnabled, modDatabase };
|
|
||||||
|
2558
src/core/theme.css
2558
src/core/theme.css
File diff suppressed because one or more lines are too long
@ -57,8 +57,8 @@ body.dark {
|
|||||||
--theme--accent-primary_contrast: #fff;
|
--theme--accent-primary_contrast: #fff;
|
||||||
--theme--accent-primary_transparent: rgba(35, 131, 226, 0.14);
|
--theme--accent-primary_transparent: rgba(35, 131, 226, 0.14);
|
||||||
--theme--accent-secondary: #eb5757;
|
--theme--accent-secondary: #eb5757;
|
||||||
|
--theme--accent-secondary_hover: rgba(235, 87, 87, 0.1);
|
||||||
--theme--accent-secondary_contrast: #fff;
|
--theme--accent-secondary_contrast: #fff;
|
||||||
--theme--accent-secondary_transparent: rgba(235, 87, 87, 0.1);
|
|
||||||
|
|
||||||
--theme--scrollbar-track: rgba(202, 204, 206, 0.04);
|
--theme--scrollbar-track: rgba(202, 204, 206, 0.04);
|
||||||
--theme--scrollbar-thumb: #474c50;
|
--theme--scrollbar-thumb: #474c50;
|
||||||
@ -154,7 +154,7 @@ body:not(.dark) {
|
|||||||
--theme--accent-primary_transparent: rgba(35, 131, 226, 0.14);
|
--theme--accent-primary_transparent: rgba(35, 131, 226, 0.14);
|
||||||
--theme--accent-secondary: #eb5757;
|
--theme--accent-secondary: #eb5757;
|
||||||
--theme--accent-secondary_contrast: #fff;
|
--theme--accent-secondary_contrast: #fff;
|
||||||
--theme--accent-secondary_transparent: rgba(235, 87, 87, 0.1);
|
--theme--accent-secondary_hover: rgba(235, 87, 87, 0.1);
|
||||||
|
|
||||||
--theme--scrollbar-track: #edece9;
|
--theme--scrollbar-track: #edece9;
|
||||||
--theme--scrollbar-thumb: #d3d1cb;
|
--theme--scrollbar-thumb: #d3d1cb;
|
||||||
|
@ -48,8 +48,8 @@ body.dark {
|
|||||||
--theme--accent-primary_contrast: #fff;
|
--theme--accent-primary_contrast: #fff;
|
||||||
--theme--accent-primary_transparent: rgb(46, 170, 220, 0.25);
|
--theme--accent-primary_transparent: rgb(46, 170, 220, 0.25);
|
||||||
--theme--accent-secondary: #eb5757;
|
--theme--accent-secondary: #eb5757;
|
||||||
|
--theme--accent-secondary_hover: rgba(235, 87, 87, 0.1);
|
||||||
--theme--accent-secondary_contrast: #fff;
|
--theme--accent-secondary_contrast: #fff;
|
||||||
--theme--accent-secondary_transparent: rgba(235, 87, 87, 0.1);
|
|
||||||
|
|
||||||
--theme--scrollbar-track: rgba(202, 204, 206, 0.04);
|
--theme--scrollbar-track: rgba(202, 204, 206, 0.04);
|
||||||
--theme--scrollbar-thumb: #474c50;
|
--theme--scrollbar-thumb: #474c50;
|
||||||
|
2
src/vendor/coloris.min.css
vendored
2
src/vendor/coloris.min.css
vendored
File diff suppressed because one or more lines are too long
2
src/vendor/coloris.min.js
vendored
2
src/vendor/coloris.min.js
vendored
File diff suppressed because one or more lines are too long
2
src/vendor/lucide.min.js
vendored
2
src/vendor/lucide.min.js
vendored
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue
Block a user