diff --git a/frontend/index.d.ts b/frontend/index.d.ts index a74ed0cd5e..90231e4355 100644 --- a/frontend/index.d.ts +++ b/frontend/index.d.ts @@ -1,3 +1,4 @@ declare module "*.svg"; declare module "*.webp"; +declare module "*.css"; declare module "regex-colorize"; diff --git a/frontend/package.json b/frontend/package.json index 91d6a42f67..1fb885a7a0 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -46,8 +46,11 @@ "lodash": "^4.17.21", "micromark": "^3.1.0", "node-fetch": "^3.1.0", + "patch-package": "^8.0.0", "postcss": "^8.4.5", + "postcss-lit": "^1.1.1", "postcss-loader": "^6.1.1", + "postinstall-postinstall": "^2.1.0", "prettier": "^2.4.1", "pretty-ms": "^7.0.1", "query-string": "^8.1.0", @@ -90,8 +93,6 @@ "@web/dev-server-rollup": "^0.3.21", "husky": "^8.0.3", "lint-staged": "^13.1.0", - "patch-package": "^8.0.0", - "postinstall-postinstall": "^2.1.0", "rollup-plugin-typescript-paths": "^1.4.0", "sinon": "^12.0.1", "ts-lit-plugin": "^2.0.1", diff --git a/frontend/postcss.config.js b/frontend/postcss.config.js index 116848f66a..c5339384f0 100644 --- a/frontend/postcss.config.js +++ b/frontend/postcss.config.js @@ -1,3 +1,4 @@ +/** @type {import('postcss-load-config').Config} */ module.exports = { plugins: [require("tailwindcss"), require("autoprefixer")], }; diff --git a/frontend/src/__mocks__/css.js b/frontend/src/__mocks__/css.js index f88ea095a5..38ac58d911 100644 --- a/frontend/src/__mocks__/css.js +++ b/frontend/src/__mocks__/css.js @@ -8,3 +8,4 @@ * }, * }, */ +export default ""; diff --git a/frontend/src/classes/TailwindElement.ts b/frontend/src/classes/TailwindElement.ts new file mode 100644 index 0000000000..3ca265665c --- /dev/null +++ b/frontend/src/classes/TailwindElement.ts @@ -0,0 +1,18 @@ +import { LitElement } from "lit"; +import { theme } from "@/theme"; + +export class TailwindElement extends LitElement { + connectedCallback(): void { + super.connectedCallback(); + // Insert the compiled Tailwind css into the shadow root. + // This has the benefit of not requiring a whole copy of compiled Tailwind + // for every TailwindElement, so we still get the benefits of atomic CSS. + // And because Tailwind uses `@layer`[^1], the order of declarations ends up + // correct, and you can use component styles with `static styles = ...`, + // *and* you can use Tailwind functions and directives in those styles + // thanks to `postcss-lit`. + // + // [^1]: (see https://tailwindcss.com/docs/adding-custom-styles#using-css-and-layer), + this.shadowRoot?.adoptedStyleSheets.push(theme); + } +} diff --git a/frontend/src/components/screencast.ts b/frontend/src/components/screencast.ts index 403241f3c5..a72dfd926f 100644 --- a/frontend/src/components/screencast.ts +++ b/frontend/src/components/screencast.ts @@ -42,6 +42,7 @@ export class Screencast extends LitElement { }/watch`; static maxRetries = 10; + // postcss-lit-disable-next-line static styles = css` .wrapper { position: relative; diff --git a/frontend/src/components/ui/alert.ts b/frontend/src/components/ui/alert.ts index ced051a82a..341a83fbbb 100644 --- a/frontend/src/components/ui/alert.ts +++ b/frontend/src/components/ui/alert.ts @@ -15,6 +15,7 @@ export class Alert extends LitElement { @property({ type: String }) variant: "success" | "warning" | "danger" | "info" = "info"; + // postcss-lit-disable-next-line static styles = css` :host > div { padding: var(--sl-spacing-x-small) var(--sl-spacing-small); diff --git a/frontend/src/components/ui/badge.ts b/frontend/src/components/ui/badge.ts index e4533fbbcb..f3b5831f85 100644 --- a/frontend/src/components/ui/badge.ts +++ b/frontend/src/components/ui/badge.ts @@ -14,6 +14,7 @@ export class Badge extends LitElement { @property({ type: String }) variant: "success" | "warning" | "danger" | "neutral" = "neutral"; + // postcss-lit-disable-next-line static styles = css` :host > span { display: inline-flex; diff --git a/frontend/src/components/ui/button.ts b/frontend/src/components/ui/button.ts index 67ee122087..890d2425c9 100644 --- a/frontend/src/components/ui/button.ts +++ b/frontend/src/components/ui/button.ts @@ -40,6 +40,7 @@ export class Button extends LitElement { @property({ type: Boolean }) icon: boolean = false; + // postcss-lit-disable-next-line static styles = css` :host { display: inline-block; diff --git a/frontend/src/components/ui/code.ts b/frontend/src/components/ui/code.ts index 07fc1d75f9..02aad91338 100644 --- a/frontend/src/components/ui/code.ts +++ b/frontend/src/components/ui/code.ts @@ -4,35 +4,26 @@ import { html as staticHtml, unsafeStatic } from "lit/static-html.js"; import hljs from "highlight.js/lib/core"; import javascript from "highlight.js/lib/languages/javascript"; import xml from "highlight.js/lib/languages/xml"; +import { TailwindElement } from "@/classes/TailwindElement"; /** * Syntax highlighting for javascript and HTML (XML) */ @customElement("btrix-code") -export class Code extends LitElement { - static styles = [ - css` - pre { - white-space: pre-wrap; - font-family: var(--sl-font-mono); - font-size: 0.9em; - color: #24292e; - margin: 0; - } +export class Code extends TailwindElement { + static styles = css` + .hljs-name { + color: #22863a; + } - .hljs-name { - color: #22863a; - } + .hljs-attr { + color: #6f42c1; + } - .hljs-attr { - color: #6f42c1; - } - - .hljs-string { - color: #032f62; - } - `, - ]; + .hljs-string { + color: #032f62; + } + `; @property({ type: String }) value: string = ""; @@ -50,8 +41,8 @@ export class Code extends LitElement { const htmlStr = hljs.highlight(this.value, { language: this.language, }).value; - return html`
${staticHtml`${unsafeStatic(
-      htmlStr
-    )}`}
`; + return html`
${staticHtml`${unsafeStatic(htmlStr)}`}
`; } } diff --git a/frontend/src/components/ui/copy-button.ts b/frontend/src/components/ui/copy-button.ts index b99549105c..f09b41089a 100644 --- a/frontend/src/components/ui/copy-button.ts +++ b/frontend/src/components/ui/copy-button.ts @@ -31,6 +31,9 @@ export class CopyButton extends LitElement { @property({ attribute: false }) getValue?: () => string | undefined; + @property({ type: Boolean }) + hoist = false; + @state() private isCopied: boolean = false; @@ -53,6 +56,7 @@ export class CopyButton extends LitElement { : this.content ? this.content : msg("Copy")} + ?hoist=${this.hoist} > + * ``` + */ +@localized() +@customElement("btrix-copy-field") +export class CopyField extends TailwindElement { + @property({ type: String }) + value?: string; + + @property({ type: Boolean }) + hideContentFromScreenReaders = false; + + @property({ type: String }) + buttonIconName?: string; + + @property({ type: String }) + buttonContent?: string; + + @property({ attribute: false }) + getValue?: () => string | undefined; + + @property({ type: Boolean }) + hoist = false; + + @property({ type: Boolean }) + monostyle = true; + + @property({ type: Boolean }) + filled = this.monostyle; + + @property() + label?: string; + + static styles = css` + :host { + display: block; + } + `; + + get _slottedChildren() { + const slot = this.shadowRoot?.querySelector("slot[name=label]"); + return (slot as HTMLSlotElement | null | undefined)?.assignedElements(); + } + + render() { + return html` +
+ +
+ + + ${this.value} + + +
+
+ `; + } +} diff --git a/frontend/src/components/ui/data-table.ts b/frontend/src/components/ui/data-table.ts index bd413f2e0c..7a3ff06606 100644 --- a/frontend/src/components/ui/data-table.ts +++ b/frontend/src/components/ui/data-table.ts @@ -22,6 +22,7 @@ type CellContent = string | TemplateResult<1>; */ @customElement("btrix-data-table") export class DataTable extends LitElement { + // postcss-lit-disable-next-line static styles = css` :host { display: contents; diff --git a/frontend/src/components/ui/desc-list.ts b/frontend/src/components/ui/desc-list.ts index 8e5ae657f0..0dacb1c7cd 100644 --- a/frontend/src/components/ui/desc-list.ts +++ b/frontend/src/components/ui/desc-list.ts @@ -20,6 +20,7 @@ import { classMap } from "lit/directives/class-map.js"; */ @customElement("btrix-desc-list-item") export class DescListItem extends LitElement { + // postcss-lit-disable-next-line static styles = css` :host { display: contents; @@ -68,6 +69,7 @@ export class DescListItem extends LitElement { @customElement("btrix-desc-list") export class DescList extends LitElement { + // postcss-lit-disable-next-line static styles = css` dl { display: grid; diff --git a/frontend/src/components/ui/details.ts b/frontend/src/components/ui/details.ts index b50eb64b1a..d17760f0f4 100644 --- a/frontend/src/components/ui/details.ts +++ b/frontend/src/components/ui/details.ts @@ -25,6 +25,7 @@ export class Details extends LitElement { @property({ type: Boolean }) disabled? = false; + // postcss-lit-disable-next-line static styles = css` :host { display: block; diff --git a/frontend/src/components/ui/index.ts b/frontend/src/components/ui/index.ts index 5b42d477cb..0a7bee00e7 100644 --- a/frontend/src/components/ui/index.ts +++ b/frontend/src/components/ui/index.ts @@ -6,6 +6,7 @@ import("./code"); import("./combobox"); import("./config-details"); import("./copy-button"); +import("./copy-field"); import("./data-table"); import("./desc-list"); import("./details"); diff --git a/frontend/src/components/ui/language-select.ts b/frontend/src/components/ui/language-select.ts index c4d30689cc..e033597325 100644 --- a/frontend/src/components/ui/language-select.ts +++ b/frontend/src/components/ui/language-select.ts @@ -31,6 +31,7 @@ const languages = sortBy("name")( @customElement("btrix-language-select") @localized() export class LanguageSelect extends LitElement { + // postcss-lit-disable-next-line static styles = css` sl-select::part(control) { box-shadow: var(--sl-shadow-small); diff --git a/frontend/src/components/ui/meter.ts b/frontend/src/components/ui/meter.ts index b0567e5b02..4877b87d14 100644 --- a/frontend/src/components/ui/meter.ts +++ b/frontend/src/components/ui/meter.ts @@ -6,6 +6,7 @@ import { queryAssignedElements, customElement, } from "lit/decorators.js"; +import { classMap } from "lit/directives/class-map.js"; import { ifDefined } from "lit/directives/if-defined.js"; import { when } from "lit/directives/when.js"; import debounce from "lodash/fp/debounce"; @@ -16,6 +17,7 @@ export class MeterBar extends LitElement { @property({ type: Number }) value = 0; + // postcss-lit-disable-next-line static styles = css` :host { display: contents; @@ -78,9 +80,9 @@ export class DividedMeterBar extends LitElement {
${when(this.value, () => { return html`
`; })} @@ -123,6 +125,7 @@ export class Meter extends LitElement { @query(".maxText") private maxText?: HTMLElement; + // postcss-lit-disable-next-line static styles = css` .meter { position: relative; diff --git a/frontend/src/components/ui/numbered-list.ts b/frontend/src/components/ui/numbered-list.ts index ba624944d1..2d048b443d 100644 --- a/frontend/src/components/ui/numbered-list.ts +++ b/frontend/src/components/ui/numbered-list.ts @@ -35,6 +35,7 @@ export class NumberedListItem extends LitElement { @property({ type: Boolean }) hoverable: boolean = false; + // postcss-lit-disable-next-line static styles = css` :host, .item { @@ -111,6 +112,7 @@ export class NumberedListItem extends LitElement { @customElement("btrix-numbered-list-header") export class NumberedListHeader extends LitElement { + // postcss-lit-disable-next-line static styles = css` :host, header { @@ -135,6 +137,7 @@ export class NumberedListHeader extends LitElement { @customElement("btrix-numbered-list") export class NumberedList extends LitElement { + // postcss-lit-disable-next-line static styles = css` :host { display: block; diff --git a/frontend/src/components/ui/pw-strength-alert.ts b/frontend/src/components/ui/pw-strength-alert.ts index 255ffb9ef2..45932996d8 100644 --- a/frontend/src/components/ui/pw-strength-alert.ts +++ b/frontend/src/components/ui/pw-strength-alert.ts @@ -26,6 +26,7 @@ export class PasswordStrengthAlert extends LitElement { @property({ type: Number }) optimal: Score = 4; + // postcss-lit-disable-next-line static styles = css` sl-alert::part(message) { /* Decrease padding size: */ diff --git a/frontend/src/components/ui/section-heading.ts b/frontend/src/components/ui/section-heading.ts index 0ce6fc769f..110698dd9d 100644 --- a/frontend/src/components/ui/section-heading.ts +++ b/frontend/src/components/ui/section-heading.ts @@ -11,6 +11,7 @@ import { customElement } from "lit/decorators.js"; */ @customElement("btrix-section-heading") export class SectionHeading extends LitElement { + // postcss-lit-disable-next-line static styles = css` .heading { display: flex; diff --git a/frontend/src/components/ui/tab-list.ts b/frontend/src/components/ui/tab-list.ts index c066f97308..65773fb71f 100644 --- a/frontend/src/components/ui/tab-list.ts +++ b/frontend/src/components/ui/tab-list.ts @@ -1,3 +1,4 @@ +import { TailwindElement } from "@/classes/TailwindElement"; import { LitElement, html, css } from "lit"; import { property, queryAsync, customElement } from "lit/decorators.js"; import { ifDefined } from "lit/directives/if-defined.js"; @@ -23,24 +24,7 @@ const SCREEN_LG = 896; */ @customElement("btrix-tab-panel") -export class TabPanel extends LitElement { - static styles = css` - :host { - display: flex; - min-height: 100%; - } - - .panel[aria-hidden="false"] { - flex: 1; - } - - .panel[aria-hidden="true"] { - display: none; - height: 0; - width: 0; - } - `; - +export class TabPanel extends TailwindElement { @property({ type: String }) name?: string; @@ -50,7 +34,7 @@ export class TabPanel extends LitElement { render() { return html`
; @localized() @customElement("btrix-new-workflow-dialog") export class NewWorkflowDialog extends LitElement { + // postcss-lit-disable-next-line static styles = css` .title, .container { diff --git a/frontend/src/features/crawl-workflows/workflow-list.ts b/frontend/src/features/crawl-workflows/workflow-list.ts index 8b5f14d213..c859c4c7c3 100644 --- a/frontend/src/features/crawl-workflows/workflow-list.ts +++ b/frontend/src/features/crawl-workflows/workflow-list.ts @@ -29,8 +29,11 @@ import { humanizeSchedule } from "@/utils/cron"; import { numberFormatter } from "@/utils/number"; import type { OverflowDropdown } from "@/components/ui/overflow-dropdown"; +// postcss-lit-disable-next-line const mediumBreakpointCss = css`30rem`; +// postcss-lit-disable-next-line const largeBreakpointCss = css`60rem`; +// postcss-lit-disable-next-line const rowCss = css` .row { display: grid; diff --git a/frontend/src/index.ts b/frontend/src/index.ts index 063ccac21c..7e0b047726 100644 --- a/frontend/src/index.ts +++ b/frontend/src/index.ts @@ -6,7 +6,6 @@ import { msg, localized } from "@lit/localize"; import { ifDefined } from "lit/directives/if-defined.js"; import type { SlDialog, SlInput } from "@shoelace-style/shoelace"; import "broadcastchannel-polyfill"; -import "tailwindcss/tailwind.css"; import "./utils/polyfills"; import appState, { use, AppStateService } from "./utils/state"; @@ -24,7 +23,6 @@ import type { import type { ViewState } from "./utils/APIRouter"; import type { CurrentUser, UserOrg } from "./types/user"; import type { AuthStorageEventDetail } from "./utils/AuthService"; -import theme from "./theme"; import { ROUTES } from "./routes"; import "./shoelace"; import "./components"; @@ -33,6 +31,10 @@ import "./pages"; import "./assets/fonts/Inter/inter.css"; import "./assets/fonts/Recursive/recursive.css"; import "./styles.css"; +import { theme } from "@/theme"; + +// Make theme CSS available in document +document.adoptedStyleSheets = [theme]; type DialogContent = { label?: TemplateResult | string; @@ -219,10 +221,6 @@ export class App extends LiteElement { render() { return html` - -
${this.renderNavBar()}
${this.renderPage()}
diff --git a/frontend/src/pages/org/collection-detail.ts b/frontend/src/pages/org/collection-detail.ts index e9e07e05dd..33307812c4 100644 --- a/frontend/src/pages/org/collection-detail.ts +++ b/frontend/src/pages/org/collection-detail.ts @@ -234,25 +234,21 @@ export class CollectionDetail extends LiteElement {

${msg("This collection can be viewed by anyone with the link.")}

-
-
- - - - -
-
${publicReplayUrl}
-
- publicReplayUrl} - content=${msg("Copy Public URL")} - > -
-
+ + + + + + + ${msg("Embed Collection")}
diff --git a/frontend/src/pages/org/crawl-detail.ts b/frontend/src/pages/org/crawl-detail.ts index 95d7ce64a1..bddd12278d 100644 --- a/frontend/src/pages/org/crawl-detail.ts +++ b/frontend/src/pages/org/crawl-detail.ts @@ -218,7 +218,7 @@ export class CrawlDetail extends LiteElement { break; } - let label = ""; + let label = "Back"; if (this.workflowId) { label = msg("Back to Crawl Workflow"); } else if (this.collectionId) { @@ -269,15 +269,12 @@ export class CrawlDetail extends LiteElement { private renderName() { if (!this.crawl) - return html``; + return html``; if (this.crawl.name) return this.crawl.name; if (!this.crawl.firstSeed) return this.crawl.id; const remainder = this.crawl.seedCount - 1; - let crawlName: any = html`${this.crawl.firstSeed}`; if (remainder) { @@ -314,7 +311,7 @@ export class CrawlDetail extends LiteElement {
`; @@ -560,7 +561,7 @@ export class CrawlDetail extends LiteElement { ?isUpload=${this.crawl.type === "upload"} > ` - : html``} + : html``} ${when(this.crawl, () => this.crawl!.type === "upload" @@ -665,19 +666,14 @@ export class CrawlDetail extends LiteElement { pages` : ""}` : html`${msg("Unknown")}`}` - : html``} + : html``} ${this.crawl - ? html` -
- ${this.crawl.id} - -
- ` - : html``} + ? html`` + : html``}
`; @@ -700,7 +696,7 @@ ${this.crawl?.description} >`, () => noneText ), - () => html`` + () => html`` )} @@ -716,7 +712,7 @@ ${this.crawl?.description} ), () => noneText ), - () => html`` + () => html`` )} @@ -742,7 +738,7 @@ ${this.crawl?.description} `, () => noneText ), - () => html`` + () => html`` )} diff --git a/frontend/src/pages/org/crawls-list.ts b/frontend/src/pages/org/crawls-list.ts index 4ba0e2a41d..1d72ad1f5d 100644 --- a/frontend/src/pages/org/crawls-list.ts +++ b/frontend/src/pages/org/crawls-list.ts @@ -203,6 +203,7 @@ export class CrawlsList extends LiteElement { > (this.isUploadingArchive = true)} ?disabled=${this.orgStorageQuotaReached} > diff --git a/frontend/src/pages/org/dashboard.ts b/frontend/src/pages/org/dashboard.ts index 2c73e39449..f3f8862095 100644 --- a/frontend/src/pages/org/dashboard.ts +++ b/frontend/src/pages/org/dashboard.ts @@ -104,7 +104,7 @@ export class Dashboard extends LiteElement { ); }} > - + ${msg("Create New...")} diff --git a/frontend/src/pages/org/settings.ts b/frontend/src/pages/org/settings.ts index 9449b7837b..816d1051b9 100644 --- a/frontend/src/pages/org/settings.ts +++ b/frontend/src/pages/org/settings.ts @@ -160,7 +160,7 @@ export class OrgSettings extends LiteElement { slot="nav" href=${`${this.orgBasePath}/${path}`} class="block font-medium rounded-sm mb-2 mr-2 p-2 transition-all ${isActive - ? "text-blue-600 bg-blue-50 shadow-sm" + ? "text-blue-600 bg-blue-50 shadow-sm shadow-blue-800/20" : "text-neutral-600 hover:bg-neutral-50"}" @click=${this.navLink} aria-selected=${isActive} @@ -174,9 +174,9 @@ export class OrgSettings extends LiteElement { return html`
-
+
-
+
@@ -199,8 +199,9 @@ export class OrgSettings extends LiteElement { )}
-
+
-
+
@@ -235,17 +236,13 @@ export class OrgSettings extends LiteElement {
- ${msg("Org ID")} -
- ${this.org.id} - this.org.id} - > -
+
-
+
diff --git a/frontend/src/pages/org/workflow-detail.ts b/frontend/src/pages/org/workflow-detail.ts index 682b809f4e..a59948be1c 100644 --- a/frontend/src/pages/org/workflow-detail.ts +++ b/frontend/src/pages/org/workflow-detail.ts @@ -530,7 +530,7 @@ export class WorkflowDetail extends LiteElement { const isActive = tabName === this.activePanel; let className = "text-neutral-600 hover:bg-neutral-50"; if (isActive) { - className = "text-blue-600 bg-blue-50 shadow-sm"; + className = "text-blue-600 bg-blue-50 shadow-sm shadow-blue-800/20"; } else if (disabled) { className = "text-neutral-300 cursor-not-allowed"; } diff --git a/frontend/src/pages/org/workflows-list.ts b/frontend/src/pages/org/workflows-list.ts index 4f010cbb3b..6aee077f38 100644 --- a/frontend/src/pages/org/workflows-list.ts +++ b/frontend/src/pages/org/workflows-list.ts @@ -294,7 +294,7 @@ export class WorkflowsList extends LiteElement {