diff --git a/bookmarks/frontend/components/dev-tool.js b/bookmarks/frontend/components/dev-tool.js new file mode 100644 index 0000000..3e51509 --- /dev/null +++ b/bookmarks/frontend/components/dev-tool.js @@ -0,0 +1,257 @@ +import { LitElement, html, css } from "lit"; + +class DevTool extends LitElement { + static properties = { + profile: { type: Object, state: true }, + formAction: { type: String, attribute: "data-form-action" }, + csrfToken: { type: String, attribute: "data-csrf-token" }, + isOpen: { type: Boolean, state: true }, + }; + + static styles = css` + :host { + position: fixed; + bottom: 1rem; + right: 1rem; + z-index: 10000; + } + + .button { + background: var(--btn-primary-bg-color); + color: var(--btn-primary-text-color); + border: none; + padding: var(--unit-2); + border-radius: var(--border-radius); + box-shadow: var(--btn-box-shadow); + cursor: pointer; + height: auto; + line-height: 0; + } + + .overlay { + display: none; + position: absolute; + bottom: 100%; + right: 0; + background: var(--body-color); + color: var(--text-color); + border: 1px solid var(--border-color); + border-radius: var(--border-radius); + padding: var(--unit-2); + margin-bottom: var(--unit-2); + min-width: 220px; + box-shadow: var(--box-shadow-lg); + font-size: var(--font-size-sm); + } + + :host([open]) .overlay { + display: block; + } + + h3 { + margin: 0 0 var(--unit-2) 0; + } + + label { + display: flex; + align-items: center; + gap: var(--unit-1); + cursor: pointer; + } + + label:has(select) { + margin-bottom: var(--unit-1); + } + + label:has(select) span { + min-width: 100px; + } + + hr { + margin: var(--unit-2) 0; + border: none; + border-top: 1px solid var(--border-color); + } + `; + + static fields = [ + { + type: "select", + key: "theme", + label: "Theme", + options: [ + { value: "auto", label: "Auto" }, + { value: "light", label: "Light" }, + { value: "dark", label: "Dark" }, + ], + }, + { + type: "select", + key: "bookmark_date_display", + label: "Date", + options: [ + { value: "relative", label: "Relative" }, + { value: "absolute", label: "Absolute" }, + { value: "hidden", label: "Hidden" }, + ], + }, + { + type: "select", + key: "bookmark_description_display", + label: "Description", + options: [ + { value: "inline", label: "Inline" }, + { value: "separate", label: "Separate" }, + ], + }, + { type: "checkbox", key: "enable_favicons", label: "Favicons" }, + { type: "checkbox", key: "enable_preview_images", label: "Preview images" }, + { type: "checkbox", key: "display_url", label: "Display URL" }, + { type: "checkbox", key: "permanent_notes", label: "Permanent notes" }, + { type: "checkbox", key: "collapse_side_panel", label: "Collapse sidebar" }, + { type: "checkbox", key: "sticky_pagination", label: "Sticky pagination" }, + { type: "checkbox", key: "hide_bundles", label: "Hide bundles" }, + ]; + + constructor() { + super(); + this.isOpen = false; + this.profile = {}; + this._onOutsideClick = this._onOutsideClick.bind(this); + } + + connectedCallback() { + super.connectedCallback(); + const profileData = document.getElementById("json_profile"); + this.profile = JSON.parse(profileData.textContent || "{}"); + document.addEventListener("click", this._onOutsideClick); + } + + disconnectedCallback() { + super.disconnectedCallback(); + document.removeEventListener("click", this._onOutsideClick); + } + + _onOutsideClick(e) { + if (!this.contains(e.target) && this.isOpen) { + this.isOpen = false; + this.removeAttribute("open"); + } + } + + _toggle() { + this.isOpen = !this.isOpen; + if (this.isOpen) { + this.setAttribute("open", ""); + } else { + this.removeAttribute("open"); + } + } + + _handleChange(key, value) { + this.profile = { ...this.profile, [key]: value }; + if (key === "theme") { + const themeLinks = document.head.querySelectorAll('link[href*="theme"]'); + themeLinks.forEach((link) => link.remove()); + } + this._submitForm(); + } + + _renderField(field) { + switch (field.type) { + case "checkbox": + return html` + + `; + case "select": + return html` + + `; + case "divider": + return html`