feat: Workflow form collapsible section enhancements (#2381)
Resolves https://github.com/webrecorder/browsertrix/issues/2359 ## Changes - Track when a workflow form section is opened - Hide workflow form section navigation on small screens --------- Co-authored-by: Ilya Kreymer <ikreymer@users.noreply.github.com> Co-authored-by: Emma Segal-Grossman <hi@emma.cafe> Co-authored-by: Ilya Kreymer <ikreymer@gmail.com>
This commit is contained in:
parent
3ca68bf1d2
commit
8db80f5570
@ -5,8 +5,6 @@ import { ifDefined } from "lit/directives/if-defined.js";
|
|||||||
import { TailwindElement } from "@/classes/TailwindElement";
|
import { TailwindElement } from "@/classes/TailwindElement";
|
||||||
|
|
||||||
const DEFAULT_PANEL_ID = "default-panel";
|
const DEFAULT_PANEL_ID = "default-panel";
|
||||||
// postcss-lit-disable-next-line
|
|
||||||
export const TWO_COL_SCREEN_MIN_CSS = css`64.5rem`;
|
|
||||||
|
|
||||||
const tabTagName = "btrix-tab-list-tab" as const;
|
const tabTagName = "btrix-tab-list-tab" as const;
|
||||||
|
|
||||||
@ -58,12 +56,6 @@ export class TabList extends TailwindElement {
|
|||||||
--track-width: 4px;
|
--track-width: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media only screen and (min-width: ${TWO_COL_SCREEN_MIN_CSS}) {
|
|
||||||
.navWrapper {
|
|
||||||
overflow: initial;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.header {
|
.header {
|
||||||
grid-area: header;
|
grid-area: header;
|
||||||
font-size: var(--sl-font-size-large);
|
font-size: var(--sl-font-size-large);
|
||||||
@ -94,12 +86,6 @@ export class TabList extends TailwindElement {
|
|||||||
margin-left: var(--track-width);
|
margin-left: var(--track-width);
|
||||||
}
|
}
|
||||||
|
|
||||||
@media only screen and (min-width: ${TWO_COL_SCREEN_MIN_CSS}) {
|
|
||||||
.tablist {
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.track {
|
.track {
|
||||||
display: none;
|
display: none;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@ -118,13 +104,6 @@ export class TabList extends TailwindElement {
|
|||||||
border-radius: var(--track-width);
|
border-radius: var(--track-width);
|
||||||
background-color: var(--sl-color-primary-500);
|
background-color: var(--sl-color-primary-500);
|
||||||
}
|
}
|
||||||
|
|
||||||
@media only screen and (min-width: ${TWO_COL_SCREEN_MIN_CSS}) {
|
|
||||||
.show-indicator .track,
|
|
||||||
.show-indicator .indicator {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
// ID of active tab
|
// ID of active tab
|
||||||
@ -178,7 +157,9 @@ export class TabList extends TailwindElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return html`<div class="navWrapper min-w-0">${this.renderNav()}</div>`;
|
return html`<div class="min-w-0 lg:overflow-hidden">
|
||||||
|
${this.renderNav()}
|
||||||
|
</div>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
renderNav() {
|
renderNav() {
|
||||||
@ -193,11 +174,14 @@ export class TabList extends TailwindElement {
|
|||||||
? "hide-indicator"
|
? "hide-indicator"
|
||||||
: "show-indicator"} -mx-3 overflow-x-hidden px-3"
|
: "show-indicator"} -mx-3 overflow-x-hidden px-3"
|
||||||
>
|
>
|
||||||
<div class="track" role="presentation">
|
<div class="track lg:block" role="presentation">
|
||||||
<div class="indicator" role="presentation"></div>
|
<div class="indicator lg:block" role="presentation"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ul class="tablist -mx-3 overflow-x-auto px-3" role="tablist">
|
<ul
|
||||||
|
class="tablist -mx-3 overflow-x-auto px-3 lg:flex-col"
|
||||||
|
role="tablist"
|
||||||
|
>
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
@ -59,9 +59,11 @@ import { panel } from "@/layouts/panel";
|
|||||||
import infoTextStrings from "@/strings/crawl-workflows/infoText";
|
import infoTextStrings from "@/strings/crawl-workflows/infoText";
|
||||||
import scopeTypeLabels from "@/strings/crawl-workflows/scopeType";
|
import scopeTypeLabels from "@/strings/crawl-workflows/scopeType";
|
||||||
import sectionStrings from "@/strings/crawl-workflows/section";
|
import sectionStrings from "@/strings/crawl-workflows/section";
|
||||||
|
import { AnalyticsTrackEvent } from "@/trackEvents";
|
||||||
import { ScopeType, type Seed, type WorkflowParams } from "@/types/crawler";
|
import { ScopeType, type Seed, type WorkflowParams } from "@/types/crawler";
|
||||||
import type { UnderlyingFunction } from "@/types/utils";
|
import type { UnderlyingFunction } from "@/types/utils";
|
||||||
import { NewWorkflowOnlyScopeType } from "@/types/workflow";
|
import { NewWorkflowOnlyScopeType } from "@/types/workflow";
|
||||||
|
import { track } from "@/utils/analytics";
|
||||||
import { isApiError } from "@/utils/api";
|
import { isApiError } from "@/utils/api";
|
||||||
import { DEPTH_SUPPORTED_SCOPES, isPageScopeType } from "@/utils/crawler";
|
import { DEPTH_SUPPORTED_SCOPES, isPageScopeType } from "@/utils/crawler";
|
||||||
import {
|
import {
|
||||||
@ -69,6 +71,7 @@ import {
|
|||||||
humanizeNextDate,
|
humanizeNextDate,
|
||||||
humanizeSchedule,
|
humanizeSchedule,
|
||||||
} from "@/utils/cron";
|
} from "@/utils/cron";
|
||||||
|
import { makeCurrentTargetHandler, stopProp } from "@/utils/events";
|
||||||
import { formValidator, maxLengthValidator } from "@/utils/form";
|
import { formValidator, maxLengthValidator } from "@/utils/form";
|
||||||
import localize from "@/utils/localize";
|
import localize from "@/utils/localize";
|
||||||
import { isArchivingDisabled } from "@/utils/orgs";
|
import { isArchivingDisabled } from "@/utils/orgs";
|
||||||
@ -234,6 +237,7 @@ export class WorkflowEditor extends BtrixElement {
|
|||||||
threshold: 0.2, // stricter; default is 0.6
|
threshold: 0.2, // stricter; default is 0.6
|
||||||
});
|
});
|
||||||
|
|
||||||
|
private readonly handleCurrentTarget = makeCurrentTargetHandler(this);
|
||||||
private readonly checkFormValidity = formValidator(this);
|
private readonly checkFormValidity = formValidator(this);
|
||||||
private readonly validateNameMax = maxLengthValidator(50);
|
private readonly validateNameMax = maxLengthValidator(50);
|
||||||
private readonly validateDescriptionMax = maxLengthValidator(350);
|
private readonly validateDescriptionMax = maxLengthValidator(350);
|
||||||
@ -405,7 +409,10 @@ export class WorkflowEditor extends BtrixElement {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<btrix-tab-list tab=${ifDefined(this.progressState?.activeTab)}>
|
<btrix-tab-list
|
||||||
|
class="hidden lg:block"
|
||||||
|
tab=${ifDefined(this.progressState?.activeTab)}
|
||||||
|
>
|
||||||
${STEPS.map(button)}
|
${STEPS.map(button)}
|
||||||
</btrix-tab-list>
|
</btrix-tab-list>
|
||||||
`;
|
`;
|
||||||
@ -436,13 +443,17 @@ export class WorkflowEditor extends BtrixElement {
|
|||||||
activeTab: name,
|
activeTab: name,
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
@sl-show=${() => {
|
@sl-show=${this.handleCurrentTarget(() => {
|
||||||
this.pauseObserve();
|
this.pauseObserve();
|
||||||
this.updateProgressState({
|
this.updateProgressState({
|
||||||
activeTab: name,
|
activeTab: name,
|
||||||
});
|
});
|
||||||
}}
|
|
||||||
@sl-hide=${(e: SlHideEvent) => {
|
track(AnalyticsTrackEvent.ExpandWorkflowFormSection, {
|
||||||
|
section: name,
|
||||||
|
});
|
||||||
|
})}
|
||||||
|
@sl-hide=${this.handleCurrentTarget((e: SlHideEvent) => {
|
||||||
const el = e.currentTarget as SlDetails;
|
const el = e.currentTarget as SlDetails;
|
||||||
|
|
||||||
// Check if there's any invalid elements before hiding
|
// Check if there's any invalid elements before hiding
|
||||||
@ -461,12 +472,19 @@ export class WorkflowEditor extends BtrixElement {
|
|||||||
invalidEl.focus();
|
invalidEl.focus();
|
||||||
invalidEl.checkValidity();
|
invalidEl.checkValidity();
|
||||||
}
|
}
|
||||||
}}
|
})}
|
||||||
@sl-after-show=${this.resumeObserve}
|
@sl-after-show=${this.handleCurrentTarget(this.resumeObserve)}
|
||||||
@sl-after-hide=${this.resumeObserve}
|
@sl-after-hide=${this.handleCurrentTarget(this.resumeObserve)}
|
||||||
>
|
>
|
||||||
<div slot="expand-icon" class="flex items-center">
|
<div slot="expand-icon" class="flex items-center">
|
||||||
<sl-tooltip content=${msg("Show section")} hoist>
|
<sl-tooltip
|
||||||
|
content=${msg("Show section")}
|
||||||
|
hoist
|
||||||
|
@sl-show=${stopProp}
|
||||||
|
@sl-hide=${stopProp}
|
||||||
|
@sl-after-show=${stopProp}
|
||||||
|
@sl-after-hide=${stopProp}
|
||||||
|
>
|
||||||
<sl-icon name="chevron-down" class="size-5"></sl-icon>
|
<sl-icon name="chevron-down" class="size-5"></sl-icon>
|
||||||
</sl-tooltip>
|
</sl-tooltip>
|
||||||
</div>
|
</div>
|
||||||
@ -477,6 +495,10 @@ export class WorkflowEditor extends BtrixElement {
|
|||||||
<sl-tooltip
|
<sl-tooltip
|
||||||
content=${msg("Please fix all errors in this section")}
|
content=${msg("Please fix all errors in this section")}
|
||||||
hoist
|
hoist
|
||||||
|
@sl-show=${stopProp}
|
||||||
|
@sl-hide=${stopProp}
|
||||||
|
@sl-after-show=${stopProp}
|
||||||
|
@sl-after-hide=${stopProp}
|
||||||
>
|
>
|
||||||
<sl-icon
|
<sl-icon
|
||||||
name="exclamation-lg"
|
name="exclamation-lg"
|
||||||
|
@ -3,7 +3,17 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
export enum AnalyticsTrackEvent {
|
export enum AnalyticsTrackEvent {
|
||||||
|
/**
|
||||||
|
* Generic
|
||||||
|
*/
|
||||||
PageView = "pageview",
|
PageView = "pageview",
|
||||||
|
/**
|
||||||
|
* Collections
|
||||||
|
*/
|
||||||
CopyShareCollectionLink = "Copy share collection link",
|
CopyShareCollectionLink = "Copy share collection link",
|
||||||
DownloadPublicCollection = "Download public collection",
|
DownloadPublicCollection = "Download public collection",
|
||||||
|
/**
|
||||||
|
* Workflows
|
||||||
|
*/
|
||||||
|
ExpandWorkflowFormSection = "Expand workflow form section",
|
||||||
}
|
}
|
||||||
|
@ -8,9 +8,10 @@
|
|||||||
import { AnalyticsTrackEvent } from "../trackEvents";
|
import { AnalyticsTrackEvent } from "../trackEvents";
|
||||||
|
|
||||||
export type AnalyticsTrackProps = {
|
export type AnalyticsTrackProps = {
|
||||||
org_slug: string | null;
|
org_slug?: string | null;
|
||||||
collection_slug?: string | null;
|
|
||||||
logged_in?: boolean;
|
logged_in?: boolean;
|
||||||
|
collection_slug?: string;
|
||||||
|
section?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
|
38
frontend/src/utils/events.ts
Normal file
38
frontend/src/utils/events.ts
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
/**
|
||||||
|
* Ignore events bubbling from children. Common use case would be
|
||||||
|
* ignoring @sl-hide events from tooltips within a dialog.
|
||||||
|
*
|
||||||
|
* @example Usage:
|
||||||
|
* ```
|
||||||
|
* <btrix-element
|
||||||
|
* @sl-hide=${makeCurrentTargetHandler(this)(() => console.log("Only current target!"))}
|
||||||
|
* >
|
||||||
|
* </btrix-element>
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
export function makeCurrentTargetHandler(t: EventTarget) {
|
||||||
|
const currentTargetHandler: <T extends Event = CustomEvent>(
|
||||||
|
handler: (event: T) => void,
|
||||||
|
) => (event: T) => void = (handler) => (e) => {
|
||||||
|
if (e.target === e.currentTarget) {
|
||||||
|
handler.bind(t)(e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return currentTargetHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop propgation shorthand.
|
||||||
|
*
|
||||||
|
* @example Usage:
|
||||||
|
* ```
|
||||||
|
* <btrix-element
|
||||||
|
* @sl-show=${stopProp}
|
||||||
|
* >
|
||||||
|
* </btrix-element>
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
export function stopProp(e: Event) {
|
||||||
|
e.stopPropagation();
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user