Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion frontend/src/components/ui/badge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ export type BadgeVariant =
| "primary"
| "cyan"
| "blue"
| "high-contrast";
| "high-contrast"
| "text";

/**
* Show numeric value in a label
Expand Down Expand Up @@ -64,6 +65,7 @@ export class Badge extends TailwindElement {
primary: tw`bg-white text-primary ring-primary`,
cyan: tw`bg-cyan-50 text-cyan-600 ring-cyan-600`,
blue: tw`bg-blue-50 text-blue-600 ring-blue-600`,
text: tw`text-blue-500 ring-blue-600`,
}[this.variant],
]
: {
Expand All @@ -75,6 +77,7 @@ export class Badge extends TailwindElement {
primary: tw`bg-primary text-neutral-0`,
cyan: tw`bg-cyan-50 text-cyan-600`,
blue: tw`bg-blue-50 text-blue-600`,
text: tw`text-blue-500`,
}[this.variant],
this.pill
? [
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/components/ui/code/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,11 @@ export class Code extends TailwindElement {
}

.hljs-path {
color: var(--sl-color-blue-900);
color: var(--sl-color-sky-600);
}

.hljs-domain {
color: var(--sl-color-blue-600);
color: var(--sl-color-sky-700);
}

.hljs-string {
Expand Down
1 change: 1 addition & 0 deletions frontend/src/components/ui/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import("./combobox");
import("./config-details");
import("./copy-button");
import("./copy-field");
import("./tag-container");
import("./data-grid");
import("./details");
import("./file-input");
Expand Down
137 changes: 137 additions & 0 deletions frontend/src/components/ui/tag-container.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
import clsx from "clsx";
import { css, html, type PropertyValues } from "lit";
import {
customElement,
property,
query,
queryAll,
state,
} from "lit/decorators.js";
import { ifDefined } from "lit/directives/if-defined.js";
import debounce from "lodash/fp/debounce";

import { TailwindElement } from "@/classes/TailwindElement";
import type { Tag } from "@/components/ui/tag";
import type { UnderlyingFunction } from "@/types/utils";
import localize from "@/utils/localize";
import { tw } from "@/utils/tailwind";

/**
* Displays all the tags that can be contained to one line.
* Overflowing tags are displayed in a popover.
*
* @cssproperty width
*/
@customElement("btrix-tag-container")
export class TagContainer extends TailwindElement {
static styles = css`
:host {
--width: 100%;
}
`;

@property({ type: Array })
tags: string[] = [];

@query("#container")
private readonly container?: HTMLElement | null;

@queryAll("btrix-tag")
private readonly tagNodes!: NodeListOf<Tag>;

@state()
private displayLimit?: number;

disconnectedCallback(): void {
this.debouncedCalculate.cancel();
super.disconnectedCallback();
}

protected updated(changedProperties: PropertyValues): void {
if (changedProperties.get("tags")) {
this.debouncedCalculate.cancel();
this.calculate();
}
}

render() {
const maxTags = this.tags.length;
const displayLimit = this.displayLimit;
const remainder = displayLimit && maxTags - displayLimit;

return html`
<sl-resize-observer
@sl-resize=${this.debouncedCalculate as UnderlyingFunction<
typeof this.calculate
>}
>
<div class="flex items-center">
<div
id="container"
class="flex h-6 w-[var(--width)] flex-wrap gap-x-1.5 overflow-hidden contain-content"
>
${this.tags.map(
(tag, i) =>
html`<btrix-tag
aria-hidden=${ifDefined(
displayLimit === undefined
? undefined
: i > displayLimit - 1
? "true"
: "false",
)}
>${tag}</btrix-tag
>`,
)}
</div>

<btrix-popover hoist placement="right">
<btrix-badge
variant="text"
size="large"
class=${clsx(!remainder && tw`invisible`)}
aria-hidden=${remainder ? "false" : "true"}
tabIndex="0"
>+${localize.number(remainder || maxTags)}</btrix-badge
>
<div slot="content" class="z-50 flex flex-wrap gap-1.5">
${this.tags
.slice(displayLimit)
.map((tag) => html`<btrix-tag>${tag}</btrix-tag>`)}
</div>
</btrix-popover>
</div>
</sl-resize-observer>
`;
}

private readonly calculate = () => {
const tagNodes = Array.from(this.tagNodes);

if (!tagNodes.length || !this.container) return;

const containerRect = this.container.getBoundingClientRect();
const containerTop = containerRect.top;

// Reset width
this.style.setProperty("--width", "100%");
const idx = tagNodes.findIndex(
(el) => el.getBoundingClientRect().top > containerTop,
);

if (idx === -1) return;
const lastVisible = tagNodes[idx - 1];
if (lastVisible as unknown) {
const rect = lastVisible.getBoundingClientRect();
// Decrease width of container to match end of last visible tag
this.style.setProperty(
"--width",
`${rect.left - containerRect.left + rect.width}px`,
);
}

this.displayLimit = idx;
};

private readonly debouncedCalculate = debounce(50)(this.calculate);
}
3 changes: 3 additions & 0 deletions frontend/src/components/ui/tag-input.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import {
import { customElement, property, query, state } from "lit/decorators.js";
import debounce from "lodash/fp/debounce";

import { TAG_MAX_CHARACTERS } from "./tag";

import type { UnderlyingFunction } from "@/types/utils";
import { type WorkflowTag } from "@/types/workflow";
import { dropdown } from "@/utils/css";
Expand Down Expand Up @@ -234,6 +236,7 @@ export class TagInput extends LitElement {
role="combobox"
aria-controls="dropdown"
aria-expanded="${this.dropdownIsOpen === true}"
maxlength=${TAG_MAX_CHARACTERS}
/>
<div
id="dropdown"
Expand Down
5 changes: 4 additions & 1 deletion frontend/src/components/ui/tag.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import { css, html } from "lit";
import { customElement, property } from "lit/decorators.js";
import { ifDefined } from "lit/directives/if-defined.js";

export const TAG_MAX_CHARACTERS = 40;

/**
* Customized <sl-tag>
*
Expand Down Expand Up @@ -55,7 +57,8 @@ export class Tag extends SLTag {
}

.tag__content {
max-width: 100%;
display: inline-block;
max-width: ${TAG_MAX_CHARACTERS + 2}ch;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
Expand Down
24 changes: 5 additions & 19 deletions frontend/src/pages/org/browser-profiles-list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,12 +66,11 @@ const DEFAULT_SORT_BY = {
} as const satisfies SortBy;
const INITIAL_PAGE_SIZE = 20;
const FILTER_BY_CURRENT_USER_STORAGE_KEY = "btrix.filterByCurrentUser.crawls";
const MAX_TAGS = 3;

const columnsCss = [
"min-content", // Status
"[clickable-start] minmax(min-content, 1fr)", // Name
"minmax(max-content, 1fr)", // Tags
"30ch", // Tags
"minmax(max-content, 1fr)", // Origins
"minmax(min-content, 20ch)", // Last modified
"[clickable-end] min-content", // Actions
Expand Down Expand Up @@ -523,8 +522,6 @@ export class BrowserProfilesList extends BtrixElement {
) || data.created;
const startingUrl = data.origins[0];
const otherOrigins = data.origins.slice(1);
const firstTags = data.tags.slice(0, MAX_TAGS);
const otherTags = data.tags.slice(MAX_TAGS);

return html`
<btrix-table-row
Expand All @@ -550,25 +547,14 @@ export class BrowserProfilesList extends BtrixElement {
>
</btrix-table-cell>
<btrix-table-cell>
<div class="flex flex-wrap gap-1.5">
${firstTags.map((tag) => html`<btrix-tag>${tag}</btrix-tag>`)}
</div>
${otherTags.length
? html`<btrix-popover placement="right" hoist>
<btrix-badge
>+${this.localize.number(otherTags.length)}</btrix-badge
>
<div slot="content" class="flex flex-wrap gap-1.5">
${otherTags.map((tag) => html`<btrix-tag>${tag}</btrix-tag>`)}
</div>
</btrix-popover>`
: nothing}
<btrix-tag-container class="relative hover:z-[2]" .tags=${data.tags}>
</btrix-tag-container>
</btrix-table-cell>
<btrix-table-cell>
<btrix-table-cell class="[--btrix-table-cell-gap:0]">
<btrix-code language="url" value=${startingUrl} noWrap></btrix-code>
${otherOrigins.length
? html`<btrix-popover placement="right" hoist>
<btrix-badge
<btrix-badge variant="text" size="large"
>+${this.localize.number(otherOrigins.length)}</btrix-badge
>
<ul slot="content">
Expand Down
37 changes: 37 additions & 0 deletions frontend/src/stories/components/TagContainer.stories.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import type { Meta, StoryObj } from "@storybook/web-components";

import { renderComponent, type RenderProps } from "./TagContainer";

const meta = {
title: "Components/Tag Container",
component: "btrix-contain-with-remainder",
tags: ["autodocs"],
render: renderComponent,
argTypes: {},
} satisfies Meta<RenderProps>;

export default meta;
type Story = StoryObj<RenderProps>;

/**
* Resize your browser window to see the remaining tags update.
*/
export const Basic: Story = {
args: {
tags: [
"Social Media",
"Marketing",
"Tooling",
"High Priority",
"Low Priority",
"Dev",
"Approved",
"Rejected",
"Good",
"Bad",
"2024",
"2025",
"2026",
],
},
};
11 changes: 11 additions & 0 deletions frontend/src/stories/components/TagContainer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { html } from "lit";

import type { TagContainer } from "@/components/ui/tag-container";

import "@/components/ui/tag-container";

export type RenderProps = TagContainer;

export const renderComponent = ({ tags }: Partial<RenderProps>) => {
return html`<btrix-tag-container .tags=${tags || []}></btrix-tag-container>`;
};
Loading