Filter action url

commit_hash:fb613bcfef9a1aa33090dacc0cebc1e81d4762e9
This commit is contained in:
4eb0da
2025-09-15 11:28:09 +03:00
parent e1388296d8
commit da72c6cf16
7 changed files with 55 additions and 8 deletions
+1
View File
@@ -22987,6 +22987,7 @@
"visual-editor/src/lib/utils/capitalize.ts":"divkit/public/visual-editor/src/lib/utils/capitalize.ts",
"visual-editor/src/lib/utils/checkDivjson.test.ts":"divkit/public/visual-editor/src/lib/utils/checkDivjson.test.ts",
"visual-editor/src/lib/utils/checkDivjson.ts":"divkit/public/visual-editor/src/lib/utils/checkDivjson.ts",
"visual-editor/src/lib/utils/checkValue.ts":"divkit/public/visual-editor/src/lib/utils/checkValue.ts",
"visual-editor/src/lib/utils/colors.test.ts":"divkit/public/visual-editor/src/lib/utils/colors.test.ts",
"visual-editor/src/lib/utils/colors.ts":"divkit/public/visual-editor/src/lib/utils/colors.ts",
"visual-editor/src/lib/utils/componentIcon.ts":"divkit/public/visual-editor/src/lib/utils/componentIcon.ts",
+8
View File
@@ -62,6 +62,12 @@ export interface CardLocale {
text: Record<string, string>;
}
export type StringValueFilter = RegExp | ((value: string) => boolean);
export interface ValueFilters {
actionUrl?: StringValueFilter;
}
export interface EditorOptions {
node: HTMLElement;
shadowRoot?: ShadowRoot;
@@ -148,6 +154,7 @@ export interface DivProEditorOptions {
/* @deprecated */
errorFileLimit?: number;
fileLimits?: FileLimits;
valueFilters?: ValueFilters;
rootConfigurable?: boolean;
customFontFaces?: FontFaceDesc[];
directionSelector?: boolean;
@@ -193,6 +200,7 @@ export const DivProEditor = {
state.direction.set(opts.direction || 'ltr');
state.safeAreaEmulation = opts.safeAreaEmulation;
state.safeAreaEmulationEnabled.set(Boolean(opts.safeAreaEmulation));
state.valueFilters = opts.valueFilters;
if (Array.isArray(json?.card?.variables)) {
const localPalette = json.card.variables.find((it?: JsonVariable) => it?.type === 'dict' && it.name === 'local_palette');
@@ -16,6 +16,8 @@
import { formatFileSize } from '../../utils/formatFileSize';
import { calcFileSizeMod, getFileSize } from '../../utils/fileSize';
import type { VideoSource } from '../../utils/video';
import type { StringValueFilter } from '../../../lib';
import { checkStringValue } from '../../utils/checkValue';
export let id: string = '';
export let value: string | number | undefined;
@@ -34,6 +36,7 @@
export let size: 'medium' | 'small' = 'medium';
export let width: 'auto' | 'small' = 'auto';
export let pattern: string | null = null;
export let filter: StringValueFilter | undefined = undefined;
export let error = '';
export let title = '';
export let generateFromVideo: VideoSource[] | undefined = undefined;
@@ -54,11 +57,18 @@
let elem: HTMLElement;
let subtypeError = false;
let requiredError = false;
let filterError = false;
let isFocused = false;
let currentSize: number | undefined = undefined;
let sizeLabelWidth = 0;
let customButtonsWidth = 0;
function validateValue(value: string | number | undefined): void {
requiredError = !value && required;
filterError = !checkStringValue(value, filter);
}
function updateInternalValue(
subtype: string,
value: string | number | undefined,
@@ -80,7 +90,7 @@
loadFileSize();
requiredError = !internalValue && required;
validateValue(internalValue);
}
$: updateInternalValue(subtype, value, defaultValue);
@@ -135,7 +145,7 @@
loadFileSize();
requiredError = !value && required;
validateValue(value);
dispatch('change', {
value
@@ -194,7 +204,7 @@
class:text_inline-label={Boolean(inlineLabel)}
class:text_button={hasButton}
class:text_disabled={disabled}
class:text_error={Boolean(error || subtypeError || requiredError)}
class:text_error={Boolean(error || subtypeError || requiredError || filterError)}
title={error}
aria-label={title}
data-custom-tooltip={title}
@@ -36,7 +36,7 @@
const { l10n, lang } = getContext<LanguageContext>(LANGUAGE_CTX);
const { state } = getContext<AppContext>(APP_CTX);
const { customActions } = state;
const { customActions, valueFilters } = state;
$: if (isShown && !readOnly && callback) {
if (!value.log_url) {
@@ -224,6 +224,7 @@
<Text
bind:value={value.url}
disabled={readOnly}
filter={valueFilters?.actionUrl}
/>
</label>
</div>
@@ -22,27 +22,31 @@
import { LANGUAGE_CTX, type LanguageContext } from '../../ctx/languageContext';
import { parseAction } from '../../data/actions';
import { APP_CTX, type AppContext } from '../../ctx/appContext';
import { checkStringValue } from '../../utils/checkValue';
export let value: Action;
const { l10n, lang } = getContext<LanguageContext>(LANGUAGE_CTX);
const { state, actions2Dialog } = getContext<AppContext>(APP_CTX);
const { customActions, readOnly } = state;
const { customActions, readOnly, valueFilters } = state;
const dispatch = createEventDispatcher();
let elem: HTMLElement;
let type = '';
let text = '';
let hasError = false;
function updateVal(value: Action): void {
const parsed = parseAction(value, $customActions);
type = '';
hasError = false;
if (parsed.type === 'url') {
type = $l10n('actions-url');
text = parsed.url || '';
hasError = !checkStringValue(parsed.url, valueFilters?.actionUrl);
} else if (parsed.type.startsWith('typed:')) {
if (parsed.type === 'typed:focus_element') {
type = $l10n('actions.focus_element');
@@ -133,6 +137,7 @@
<button
class="actions2-item"
class:actions2-item_readonly={$readOnly}
class:actions2-item_error={hasError}
bind:this={elem}
on:click={onClick}
>
@@ -164,11 +169,15 @@
background: var(--fill-transparent-1);
}
.actions2-item:not(.actions2-item_readonly):hover {
.actions2-item_error {
border-color: var(--accent-red);
}
.actions2-item:not(.actions2-item_readonly):not(.actions2-item_error):hover {
border-color: var(--fill-transparent-4);
}
.actions2-item:not(.actions2-item_readonly):focus-visible {
.actions2-item:not(.actions2-item_readonly):not(.actions2-item_error):focus-visible {
outline: none;
border-color: var(--accent-purple);
}
+3 -1
View File
@@ -6,7 +6,7 @@ import { parseExpression, walkExpression } from '@divkitframework/divkit/client-
import type { PaletteItem } from './palette';
import { parseVariableValue, type JsonVariable, type Variable } from './customVariables';
import { type JsonTimer, type Timer } from './timers';
import type { ActionDesc, EditorError, FileLimits, GetTranslationKey, Locale, SafeAreaEmulation, Source, TankerMeta } from '../../lib';
import type { ActionDesc, EditorError, FileLimits, GetTranslationKey, Locale, SafeAreaEmulation, Source, TankerMeta, ValueFilters } from '../../lib';
import type { TreeLeaf } from '../ctx/tree';
import type { BaseCommand } from './commands/base';
import { namedTemplates } from './templates';
@@ -152,6 +152,8 @@ export class State {
safeAreaEmulation?: SafeAreaEmulation;
safeAreaEmulationEnabled = writable(false);
valueFilters?: ValueFilters;
private getTranslationKey: GetTranslationKey | undefined;
lang: Writable<Locale> = writable('en');
+16
View File
@@ -0,0 +1,16 @@
import type { StringValueFilter } from '../../lib';
export function checkStringValue(
value: string | number | undefined,
filter?: StringValueFilter
): boolean {
if (!value) {
return true;
} else if (typeof filter === 'function') {
return filter(String(value));
} else if (filter instanceof RegExp) {
return filter.test(String(value));
}
return true;
}