Crawl config tag editor UI (#422)
Allow users to set tags on a crawl config. Resolves #362
This commit is contained in:
parent
52d9ae9661
commit
e3d34ff08f
@ -52,6 +52,14 @@ export class ConfigDetails extends LiteElement {
|
|||||||
>
|
>
|
||||||
<btrix-desc-list>
|
<btrix-desc-list>
|
||||||
${this.renderSetting(msg("Name"), crawlConfig?.name)}
|
${this.renderSetting(msg("Name"), crawlConfig?.name)}
|
||||||
|
${this.renderSetting(
|
||||||
|
msg("Tags"),
|
||||||
|
crawlConfig?.tags?.length
|
||||||
|
? crawlConfig.tags.map(
|
||||||
|
(tag) => html`<btrix-tag class="mt-1 mr-2">${tag}</btrix-tag>`
|
||||||
|
)
|
||||||
|
: undefined
|
||||||
|
)}
|
||||||
</btrix-desc-list>
|
</btrix-desc-list>
|
||||||
</section>
|
</section>
|
||||||
<section id="crawler-settings" class="mb-8">
|
<section id="crawler-settings" class="mb-8">
|
||||||
|
@ -95,6 +95,12 @@ import("./section-heading").then(({ SectionHeading }) => {
|
|||||||
import("./config-details").then(({ ConfigDetails }) => {
|
import("./config-details").then(({ ConfigDetails }) => {
|
||||||
customElements.define("btrix-config-details", ConfigDetails);
|
customElements.define("btrix-config-details", ConfigDetails);
|
||||||
});
|
});
|
||||||
|
import("./tag-input").then(({ TagInput }) => {
|
||||||
|
customElements.define("btrix-tag-input", TagInput);
|
||||||
|
});
|
||||||
|
import("./tag").then(({ Tag }) => {
|
||||||
|
customElements.define("btrix-tag", Tag);
|
||||||
|
});
|
||||||
|
|
||||||
customElements.define("btrix-alert", Alert);
|
customElements.define("btrix-alert", Alert);
|
||||||
customElements.define("btrix-input", Input);
|
customElements.define("btrix-input", Input);
|
||||||
|
355
frontend/src/components/tag-input.ts
Normal file
355
frontend/src/components/tag-input.ts
Normal file
@ -0,0 +1,355 @@
|
|||||||
|
import { LitElement, html, css } from "lit";
|
||||||
|
import { state, property, query } from "lit/decorators.js";
|
||||||
|
import { msg, localized, str } from "@lit/localize";
|
||||||
|
import type { SlInput, SlMenu } from "@shoelace-style/shoelace";
|
||||||
|
import inputCss from "@shoelace-style/shoelace/dist/components/input/input.styles.js";
|
||||||
|
import union from "lodash/fp/union";
|
||||||
|
import debounce from "lodash/fp/debounce";
|
||||||
|
|
||||||
|
export type Tags = string[];
|
||||||
|
export type TagsChangeEvent = CustomEvent<{
|
||||||
|
tags: string[];
|
||||||
|
}>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Usage:
|
||||||
|
* ```ts
|
||||||
|
* <btrix-tag-input
|
||||||
|
* initialTags=${[]}
|
||||||
|
* @tags-change=${console.log}
|
||||||
|
* ></btrix-tag-input>
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @events tags-change
|
||||||
|
*/
|
||||||
|
@localized()
|
||||||
|
export class TagInput extends LitElement {
|
||||||
|
static styles = css`
|
||||||
|
:host {
|
||||||
|
--tag-height: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
${inputCss}
|
||||||
|
|
||||||
|
.input {
|
||||||
|
flex-wrap: wrap;
|
||||||
|
height: auto;
|
||||||
|
overflow: visible;
|
||||||
|
min-height: calc(var(--tag-height) + 1rem);
|
||||||
|
}
|
||||||
|
|
||||||
|
.input__control {
|
||||||
|
align-self: center;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdownWrapper {
|
||||||
|
flex-grow: 1;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdownWrapper:not(:first-child) .input__control {
|
||||||
|
padding-left: var(--sl-spacing-small);
|
||||||
|
padding-right: var(--sl-spacing-small);
|
||||||
|
}
|
||||||
|
|
||||||
|
btrix-tag {
|
||||||
|
margin-left: var(--sl-spacing-x-small);
|
||||||
|
margin-top: calc(0.5rem - 1px);
|
||||||
|
max-width: calc(
|
||||||
|
100% - var(--sl-spacing-x-small) - var(--sl-spacing-x-small)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 9999;
|
||||||
|
margin-top: -0.25rem;
|
||||||
|
margin-left: 0.25rem;
|
||||||
|
transform-origin: top left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hidden {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.animateShow {
|
||||||
|
animation: dropdownShow 100ms ease forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
.animateHide {
|
||||||
|
animation: dropdownHide 100ms ease forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes dropdownShow {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: scale(0.9);
|
||||||
|
}
|
||||||
|
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes dropdownHide {
|
||||||
|
from {
|
||||||
|
opacity: 1;
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
to {
|
||||||
|
opacity: 0;
|
||||||
|
transform: scale(0.9);
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
@property({ type: Array })
|
||||||
|
initialTags?: Tags;
|
||||||
|
|
||||||
|
@property({ type: Boolean })
|
||||||
|
disabled = false;
|
||||||
|
|
||||||
|
// TODO validate required
|
||||||
|
@property({ type: Boolean })
|
||||||
|
required = false;
|
||||||
|
|
||||||
|
@state()
|
||||||
|
private tags: Tags = [];
|
||||||
|
|
||||||
|
@state()
|
||||||
|
private inputValue = "";
|
||||||
|
|
||||||
|
@state()
|
||||||
|
private dropdownIsOpen?: boolean;
|
||||||
|
|
||||||
|
@state()
|
||||||
|
private tagOptions: Tags = [];
|
||||||
|
|
||||||
|
@query("#input")
|
||||||
|
private input?: HTMLInputElement;
|
||||||
|
|
||||||
|
@query("sl-menu")
|
||||||
|
private menu!: SlMenu;
|
||||||
|
|
||||||
|
connectedCallback() {
|
||||||
|
if (this.initialTags) {
|
||||||
|
this.tags = this.initialTags;
|
||||||
|
}
|
||||||
|
super.connectedCallback();
|
||||||
|
}
|
||||||
|
|
||||||
|
willUpdate(changedProperties: Map<string, any>) {
|
||||||
|
if (changedProperties.has("tags") && this.required) {
|
||||||
|
if (this.tags.length) {
|
||||||
|
this.removeAttribute("data-invalid");
|
||||||
|
} else {
|
||||||
|
this.setAttribute("data-invalid", "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
reportValidity() {
|
||||||
|
this.input?.reportValidity();
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const placeholder = msg("Tags separated by comma");
|
||||||
|
return html`
|
||||||
|
<div class="form-control form-control--has-label">
|
||||||
|
<label
|
||||||
|
class="form-control__label"
|
||||||
|
part="form-control-label"
|
||||||
|
for="input"
|
||||||
|
>
|
||||||
|
<slot name="label">${msg("Tags")}</slot>
|
||||||
|
</label>
|
||||||
|
<div
|
||||||
|
class="input input--medium input--standard"
|
||||||
|
@click=${this.onInputWrapperClick}
|
||||||
|
>
|
||||||
|
${this.renderTags()}
|
||||||
|
<div
|
||||||
|
class="dropdownWrapper"
|
||||||
|
style="min-width: ${placeholder.length}ch"
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
slot="trigger"
|
||||||
|
id="input"
|
||||||
|
class="input__control"
|
||||||
|
@focus=${this.onFocus}
|
||||||
|
@blur=${this.onBlur}
|
||||||
|
@keydown=${this.onKeydown}
|
||||||
|
@input=${this.onInput}
|
||||||
|
@keyup=${this.onKeyup}
|
||||||
|
@paste=${this.onPaste}
|
||||||
|
?required=${this.required && !this.tags.length}
|
||||||
|
placeholder=${placeholder}
|
||||||
|
role="combobox"
|
||||||
|
aria-controls="dropdown"
|
||||||
|
aria-expanded="${this.dropdownIsOpen === true}"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
id="dropdown"
|
||||||
|
class="dropdown ${this.dropdownIsOpen === true
|
||||||
|
? "animateShow"
|
||||||
|
: this.dropdownIsOpen === false
|
||||||
|
? "animateHide"
|
||||||
|
: "hidden"}"
|
||||||
|
>
|
||||||
|
<sl-menu
|
||||||
|
role="listbox"
|
||||||
|
@keydown=${(e: KeyboardEvent) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
}}
|
||||||
|
@keyup=${(e: KeyboardEvent) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
if (e.key === "Escape") {
|
||||||
|
this.dropdownIsOpen = false;
|
||||||
|
this.input?.focus();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
@sl-select=${this.onSelect}
|
||||||
|
>
|
||||||
|
${this.tagOptions
|
||||||
|
.slice(0, 3)
|
||||||
|
.map(
|
||||||
|
(tag) => html`
|
||||||
|
<sl-menu-item role="option" value=${tag}
|
||||||
|
>${tag}</sl-menu-item
|
||||||
|
>
|
||||||
|
`
|
||||||
|
)}
|
||||||
|
${this.tagOptions.length ? html`<sl-divider></sl-divider>` : ""}
|
||||||
|
|
||||||
|
<sl-menu-item role="option" value=${this.inputValue}>
|
||||||
|
${msg(str`Add “${this.inputValue.toLocaleLowerCase()}”`)}
|
||||||
|
</sl-menu-item>
|
||||||
|
</sl-menu>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private renderTags() {
|
||||||
|
return this.tags.map(this.renderTag);
|
||||||
|
}
|
||||||
|
|
||||||
|
private renderTag = (content: string) => {
|
||||||
|
const removeTag = () => {
|
||||||
|
this.tags = this.tags.filter((v) => v !== content);
|
||||||
|
this.dispatchChange();
|
||||||
|
};
|
||||||
|
return html`
|
||||||
|
<btrix-tag
|
||||||
|
variant="primary"
|
||||||
|
removable
|
||||||
|
@sl-remove=${removeTag}
|
||||||
|
title=${content}
|
||||||
|
>${content}</btrix-tag
|
||||||
|
>
|
||||||
|
`;
|
||||||
|
};
|
||||||
|
|
||||||
|
private onSelect(e: CustomEvent) {
|
||||||
|
this.addTags([e.detail.item.value]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private onFocus(e: FocusEvent) {
|
||||||
|
const input = e.target as HTMLInputElement;
|
||||||
|
(input.parentElement as HTMLElement).classList.add("input--focused");
|
||||||
|
if (input.value) {
|
||||||
|
this.dropdownIsOpen = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private onBlur(e: FocusEvent) {
|
||||||
|
if (this.menu?.contains(e.relatedTarget as HTMLElement)) {
|
||||||
|
// Keep focus on form control if moving to menu selection
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const input = e.target as HTMLInputElement;
|
||||||
|
(input.parentElement as HTMLElement).classList.remove("input--focused");
|
||||||
|
this.addTags([input.value]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private onKeydown(e: KeyboardEvent) {
|
||||||
|
if (e.key === "ArrowDown") {
|
||||||
|
e.preventDefault();
|
||||||
|
this.menu?.querySelector("sl-menu-item")?.focus();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (e.key === "," || e.key === "Enter") {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
const input = e.target as HTMLInputElement;
|
||||||
|
const value = input.value.trim();
|
||||||
|
if (value) {
|
||||||
|
this.addTags([value]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private onInput = debounce(200)(async (e: InputEvent) => {
|
||||||
|
const input = (e as any).originalTarget as HTMLInputElement;
|
||||||
|
this.inputValue = input.value;
|
||||||
|
if (input.value.length) {
|
||||||
|
this.dropdownIsOpen = true;
|
||||||
|
this.tagOptions = await this.getOptions();
|
||||||
|
}
|
||||||
|
}) as any;
|
||||||
|
|
||||||
|
private onKeyup(e: KeyboardEvent) {
|
||||||
|
const input = e.target as HTMLInputElement;
|
||||||
|
if (e.key === "Escape") {
|
||||||
|
(input.parentElement as HTMLElement).classList.remove("input--focused");
|
||||||
|
this.dropdownIsOpen = false;
|
||||||
|
this.inputValue = "";
|
||||||
|
input.value = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private onPaste(e: ClipboardEvent) {
|
||||||
|
const text = e.clipboardData?.getData("text");
|
||||||
|
if (text) {
|
||||||
|
this.addTags(text.split(","));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private onInputWrapperClick(e: MouseEvent) {
|
||||||
|
if (e.target === e.currentTarget) {
|
||||||
|
this.input?.focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async addTags(tags: Tags) {
|
||||||
|
await this.updateComplete;
|
||||||
|
this.tags = union(
|
||||||
|
tags.map((v) => v.trim().toLocaleLowerCase()).filter((v) => v),
|
||||||
|
this.tags
|
||||||
|
);
|
||||||
|
this.dispatchChange();
|
||||||
|
this.dropdownIsOpen = false;
|
||||||
|
this.input!.value = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
private async dispatchChange() {
|
||||||
|
await this.updateComplete;
|
||||||
|
this.dispatchEvent(
|
||||||
|
<TagsChangeEvent>new CustomEvent("tags-change", {
|
||||||
|
detail: { tags: this.tags },
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async getOptions() {
|
||||||
|
// TODO actual API call
|
||||||
|
// https://github.com/webrecorder/browsertrix-cloud/issues/453
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
45
frontend/src/components/tag.ts
Normal file
45
frontend/src/components/tag.ts
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
import { css } from "lit";
|
||||||
|
import SLTag from "@shoelace-style/shoelace/dist/components/tag/tag.js";
|
||||||
|
import tagStyles from "@shoelace-style/shoelace/dist/components/tag/tag.styles.js";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Customized <sl-tag>
|
||||||
|
*
|
||||||
|
* Usage:
|
||||||
|
* ```ts
|
||||||
|
* <btrix-tag>Content</btrix-tag>
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
export class Tag extends SLTag {
|
||||||
|
static styles = css`
|
||||||
|
${tagStyles}
|
||||||
|
|
||||||
|
.tag {
|
||||||
|
height: var(--tag-height, 1.5rem);
|
||||||
|
background-color: var(--sl-color-blue-100);
|
||||||
|
border-color: var(--sl-color-blue-500);
|
||||||
|
color: var(--sl-color-blue-600);
|
||||||
|
font-family: var(--sl-font-sans);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tag__content {
|
||||||
|
max-width: 100%;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tag__remove {
|
||||||
|
color: var(--sl-color-blue-600);
|
||||||
|
border-radius: 100%;
|
||||||
|
transition: background-color 0.1s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tag__remove:hover {
|
||||||
|
background-color: var(--sl-color-blue-600);
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
pill = true;
|
||||||
|
}
|
@ -35,6 +35,7 @@ import type {
|
|||||||
ExclusionChangeEvent,
|
ExclusionChangeEvent,
|
||||||
} from "../../components/queue-exclusion-table";
|
} from "../../components/queue-exclusion-table";
|
||||||
import type { TimeInputChangeEvent } from "../../components/time-input";
|
import type { TimeInputChangeEvent } from "../../components/time-input";
|
||||||
|
import type { Tags, TagsChangeEvent } from "../../components/tag-input";
|
||||||
import type {
|
import type {
|
||||||
CrawlConfigParams,
|
CrawlConfigParams,
|
||||||
Profile,
|
Profile,
|
||||||
@ -91,6 +92,7 @@ type FormState = {
|
|||||||
runNow: boolean;
|
runNow: boolean;
|
||||||
jobName: CrawlConfigParams["name"];
|
jobName: CrawlConfigParams["name"];
|
||||||
browserProfile: Profile | null;
|
browserProfile: Profile | null;
|
||||||
|
tags: Tags;
|
||||||
};
|
};
|
||||||
|
|
||||||
const getDefaultProgressState = (hasConfigId = false): ProgressState => {
|
const getDefaultProgressState = (hasConfigId = false): ProgressState => {
|
||||||
@ -156,6 +158,7 @@ const getDefaultFormState = (): FormState => ({
|
|||||||
runNow: false,
|
runNow: false,
|
||||||
jobName: "",
|
jobName: "",
|
||||||
browserProfile: null,
|
browserProfile: null,
|
||||||
|
tags: [],
|
||||||
});
|
});
|
||||||
const defaultProgressState = getDefaultProgressState();
|
const defaultProgressState = getDefaultProgressState();
|
||||||
const orderedTabNames = STEPS.filter(
|
const orderedTabNames = STEPS.filter(
|
||||||
@ -310,45 +313,48 @@ export class CrawlConfigEditor extends LiteElement {
|
|||||||
|
|
||||||
private getInitialFormState(): Partial<FormState> {
|
private getInitialFormState(): Partial<FormState> {
|
||||||
if (!this.initialCrawlConfig) return {};
|
if (!this.initialCrawlConfig) return {};
|
||||||
const seedState: Partial<FormState> = {};
|
const formState: Partial<FormState> = {};
|
||||||
const { seeds, scopeType } = this.initialCrawlConfig.config;
|
const { seeds, scopeType } = this.initialCrawlConfig.config;
|
||||||
if (this.initialCrawlConfig.jobType === "seed-crawl") {
|
if (this.initialCrawlConfig.jobType === "seed-crawl") {
|
||||||
seedState.primarySeedUrl =
|
formState.primarySeedUrl =
|
||||||
typeof seeds[0] === "string" ? seeds[0] : seeds[0].url;
|
typeof seeds[0] === "string" ? seeds[0] : seeds[0].url;
|
||||||
} else {
|
} else {
|
||||||
// Treat "custom" like URL list
|
// Treat "custom" like URL list
|
||||||
seedState.urlList = seeds
|
formState.urlList = seeds
|
||||||
.map((seed) => (typeof seed === "string" ? seed : seed.url))
|
.map((seed) => (typeof seed === "string" ? seed : seed.url))
|
||||||
.join("\n");
|
.join("\n");
|
||||||
|
|
||||||
if (this.initialCrawlConfig.jobType === "custom") {
|
if (this.initialCrawlConfig.jobType === "custom") {
|
||||||
seedState.scopeType = scopeType || "page";
|
formState.scopeType = scopeType || "page";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const scheduleState: Partial<FormState> = {};
|
|
||||||
if (this.initialCrawlConfig.schedule) {
|
if (this.initialCrawlConfig.schedule) {
|
||||||
scheduleState.scheduleType = "cron";
|
formState.scheduleType = "cron";
|
||||||
scheduleState.scheduleFrequency = getScheduleInterval(
|
formState.scheduleFrequency = getScheduleInterval(
|
||||||
this.initialCrawlConfig.schedule
|
this.initialCrawlConfig.schedule
|
||||||
);
|
);
|
||||||
const nextDate = getNextDate(this.initialCrawlConfig.schedule)!;
|
const nextDate = getNextDate(this.initialCrawlConfig.schedule)!;
|
||||||
scheduleState.scheduleDayOfMonth = nextDate.getDate();
|
formState.scheduleDayOfMonth = nextDate.getDate();
|
||||||
scheduleState.scheduleDayOfWeek = nextDate.getDay();
|
formState.scheduleDayOfWeek = nextDate.getDay();
|
||||||
const hours = nextDate.getHours();
|
const hours = nextDate.getHours();
|
||||||
scheduleState.scheduleTime = {
|
formState.scheduleTime = {
|
||||||
hour: hours % 12 || 12,
|
hour: hours % 12 || 12,
|
||||||
minute: nextDate.getMinutes(),
|
minute: nextDate.getMinutes(),
|
||||||
period: hours > 11 ? "PM" : "AM",
|
period: hours > 11 ? "PM" : "AM",
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
if (this.configId) {
|
if (this.configId) {
|
||||||
scheduleState.scheduleType = "none";
|
formState.scheduleType = "none";
|
||||||
} else {
|
} else {
|
||||||
scheduleState.scheduleType = "now";
|
formState.scheduleType = "now";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.initialCrawlConfig.tags?.length) {
|
||||||
|
formState.tags = this.initialCrawlConfig.tags;
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
jobName: this.initialCrawlConfig.name,
|
jobName: this.initialCrawlConfig.name,
|
||||||
browserProfile: this.initialCrawlConfig.profileid
|
browserProfile: this.initialCrawlConfig.profileid
|
||||||
@ -358,8 +364,7 @@ export class CrawlConfigEditor extends LiteElement {
|
|||||||
.scopeType as FormState["scopeType"],
|
.scopeType as FormState["scopeType"],
|
||||||
exclusions: this.initialCrawlConfig.config.exclude,
|
exclusions: this.initialCrawlConfig.config.exclude,
|
||||||
includeLinkedPages: Boolean(this.initialCrawlConfig.config.extraHops),
|
includeLinkedPages: Boolean(this.initialCrawlConfig.config.extraHops),
|
||||||
...seedState,
|
...formState,
|
||||||
...scheduleState,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1243,6 +1248,24 @@ https://example.net`}
|
|||||||
${this.renderHelpTextCol(
|
${this.renderHelpTextCol(
|
||||||
html`Try to create a unique name to help keep things organized!`
|
html`Try to create a unique name to help keep things organized!`
|
||||||
)}
|
)}
|
||||||
|
${this.renderFormCol(
|
||||||
|
html`
|
||||||
|
<btrix-tag-input
|
||||||
|
.initialTags=${this.formState.tags}
|
||||||
|
@tags-change=${(e: TagsChangeEvent) =>
|
||||||
|
this.updateFormState(
|
||||||
|
{
|
||||||
|
tags: e.detail.tags,
|
||||||
|
},
|
||||||
|
true
|
||||||
|
)}
|
||||||
|
></btrix-tag-input>
|
||||||
|
`
|
||||||
|
)}
|
||||||
|
${this.renderHelpTextCol(
|
||||||
|
html`Create or assign this crawl (and its outputs) to one or more tags
|
||||||
|
to help organize your archived data.`
|
||||||
|
)}
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1261,6 +1284,7 @@ https://example.net`}
|
|||||||
<btrix-config-details .crawlConfig=${crawlConfig}>
|
<btrix-config-details .crawlConfig=${crawlConfig}>
|
||||||
</btrix-config-details>
|
</btrix-config-details>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
${when(this.formHasError, () =>
|
${when(this.formHasError, () =>
|
||||||
this.renderErrorAlert(
|
this.renderErrorAlert(
|
||||||
msg(
|
msg(
|
||||||
@ -1627,6 +1651,7 @@ https://example.net`}
|
|||||||
crawlTimeout: this.formState.crawlTimeoutMinutes
|
crawlTimeout: this.formState.crawlTimeoutMinutes
|
||||||
? this.formState.crawlTimeoutMinutes * 60
|
? this.formState.crawlTimeoutMinutes * 60
|
||||||
: 0,
|
: 0,
|
||||||
|
tags: this.formState.tags,
|
||||||
config: {
|
config: {
|
||||||
...(this.jobType === "seed-crawl"
|
...(this.jobType === "seed-crawl"
|
||||||
? this.parseSeededConfig()
|
? this.parseSeededConfig()
|
||||||
|
@ -460,6 +460,7 @@ export class CrawlTemplatesDetail extends LiteElement {
|
|||||||
profileid: this.crawlConfig.profileid || null,
|
profileid: this.crawlConfig.profileid || null,
|
||||||
jobType: this.crawlConfig.jobType,
|
jobType: this.crawlConfig.jobType,
|
||||||
schedule: this.crawlConfig.schedule,
|
schedule: this.crawlConfig.schedule,
|
||||||
|
tags: this.crawlConfig.tags,
|
||||||
};
|
};
|
||||||
|
|
||||||
this.navTo(`/archives/${this.archiveId}/crawl-templates/new`, {
|
this.navTo(`/archives/${this.archiveId}/crawl-templates/new`, {
|
||||||
|
@ -611,6 +611,7 @@ export class CrawlTemplatesList extends LiteElement {
|
|||||||
profileid: template.profileid || null,
|
profileid: template.profileid || null,
|
||||||
jobType: template.jobType,
|
jobType: template.jobType,
|
||||||
schedule: template.schedule,
|
schedule: template.schedule,
|
||||||
|
tags: template.tags,
|
||||||
};
|
};
|
||||||
|
|
||||||
this.navTo(`/archives/${this.archiveId}/crawl-templates/new`, {
|
this.navTo(`/archives/${this.archiveId}/crawl-templates/new`, {
|
||||||
|
@ -731,6 +731,7 @@ export class CrawlsList extends LiteElement {
|
|||||||
profileid: template.profileid || null,
|
profileid: template.profileid || null,
|
||||||
jobType: template.jobType,
|
jobType: template.jobType,
|
||||||
schedule: template.schedule,
|
schedule: template.schedule,
|
||||||
|
tags: template.tags,
|
||||||
};
|
};
|
||||||
|
|
||||||
this.navTo(`/archives/${crawl.aid}/crawl-templates/new`, {
|
this.navTo(`/archives/${crawl.aid}/crawl-templates/new`, {
|
||||||
|
@ -61,11 +61,12 @@ export type CrawlConfigParams = {
|
|||||||
profileid: string | null;
|
profileid: string | null;
|
||||||
config: SeedConfig;
|
config: SeedConfig;
|
||||||
crawlTimeout: number | null;
|
crawlTimeout: number | null;
|
||||||
|
tags?: string[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export type InitialCrawlConfig = Pick<
|
export type InitialCrawlConfig = Pick<
|
||||||
CrawlConfigParams,
|
CrawlConfigParams,
|
||||||
"name" | "profileid" | "schedule"
|
"name" | "profileid" | "schedule" | "tags"
|
||||||
> & {
|
> & {
|
||||||
jobType?: JobType;
|
jobType?: JobType;
|
||||||
config: Pick<
|
config: Pick<
|
||||||
|
@ -99,12 +99,14 @@ const theme = css`
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Add more spacing between label, input and help text */
|
/* Add more spacing between label, input and help text */
|
||||||
|
btrix-tag-input::part(form-control-label),
|
||||||
sl-input::part(form-control-label),
|
sl-input::part(form-control-label),
|
||||||
sl-textarea::part(form-control-label),
|
sl-textarea::part(form-control-label),
|
||||||
sl-select::part(form-control-label) {
|
sl-select::part(form-control-label) {
|
||||||
line-height: 1.4;
|
line-height: 1.4;
|
||||||
margin-bottom: 0.375rem;
|
margin-bottom: 0.375rem;
|
||||||
}
|
}
|
||||||
|
btrix-tag-input::part(form-control-help-text),
|
||||||
sl-input::part(form-control-help-text),
|
sl-input::part(form-control-help-text),
|
||||||
sl-textarea::part(form-control-help-text),
|
sl-textarea::part(form-control-help-text),
|
||||||
sl-select::part(form-control-help-text) {
|
sl-select::part(form-control-help-text) {
|
||||||
|
Loading…
Reference in New Issue
Block a user