diff --git a/client/web/divkit/README.md b/client/web/divkit/README.md index e7247ab32..41df31df1 100644 --- a/client/web/divkit/README.md +++ b/client/web/divkit/README.md @@ -446,6 +446,22 @@ render(({ }); ``` +#### setData (EXPERIMENTAL) + +This methods allows you to quckly redraw the DivKit card: + +```ts +instance.setData({ + ... +}); +``` + +Some notes: + +* This method is experimental, its logic and api may be changed the future versions. +* `setData` doesn't change any existing variables or variable triggers. It will onky create new variables. This is intentional. +* This method will try to do as little work as possible and will try to preserve the state of the card (input values and so on). + ### Note about 64-bit integers DivKit internally uses the `BigInt` type if the current platform supports it (both client-side and server-side). This means that on unsupported platforms, integers will lose precision if the value exceeds 2^53. diff --git a/client/web/divkit/src/client-devtool.ts b/client/web/divkit/src/client-devtool.ts index 8c8f133c2..de0da9c0d 100644 --- a/client/web/divkit/src/client-devtool.ts +++ b/client/web/divkit/src/client-devtool.ts @@ -1,9 +1,64 @@ import type { Variable } from './expressions/variable'; import type { Node } from './expressions/ast'; -import type { WrappedError } from '../typings/common'; +import type { ComponentCallback, CustomActionCallback, Customization, DivExtensionClass, DivJson, ErrorCallback, FetchInit, Platform, StatCallback, Theme, TypefaceProvider, WrappedError } from '../typings/common'; +import type { GlobalVariablesController } from './expressions/globalVariablesController'; import { parse } from './expressions/expressions'; import { evalExpression as evalExpressionInner, EvalResult } from './expressions/eval'; import { funcs } from './expressions/funcs/funcs'; +import { CustomComponentDescription } from '../typings/custom'; +import { DivkitDebugInstance } from '../typings/client-devtool'; +import Root from './components/Root.svelte'; + +export function render(opts: { + target: HTMLElement; + json: DivJson; + id: string; + hydrate?: boolean; + globalVariablesController?: GlobalVariablesController; + mix?: string; + customization?: Customization; + builtinProtocols?: string[]; + extensions?: Map; + onStat?: StatCallback; + onCustomAction?: CustomActionCallback; + onError?: ErrorCallback; + onComponent?: ComponentCallback; + typefaceProvider?: TypefaceProvider; + platform?: Platform; + theme?: Theme; + fetchInit?: FetchInit; + tooltipRoot?: HTMLElement; + customComponents?: Map | undefined; +}): DivkitDebugInstance { + const { target, hydrate, ...rest } = opts; + + const instance = new Root({ + target: target, + props: rest, + hydrate: hydrate + }); + + return { + $destroy() { + instance.$destroy(); + }, + execAction(action) { + instance.execAction(action); + }, + setTheme(theme) { + instance.setTheme(theme); + }, + setData(newJson) { + instance.setData(newJson); + }, + getDebugVariables() { + return instance.getDebugVariables(); + }, + getDebugAllVariables() { + return instance.getDebugAllVariables(); + } + }; +} export * from './client'; @@ -43,3 +98,13 @@ export { valToString } from './expressions/utils'; export function functionNames(): string[] { return Array.from(funcs.keys()); } + +export function parseExpression(expr: string, opts?: { + type?: 'exact' | 'json'; +}): Node { + return parse(expr, { + startRule: opts?.type === 'json' ? 'JsonStringContents' : 'start' + }); +} + +export { walk as walkExpression } from './expressions/walk'; diff --git a/client/web/divkit/src/client.ts b/client/web/divkit/src/client.ts index 5d3cb4ede..0552e642a 100644 --- a/client/web/divkit/src/client.ts +++ b/client/web/divkit/src/client.ts @@ -5,6 +5,7 @@ import type { Customization, DivExtensionClass, DivJson, + DivkitInstance, ErrorCallback, FetchInit, Platform, @@ -35,14 +36,29 @@ export function render(opts: { fetchInit?: FetchInit; tooltipRoot?: HTMLElement; customComponents?: Map | undefined; -}) { +}): DivkitInstance { const { target, hydrate, ...rest } = opts; - return new Root({ + const instance = new Root({ target: target, props: rest, hydrate: hydrate }); + + return { + $destroy() { + instance.$destroy(); + }, + execAction(action) { + instance.execAction(action); + }, + setTheme(theme) { + instance.setTheme(theme); + }, + setData(newJson) { + instance.setData(newJson); + } + }; } export { diff --git a/client/web/divkit/src/components/Root.svelte b/client/web/divkit/src/components/Root.svelte index 3cc701d9c..a47f1bea9 100644 --- a/client/web/divkit/src/components/Root.svelte +++ b/client/web/divkit/src/components/Root.svelte @@ -37,7 +37,8 @@ DivExtensionClass, TypefaceProvider, DisappearAction, - FetchInit + FetchInit, + DivVariable } from '../../typings/common'; import type { CustomComponentDescription } from '../../typings/custom'; import type { AppearanceTransition, DivBaseData, Tooltip, TransitionChange } from '../types/base'; @@ -137,34 +138,46 @@ export function getDebugVariables() { if (!process.env.DEVTOOL) { - return; + return new Map(); } return localVariables; } + export function getDebugAllVariables() { + if (!process.env.DEVTOOL) { + return new Map(); + } + + return variables; + } + + export function setData(newJson: Partial) { + json = newJson; + } + const builtinSet = new Set(builtinProtocols); let hasError = false; + let hsaIdError = false; - if (!json) { - hasError = true; - logError(wrapError(new Error('"json" prop is required'))); + $: { + hasError = false; + + const initialError = simpleCheckInput(json); + if (initialError) { + hasError = true; + logError(initialError); + } } if (!id) { - hasError = true; + hsaIdError = true; logError(wrapError(new Error('"id" prop is required'))); } - let initialError = simpleCheckInput(json); let templateContext: TemplateContext = {}; - const templates = json.templates || {}; - - if (initialError) { - hasError = true; - logError(initialError); - } + $: templates = json.templates || {}; const running: Record = { stateChange: false @@ -302,12 +315,14 @@ }; } - function registerComponentReal({ + function componentDevtoolReal({ + type, node, json, origJson, templateContext }: { + type: 'mount' | 'update' | 'destroy'; node: HTMLElement; json: Partial; origJson: DivBase | undefined; @@ -315,7 +330,7 @@ }): void { if (onComponent) { onComponent({ - type: 'mount', + type, node, json: json as DivBase, origJson, @@ -323,18 +338,6 @@ }); } } - function unregisterComponentReal({ - node - }: { - node: HTMLElement; - }): void { - if (onComponent) { - onComponent({ - type: 'destroy', - node - }); - } - } let idCounter = 0; function genId(key: string): string { @@ -1069,8 +1072,7 @@ isDesktop, isPointerFocus, customComponents, - registerComponent: process.env.DEVTOOL ? registerComponentReal : undefined, - unregisterComponent: process.env.DEVTOOL ? unregisterComponentReal : undefined + componentDevtool: process.env.DEVTOOL ? componentDevtoolReal : undefined }); setContext(ACTION_CTX, { @@ -1181,10 +1183,43 @@ } } + function declVariable(variable: DivVariable): void { + if (!(variable.type in TYPE_TO_CLASS)) { + // Skip unknown types (from the future versions maybe) + return; + } + + if ( + variable.type === 'integer' && typeof variable.value === 'number' && + (variable.value > Number.MAX_SAFE_INTEGER || variable.value < Number.MIN_SAFE_INTEGER) + ) { + logError(wrapError(new Error('The value of the integer variable could lose accuracy'), { + level: 'warn', + additional: { + name: variable.name, + value: variable.value + } + })); + } + + try { + const varInstance = createVariable(variable.name, variable.type, variable.value); + + localVariables.set(variable.name, varInstance); + variables.set(variable.name, varInstance); + } catch (err: any) { + logError(wrapError(err, { + additional: { + name: variable.name + } + })); + } + } + const startVariables = json?.card?.variables; if (Array.isArray(startVariables)) { startVariables.forEach(variable => { - if (variable && variable.type in TYPE_TO_CLASS && variable.name) { + if (variable && variable.name) { if (localVariables.has(variable.name)) { logError(wrapError(new Error('Duplicate variable'), { additional: { @@ -1195,31 +1230,15 @@ return; } - if ( - variable.type === 'integer' && typeof variable.value === 'number' && - (variable.value > Number.MAX_SAFE_INTEGER || variable.value < Number.MIN_SAFE_INTEGER) - ) { - logError(wrapError(new Error('The value of the integer variable could lose accuracy'), { - level: 'warn', - additional: { - name: variable.name, - value: variable.value - } - })); - } + declVariable(variable); + } + }); + } - try { - const varInstance = createVariable(variable.name, variable.type, variable.value); - - localVariables.set(variable.name, varInstance); - variables.set(variable.name, varInstance); - } catch (err: any) { - logError(wrapError(err, { - additional: { - name: variable.name - } - })); - } + $: if (json?.card?.variables && Array.isArray(json.card.variables) && json.card.variables !== startVariables) { + json.card.variables.forEach(variable => { + if (variable && variable.name && !localVariables.has(variable.name)) { + declVariable(variable); } }); } @@ -1376,9 +1395,9 @@ timers.forEach(timer => controller.createTimer(timer)); } - const states = json?.card?.states; + $: states = json?.card?.states; let rootStateDiv: DivBaseData | undefined; - if (states && !hasError) { + $: if (states && !hasError && !hsaIdError) { rootStateDiv = (process.env.ENABLE_COMPONENT_STATE || process.env.ENABLE_COMPONENT_STATE === undefined) ? { type: 'state', id: 'root', @@ -1435,7 +1454,7 @@ }); -{#if !hasError && rootStateDiv} +{#if !hasError && !hsaIdError && rootStateDiv}
- import { getContext } from 'svelte'; - import { Readable, derived } from 'svelte/store'; - - import css from './Container.module.css'; - - import type { ContainerOrientation, DivContainerData } from '../../types/container'; - import type { LayoutParams } from '../../types/layoutParams'; - import type { DivBase, TemplateContext } from '../../../typings/common'; - import type { DivBaseData } from '../../types/base'; - import type { - ContentAlignmentHorizontal, - ContentAlignmentVertical - } from '../../types/alignment'; - import type { ContainerChildInfo, SeparatorStyle } from '../../utils/container'; - import { prepareMargins } from '../../utils/container'; - import { ROOT_CTX, RootCtxValue } from '../../context/root'; - import { wrapError } from '../../utils/wrapError'; - import { genClassName } from '../../utils/genClassName'; - import { correctContainerOrientation } from '../../utils/correctContainerOrientation'; - import { assignIfDifferent } from '../../utils/assignIfDifferent'; - import { correctDrawableStyle, DrawableStyle } from '../../utils/correctDrawableStyles'; - import { calcAdditionalPaddings, calcItemsGap, hasKnownHeightCheck, hasKnownWidthCheck } from '../../utils/container'; - import { hasGapSupport } from '../../utils/hasGapSupport'; - import { isPositiveNumber } from '../../utils/isPositiveNumber'; - import ContainerSeparators from './ContainerSeparators.svelte'; - import Unknown from '../utilities/Unknown.svelte'; - import Outer from '../utilities/Outer.svelte'; - import { correctContentAlignmentVertical } from '../../utils/correctContentAlignmentVertical'; - import { correctContentAlignmentHorizontal } from '../../utils/correctContentAlignmentHorizontal'; - import { Truthy } from '../../utils/truthy'; - - export let json: Partial = {}; - export let templateContext: TemplateContext; - export let origJson: DivBase | undefined = undefined; - export let layoutParams: LayoutParams | undefined = undefined; - + + + + @@ -451,24 +473,22 @@ class={css['gallery__items-grid']} style={makeStyle(gridStyle)} > - {#key itemsGrid} - {#each itemsGrid as itemsRow, rowIndex} -
- {#each itemsRow as item} - - {/each} -
- {/each} - {/key} + {#each itemsGrid as itemsRow, rowIndex} +
+ {#each itemsRow as item} + + {/each} +
+ {/each}
{#if orientation === 'horizontal'} diff --git a/client/web/divkit/src/components/grid/Grid.svelte b/client/web/divkit/src/components/grid/Grid.svelte index fdab40b9e..e887c9e62 100644 --- a/client/web/divkit/src/components/grid/Grid.svelte +++ b/client/web/divkit/src/components/grid/Grid.svelte @@ -30,7 +30,35 @@ const rootCtx = getContext(ROOT_CTX); let hasItemsError = false; + let columnCount = 1; + let childStore: Readable; + let resultItems: { + json: DivBaseData; + templateContext: TemplateContext; + origJson: DivBaseData; + layoutParams: LayoutParams; + }[]; + let columnsWeight: number[] = []; + let rowsWeight: number[] = []; + let columnsMinWidth: number[] = []; + let rowsMinHeight: number[] = []; + let rowCount = 0; + let hasLayoutError = false; + let contentVAlign: AlignmentVertical = 'top'; + let contentHAlign: AlignmentHorizontal = 'left'; + + $: if (json) { + columnCount = 1; + contentVAlign = 'top'; + contentHAlign = 'left'; + } + $: jsonItems = json.items; + + $: jsonColumnCount = rootCtx.getDerivedFromVars(json.column_count); + $: jsonContentVAlign = rootCtx.getDerivedFromVars(json.content_alignment_vertical); + $: jsonContentHAlign = rootCtx.getDerivedFromVars(json.content_alignment_horizontal); + $: { if (!jsonItems?.length || !Array.isArray(jsonItems)) { hasItemsError = true; @@ -40,9 +68,6 @@ } } - - let columnCount = 1; - $: jsonColumnCount = rootCtx.getDerivedFromVars(json.column_count); $: { columnCount = correctPositiveNumber($jsonColumnCount, columnCount); } @@ -76,7 +101,6 @@ width: MaybeMissing | undefined; height: MaybeMissing | undefined; } - let childStore: Readable; $: { let children: Readable[] = []; @@ -95,18 +119,6 @@ childStore = derived(children, val => [...val]); } - let resultItems: { - json: DivBaseData; - templateContext: TemplateContext; - origJson: DivBaseData; - layoutParams: LayoutParams; - }[]; - let columnsWeight: number[] = []; - let rowsWeight: number[] = []; - let columnsMinWidth: number[] = []; - let rowsMinHeight: number[] = []; - let rowCount = 0; - let hasLayoutError = false; $: { const used: Record = {}; let x = 0; @@ -202,14 +214,10 @@ rowCount = y; } - let contentVAlign: AlignmentVertical = 'top'; - $: jsonContentVAlign = rootCtx.getDerivedFromVars(json.content_alignment_vertical); $: { contentVAlign = correctAlignmentVertical($jsonContentVAlign, contentVAlign); } - let contentHAlign: AlignmentHorizontal = 'left'; - $: jsonContentHAlign = rootCtx.getDerivedFromVars(json.content_alignment_horizontal); $: { contentHAlign = correctAlignmentHorizontal($jsonContentHAlign, contentHAlign); } @@ -238,15 +246,13 @@ parentOf={json.items} {replaceItems} > - {#key resultItems} - {#each resultItems as item} - - {/each} - {/key} + {#each resultItems as item} + + {/each} {/if} diff --git a/client/web/divkit/src/components/image/Image.svelte b/client/web/divkit/src/components/image/Image.svelte index ff4b60f2e..05374fa35 100644 --- a/client/web/divkit/src/components/image/Image.svelte +++ b/client/web/divkit/src/components/image/Image.svelte @@ -1,3 +1,14 @@ + + + + diff --git a/client/web/divkit/src/components/pager/Pager.svelte b/client/web/divkit/src/components/pager/Pager.svelte index 7095ef883..b5756468d 100644 --- a/client/web/divkit/src/components/pager/Pager.svelte +++ b/client/web/divkit/src/components/pager/Pager.svelte @@ -38,10 +38,38 @@ const rightClass = rootCtx.getCustomization('pagerRightClass'); const isDesktop = rootCtx.isDesktop; + + const onScrollDebounced = debounce(onScroll, 50); + + let prevId: string | undefined; + let pagerItemsWrapper: HTMLElement; let mounted = false; let hasItemsError = false; + + let currentItem = 0; + let prevSelectedItem = 0; + + let hasLayoutModeError = false; + + let orientation: Orientation = 'horizontal'; + let itemSpacing = ''; + let padding = ''; + let sizeVal = ''; + + $: if (json) { + itemSpacing = ''; + padding = ''; + sizeVal = ''; + } + + $: jsonLayoutMode = rootCtx.getDerivedFromVars(json.layout_mode); + $: jsonOrientation = rootCtx.getDerivedFromVars(json.orientation); + $: jsonItemSpacing = rootCtx.getDerivedFromVars(json.item_spacing); + $: jsonPaddings = rootCtx.getDerivedFromVars(json.paddings); + $: jsonRestrictParentScroll = rootCtx.getDerivedFromVars(json.restrict_parent_scroll); + $: { if (!json.items?.length || !Array.isArray(json.items)) { hasItemsError = true; @@ -73,12 +101,6 @@ origJson: item }; }); - - let currentItem = 0; - let prevSelectedItem = 0; - - let hasLayoutModeError = false; - $: jsonLayoutMode = rootCtx.getDerivedFromVars(json.layout_mode); $: { if (!$jsonLayoutMode) { hasLayoutModeError = true; @@ -91,14 +113,10 @@ } } - let orientation: Orientation = 'horizontal'; - $: jsonOrientation = rootCtx.getDerivedFromVars(json.orientation); $: { orientation = correctGeneralOrientation($jsonOrientation, orientation); } - let itemSpacing = ''; - $: jsonItemSpacing = rootCtx.getDerivedFromVars(json.item_spacing); $: { const val = $jsonItemSpacing?.value; if (val && isNonNegativeNumber(val)) { @@ -106,16 +124,11 @@ } } - $: jsonPaddings = rootCtx.getDerivedFromVars(json.paddings); - let padding = ''; $: { padding = correctEdgeInserts($jsonPaddings, padding); } - $: jsonRestrictParentScroll = rootCtx.getDerivedFromVars(json.restrict_parent_scroll); - $: gridAuto = orientation === 'horizontal' ? 'grid-auto-columns' : 'grid-auto-rows'; - let sizeVal = ''; $: { if ($jsonLayoutMode?.type === 'fixed') { @@ -185,8 +198,6 @@ } } - const onScrollDebounced = debounce(onScroll, 50); - $: pagers = rootCtx.getStore>('pagers'); function pagerDataUpdate(size: number, currentItem: number): void { @@ -248,18 +259,26 @@ scrollToPagerItem(nextItem); } - if (json.id && !layoutParams?.fakeElement) { - rootCtx.registerInstance(json.id, { - setCurrentItem(item: number) { - if (item < 0 || item > items.length - 1) { - throw new Error('Item is out of range in "set-current-item" action'); - } + $: if (json) { + if (prevId) { + rootCtx.unregisterInstance(prevId); + prevId = undefined; + } - scrollToPagerItem(item); - }, - setPreviousItem, - setNextItem - }); + if (json.id && !layoutParams?.fakeElement) { + prevId = json.id; + rootCtx.registerInstance(json.id, { + setCurrentItem(item: number) { + if (item < 0 || item > items.length - 1) { + throw new Error('Item is out of range in "set-current-item" action'); + } + + scrollToPagerItem(item); + }, + setPreviousItem, + setNextItem + }); + } } onMount(() => { @@ -281,8 +300,9 @@ onDestroy(() => { mounted = false; - if (json.id && !layoutParams?.fakeElement) { - rootCtx.unregisterInstance(json.id); + if (prevId) { + rootCtx.unregisterInstance(prevId); + prevId = undefined; } }); @@ -304,18 +324,16 @@ bind:this={pagerItemsWrapper} on:scroll={onScrollDebounced} > - {#key items} - {#each items as item} -
- -
- {/each} - {/key} + {#each items as item} +
+ +
+ {/each} {#if hasScrollLeft && shouldCheckArrows} diff --git a/client/web/divkit/src/components/select/Select.svelte b/client/web/divkit/src/components/select/Select.svelte index 2e58cb71b..318950670 100644 --- a/client/web/divkit/src/components/select/Select.svelte +++ b/client/web/divkit/src/components/select/Select.svelte @@ -29,28 +29,61 @@ const rootCtx = getContext(ROOT_CTX); - const variable = json.value_variable; + let prevId: string | undefined; let select: HTMLSelectElement; - let hasError = false; - if (!variable) { + let selectText = ''; + let selfPadding: EdgeInsets | null = null; + let padding = ''; + let hintColor = 'rgba(0,0,0,.45)'; + let fontSize = 12; + let fontWeight: number | undefined = undefined; + let fontFamily = ''; + let lineHeight: number | undefined = undefined; + let letterSpacing = ''; + let textColor = '#000'; + let description = ''; + + $: if (json) { + selfPadding = null; + hintColor = 'rgba(0,0,0,.45)'; + fontSize = 12; + fontWeight = undefined; + fontFamily = ''; + lineHeight = undefined; + letterSpacing = ''; + textColor = '#000'; + description = ''; + } + + $: variable = json.value_variable; + $: items = json.options; + $: filteredItems = Array.isArray(items) && items.filter(it => typeof it.value === 'string') || []; + + $: valueVariable = variable && rootCtx.getVariable(variable, 'string') || createVariable('temp', 'string', ''); + + $: jsonPaddings = rootCtx.getDerivedFromVars(json.paddings); + $: jsonHintText = rootCtx.getDerivedFromVars(json.hint_text); + $: jsonHintColor = rootCtx.getDerivedFromVars(json.hint_color); + $: jsonFontSize = rootCtx.getDerivedFromVars(json.font_size); + $: jsonFontWeight = rootCtx.getDerivedFromVars(json.font_weight); + $: jsonFontFamily = rootCtx.getDerivedFromVars(json.font_family); + $: jsonLineHeight = rootCtx.getDerivedFromVars(json.line_height); + $: jsonLetterSpacing = rootCtx.getDerivedFromVars(json.letter_spacing); + $: jsonTextColor = rootCtx.getDerivedFromVars(json.text_color); + $: jsonAccessibility = rootCtx.getDerivedFromVars(json.accessibility); + + $: if (!(Array.isArray(filteredItems) && filteredItems.length)) { + rootCtx.logError(wrapError(new Error('Empty selection "items" in "select"'))); + } + + $: if (variable) { + hasError = false; + } else { hasError = true; rootCtx.logError(wrapError(new Error('Missing "value_variable" in "select"'))); } - let valueVariable = variable && rootCtx.getVariable(variable, 'string') || createVariable('temp', 'string', ''); - let value = ''; - $: { - value = $valueVariable as string; - } - - const items = json.options; - const filteredItems = Array.isArray(items) && items.filter(it => typeof it.value === 'string') || []; - if (!(Array.isArray(filteredItems) && filteredItems.length)) { - rootCtx.logError(wrapError(new Error('Empty selection "items" in "select"'))); - } - - let selectText = ''; $: { const item = filteredItems.find(it => { return it.value === $valueVariable; @@ -65,9 +98,6 @@ } } - $: jsonPaddings = rootCtx.getDerivedFromVars(json.paddings); - let selfPadding: EdgeInsets | null = null; - let padding = ''; $: { selfPadding = correctEdgeInsertsObject(($jsonPaddings) ? $jsonPaddings : undefined, selfPadding); padding = selfPadding ? edgeInsertsToCss({ @@ -78,25 +108,14 @@ }) : ''; } - const jsonHintText = rootCtx.getDerivedFromVars(json.hint_text); - $: hint = $jsonHintText; - - const jsonHintColor = rootCtx.getDerivedFromVars(json.hint_color); - let hintColor = 'rgba(0,0,0,.45)'; $: { hintColor = correctColor($jsonHintColor, 1, hintColor); } - const jsonFontSize = rootCtx.getDerivedFromVars(json.font_size); - let fontSize = 12; $: { fontSize = correctPositiveNumber($jsonFontSize, fontSize); } - const jsonFontWeight = rootCtx.getDerivedFromVars(json.font_weight); - const jsonFontFamily = rootCtx.getDerivedFromVars(json.font_family); - let fontWeight: number | undefined = undefined; - let fontFamily = ''; $: { fontWeight = correctFontWeight($jsonFontWeight, fontWeight); if ($jsonFontFamily && typeof $jsonFontFamily === 'string') { @@ -108,8 +127,6 @@ } } - const jsonLineHeight = rootCtx.getDerivedFromVars(json.line_height); - let lineHeight: number | undefined = undefined; $: { const val = $jsonLineHeight; if (isPositiveNumber(val)) { @@ -117,22 +134,16 @@ } } - const jsonLetterSpacing = rootCtx.getDerivedFromVars(json.letter_spacing); - let letterSpacing = ''; $: { if (isNumber($jsonLetterSpacing)) { letterSpacing = pxToEm($jsonLetterSpacing / fontSize * 10); } } - const jsonTextColor = rootCtx.getDerivedFromVars(json.text_color); - let textColor = '#000'; $: { textColor = correctColor($jsonTextColor, 1, textColor); } - $: jsonAccessibility = rootCtx.getDerivedFromVars(json.accessibility); - let description = ''; $: if ($jsonAccessibility?.description) { description = $jsonAccessibility.description; } else { @@ -162,8 +173,14 @@ 'letter-spacing': letterSpacing }; - onMount(() => { - if (json.id) { + $: if (json && select) { + if (prevId) { + rootCtx.unregisterFocusable(prevId); + prevId = undefined; + } + + if (json.id && !layoutParams?.fakeElement) { + prevId = json.id; rootCtx.registerFocusable(json.id, { focus() { if (select) { @@ -172,11 +189,12 @@ } }); } - }); + } onDestroy(() => { - if (json.id) { - rootCtx.unregisterFocusable(json.id); + if (prevId) { + rootCtx.unregisterFocusable(prevId); + prevId = undefined; } }); @@ -199,7 +217,7 @@ >