setData method

This commit is contained in:
4eb0da
2023-12-12 17:52:07 +03:00
parent 188290c915
commit bade1b43f3
28 changed files with 1521 additions and 1018 deletions
+16
View File
@@ -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.
+66 -1
View File
@@ -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<string, DivExtensionClass>;
onStat?: StatCallback;
onCustomAction?: CustomActionCallback;
onError?: ErrorCallback;
onComponent?: ComponentCallback;
typefaceProvider?: TypefaceProvider;
platform?: Platform;
theme?: Theme;
fetchInit?: FetchInit;
tooltipRoot?: HTMLElement;
customComponents?: Map<string, CustomComponentDescription> | 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';
+18 -2
View File
@@ -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<string, CustomComponentDescription> | 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 {
+76 -57
View File
@@ -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<string, Variable>();
}
return localVariables;
}
export function getDebugAllVariables() {
if (!process.env.DEVTOOL) {
return new Map<string, Variable>();
}
return variables;
}
export function setData(newJson: Partial<DivJson>) {
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<Running, boolean> = {
stateChange: false
@@ -302,12 +315,14 @@
};
}
function registerComponentReal({
function componentDevtoolReal({
type,
node,
json,
origJson,
templateContext
}: {
type: 'mount' | 'update' | 'destroy';
node: HTMLElement;
json: Partial<DivBaseData>;
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<ActionCtxValue>(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 @@
});
</script>
{#if !hasError && rootStateDiv}
{#if !hasError && !hsaIdError && rootStateDiv}
<div
class="{css.root}{$isDesktop ? ` ${css.root_platform_desktop}` : ''}{mix ? ` ${mix}` : ''}"
on:touchstart={emptyTouchstartHandler}
@@ -1,40 +1,4 @@
<script lang="ts">
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<DivContainerData> = {};
export let templateContext: TemplateContext;
export let origJson: DivBase | undefined = undefined;
export let layoutParams: LayoutParams | undefined = undefined;
<script lang="ts" context="module">
const HALIGN_MAP = {
left: 'start',
center: 'center',
@@ -56,12 +20,78 @@
'space-evenly': 'start'
} as const;
const AVAIL_SEPARATOR_SHAPES = ['rounded_rectangle', 'circle'];
const AVAIL_SEPARATOR_SHAPES = [
'rounded_rectangle',
'circle'
];
</script>
<script lang="ts">
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 { 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<DivContainerData> = {};
export let templateContext: TemplateContext;
export let origJson: DivBase | undefined = undefined;
export let layoutParams: LayoutParams | undefined = undefined;
const rootCtx = getContext<RootCtxValue>(ROOT_CTX);
let childStore: Readable<ContainerChildInfo[]>;
let orientation: ContainerOrientation = 'vertical';
let contentVAlign: ContentAlignmentVertical = 'top';
let contentHAlign: ContentAlignmentHorizontal = 'left';
let separator: SeparatorStyle | null = null;
let lineSeparator: SeparatorStyle | null = null;
let aspect: number | undefined = undefined;
let childLayoutParams: LayoutParams = {};
$: if (json) {
orientation = 'vertical';
contentVAlign = 'top';
contentHAlign = 'left';
aspect = undefined;
}
$: jsonItems = json.items;
$: jsonOrientation = rootCtx.getDerivedFromVars(json.orientation);
$: jsonLayoutMode = rootCtx.getDerivedFromVars(json.layout_mode);
$: jsonContentVAlign = rootCtx.getDerivedFromVars(json.content_alignment_vertical);
$: jsonContentHAlign = rootCtx.getDerivedFromVars(json.content_alignment_horizontal);
$: jsonSeparator = rootCtx.getDerivedFromVars(json.separator);
$: jsonLineSeparator = rootCtx.getDerivedFromVars(json.line_separator);
$: jsonAspect = rootCtx.getDerivedFromVars(json.aspect);
$: jsonWidth = rootCtx.getDerivedFromVars(json.width);
$: jsonHeight = rootCtx.getDerivedFromVars(json.height);
function replaceItems(items: (DivBaseData | undefined)[]): void {
json = {
...json,
@@ -85,7 +115,6 @@
};
});
let childStore: Readable<ContainerChildInfo[]>;
$: {
let children: Readable<ContainerChildInfo>[] = [];
@@ -102,32 +131,23 @@
childStore = derived(children, val => [...val]);
}
let orientation: ContainerOrientation = 'vertical';
$: jsonOrientation = rootCtx.getDerivedFromVars(json.orientation);
$: {
orientation = correctContainerOrientation($jsonOrientation, orientation);
}
$: jsonLayoutMode = rootCtx.getDerivedFromVars(json.layout_mode);
$: wrap = $jsonLayoutMode === 'wrap';
$: hasKnownWidth = orientation !== 'horizontal' && !wrap && $childStore.some(hasKnownWidthCheck);
$: hasKnownHeight = orientation !== 'vertical' && !wrap && $childStore.some(hasKnownHeightCheck);
let contentVAlign: ContentAlignmentVertical = 'top';
$: jsonContentVAlign = rootCtx.getDerivedFromVars(json.content_alignment_vertical);
$: {
contentVAlign = correctContentAlignmentVertical($jsonContentVAlign, contentVAlign);
}
let contentHAlign: ContentAlignmentHorizontal = 'left';
$: jsonContentHAlign = rootCtx.getDerivedFromVars(json.content_alignment_horizontal);
$: {
contentHAlign = correctContentAlignmentHorizontal($jsonContentHAlign, contentHAlign);
}
$: jsonSeparator = rootCtx.getDerivedFromVars(json.separator);
let separator: SeparatorStyle | null = null;
$: {
if ($jsonSeparator?.style && orientation !== 'overlap' && hasGapSupport()) {
const style = correctDrawableStyle<DrawableStyle | null>(
@@ -152,8 +172,6 @@
}
}
$: jsonLineSeparator = rootCtx.getDerivedFromVars(json.line_separator);
let lineSeparator: SeparatorStyle | null = null;
$: {
if ($jsonLineSeparator?.style && orientation !== 'overlap' && hasGapSupport()) {
const style = correctDrawableStyle<DrawableStyle | null>(
@@ -182,8 +200,6 @@
calcAdditionalPaddings(orientation, separator, lineSeparator) :
null;
$: jsonAspect = rootCtx.getDerivedFromVars(json.aspect);
let aspect: number | undefined = undefined;
$: {
const newRatio = $jsonAspect?.ratio;
if (newRatio && isPositiveNumber(newRatio)) {
@@ -193,10 +209,7 @@
}
}
$: jsonWidth = rootCtx.getDerivedFromVars(json.width);
$: jsonHeight = rootCtx.getDerivedFromVars(json.height);
let childLayoutParams: LayoutParams = {};
$: {
let newChildLayoutParams: LayoutParams = {};
@@ -269,16 +282,14 @@
parentOf={jsonItems}
{replaceItems}
>
{#key jsonItems}
{#each items as item}
<Unknown
layoutParams={childLayoutParams}
div={item.json}
templateContext={item.templateContext}
origJson={item.origJson}
/>
{/each}
{/key}
{#each items as item}
<Unknown
layoutParams={childLayoutParams}
div={item.json}
templateContext={item.templateContext}
origJson={item.origJson}
/>
{/each}
{#if separator || lineSeparator}
<ContainerSeparators
@@ -1,3 +1,7 @@
<script lang="ts" context="module">
const THROTTLE_TIMEOUT = 10;
</script>
<script lang="ts">
import { onDestroy, onMount } from 'svelte';
@@ -17,7 +21,6 @@
export let contentHAlign: ContentAlignmentHorizontal;
export let contentVAlign: ContentAlignmentVertical;
const THROTTLE_TIMEOUT = 10;
const throttledUpdated = simpleThrottle(updateSeparators, THROTTLE_TIMEOUT);
interface SeparatorItem {
@@ -50,8 +50,44 @@
const leftClass = rootCtx.getCustomization('galleryLeftClass');
const rightClass = rootCtx.getCustomization('galleryRightClass');
let prevId: string | undefined;
let hasError = false;
let columns = 1;
let orientation: Orientation = 'horizontal';
let align: Align = 'start';
let gridGap: string | undefined;
let itemSpacing = 8;
let crossGridGap: string | undefined;
let crossSpacing;
let padding = '';
let templateSizes: string[] = [];
let childStore: Readable<(MaybeMissing<Size> | undefined)[]>;
let scrollerStyle: Style = {};
let scrollSnap = false;
let childLayoutParams: LayoutParams = {};
let defaultItem = 0;
$: if (json) {
columns = 1;
orientation = 'horizontal';
align = 'start';
itemSpacing = 8;
padding = '';
}
$: jsonItems = json.items;
$: jsonColumnCount = rootCtx.getDerivedFromVars(json.column_count);
$: jsonOrientation = rootCtx.getDerivedFromVars(json.orientation);
$: jsonCrossContentAlignment = rootCtx.getDerivedFromVars(json.cross_content_alignment);
$: jsonItemSpacing = rootCtx.getDerivedFromVars(json.item_spacing);
$: jsonCrossSpacing = rootCtx.getDerivedFromVars(json.cross_spacing);
$: jsonPaddings = rootCtx.getDerivedFromVars(json.paddings);
$: jsonScrollMode = rootCtx.getDerivedFromVars(json.scroll_mode);
$: jsonRestrictParentScroll = rootCtx.getDerivedFromVars(json.restrict_parent_scroll);
$: jsonScrollbar = rootCtx.getDerivedFromVars(json.scrollbar);
$: jsonDefaultItem = rootCtx.getDerivedFromVars(json.default_item);
$: {
if (!jsonItems?.length || !Array.isArray(jsonItems)) {
hasError = true;
@@ -106,8 +142,6 @@
resizeObserver = null;
}
$: jsonColumnCount = rootCtx.getDerivedFromVars(json.column_count);
let columns = 1;
$: {
columns = correctPositiveNumber($jsonColumnCount, columns);
}
@@ -130,44 +164,29 @@
}
$: itemsGrid = rebuildItemsGrid(items, columns);
$: jsonOrientation = rootCtx.getDerivedFromVars(json.orientation);
let orientation: Orientation = 'horizontal';
$: {
orientation = correctGeneralOrientation($jsonOrientation, orientation);
}
let align: Align = 'start';
$: jsonCrossContentAlignment = rootCtx.getDerivedFromVars(json.cross_content_alignment);
$: {
align = correctAlignment($jsonCrossContentAlignment, align);
}
let gridGap: string | undefined;
let itemSpacing = 8;
$: jsonItemSpacing = rootCtx.getDerivedFromVars(json.item_spacing);
$: {
itemSpacing = correctNonNegativeNumber($jsonItemSpacing, itemSpacing);
gridGap = pxToEm(itemSpacing);
}
let crossGridGap: string | undefined;
let crossSpacing;
$: jsonCrossSpacing = rootCtx.getDerivedFromVars(json.cross_spacing);
$: {
crossSpacing = correctNonNegativeNumber($jsonCrossSpacing, itemSpacing);
crossGridGap = pxToEm(crossSpacing);
}
$: jsonPaddings = rootCtx.getDerivedFromVars(json.paddings);
let padding = '';
$: {
padding = correctEdgeInserts($jsonPaddings, padding);
}
$: gridTemplate = orientation === 'horizontal' ? 'grid-template-columns' : 'grid-template-rows';
let templateSizes: string[] = [];
let childStore: Readable<(MaybeMissing<Size> | undefined)[]>;
$: {
let children: Readable<MaybeMissing<Size> | undefined>[] = [];
@@ -194,10 +213,6 @@
}
}
let scrollerStyle: Style = {};
let scrollSnap = false;
$: jsonScrollMode = rootCtx.getDerivedFromVars(json.scroll_mode);
let childLayoutParams: LayoutParams = {};
$: {
const newScrollerStyle: Style = {};
let newChildLayoutParams: LayoutParams = {};
@@ -229,10 +244,6 @@
childLayoutParams = assignIfDifferent(newChildLayoutParams, childLayoutParams);
}
$: jsonRestrictParentScroll = rootCtx.getDerivedFromVars(json.restrict_parent_scroll);
$: jsonScrollbar = rootCtx.getDerivedFromVars(json.scrollbar);
$: gridStyle = {
padding,
'grid-gap': crossGridGap
@@ -249,14 +260,12 @@
scrollbar: $jsonScrollbar === 'auto' ? 'auto' : 'none'
};
$: jsonDefaultItem = rootCtx.getDerivedFromVars(json.default_item);
let defaultItem = 0;
$: {
defaultItem = correctNonNegativeNumber($jsonDefaultItem, defaultItem);
}
function updateArrowsVisibility(): void {
if (!scroller) {
if (!scroller || hasError) {
return;
}
@@ -270,6 +279,10 @@
const updateArrowsVisibilityDebounced = debounce(updateArrowsVisibility, 50);
$: if (json) {
updateArrowsVisibilityDebounced();
}
function scroll(type: 'left' | 'right'): void {
scroller.scroll({
left: scroller.scrollLeft + (scroller.offsetWidth * .75) * (type === 'right' ? 1 : -1),
@@ -358,50 +371,58 @@
return action === 'prev' ? 1 : galleryElements.length - 2;
}
if (json.id && !hasError && !layoutParams?.fakeElement) {
rootCtx.registerInstance<SwitchElements>(json.id, {
setCurrentItem(item: number) {
const galleryElements = getItems();
if (item < 0 || item > galleryElements.length - 1) {
throw new Error('Item is out of range in "set-current-item" action');
$: if (json) {
if (prevId) {
rootCtx.unregisterInstance(prevId);
prevId = undefined;
}
if (json.id && !hasError && !layoutParams?.fakeElement) {
prevId = json.id;
rootCtx.registerInstance<SwitchElements>(json.id, {
setCurrentItem(item: number) {
const galleryElements = getItems();
if (item < 0 || item > galleryElements.length - 1) {
throw new Error('Item is out of range in "set-current-item" action');
}
scrollToGalleryItem(galleryElements, item);
},
setPreviousItem(overflow: Overflow) {
const currentElementIndex = calculateCurrentElementIndex('prev');
const galleryElements = getItems();
let previousItem = currentElementIndex - 1;
if (previousItem < 0) {
previousItem = overflow === 'ring' ? galleryElements.length - 1 : currentElementIndex;
}
scrollToGalleryItem(galleryElements, previousItem);
},
setNextItem(overflow: Overflow) {
// Go to scroller start, if we reached right/bottom edge of scroller
const isEdgeScroll = orientation === 'horizontal' ? (
scroller.scrollLeft + scroller.offsetWidth === scroller.scrollWidth
) : (
scroller.scrollTop + scroller.offsetHeight === scroller.scrollHeight
);
const galleryElements = getItems();
if (isEdgeScroll && overflow === 'ring') {
scrollToGalleryItem(galleryElements, 0);
return;
}
const currentElementIndex = calculateCurrentElementIndex('next');
let nextItem = currentElementIndex + 1;
if (nextItem > galleryElements.length - 1) {
nextItem = overflow === 'ring' ? 0 : currentElementIndex;
}
scrollToGalleryItem(galleryElements, nextItem);
}
scrollToGalleryItem(galleryElements, item);
},
setPreviousItem(overflow: Overflow) {
const currentElementIndex = calculateCurrentElementIndex('prev');
const galleryElements = getItems();
let previousItem = currentElementIndex - 1;
if (previousItem < 0) {
previousItem = overflow === 'ring' ? galleryElements.length - 1 : currentElementIndex;
}
scrollToGalleryItem(galleryElements, previousItem);
},
setNextItem(overflow: Overflow) {
// Go to scroller start, if we reached right/bottom edge of scroller
const isEdgeScroll = orientation === 'horizontal' ? (
scroller.scrollLeft + scroller.offsetWidth === scroller.scrollWidth
) : (
scroller.scrollTop + scroller.offsetHeight === scroller.scrollHeight
);
const galleryElements = getItems();
if (isEdgeScroll && overflow === 'ring') {
scrollToGalleryItem(galleryElements, 0);
return;
}
const currentElementIndex = calculateCurrentElementIndex('next');
let nextItem = currentElementIndex + 1;
if (nextItem > galleryElements.length - 1) {
nextItem = overflow === 'ring' ? 0 : currentElementIndex;
}
scrollToGalleryItem(galleryElements, nextItem);
}
});
});
}
}
onMount(() => {
@@ -420,8 +441,9 @@
onDestroy(() => {
mounted = false;
if (json.id && !layoutParams?.fakeElement) {
rootCtx.unregisterInstance(json.id);
if (prevId && !layoutParams?.fakeElement) {
rootCtx.unregisterInstance(prevId);
prevId = undefined;
}
});
</script>
@@ -451,24 +473,22 @@
class={css['gallery__items-grid']}
style={makeStyle(gridStyle)}
>
{#key itemsGrid}
{#each itemsGrid as itemsRow, rowIndex}
<div
class={css.gallery__items}
style={makeStyle(columnStyle)}
bind:this={galleryItemsWrappers[rowIndex]}
>
{#each itemsRow as item}
<Unknown
layoutParams={childLayoutParams}
div={item.json}
templateContext={item.templateContext}
origJson={item.origJson}
/>
{/each}
</div>
{/each}
{/key}
{#each itemsGrid as itemsRow, rowIndex}
<div
class={css.gallery__items}
style={makeStyle(columnStyle)}
bind:this={galleryItemsWrappers[rowIndex]}
>
{#each itemsRow as item}
<Unknown
layoutParams={childLayoutParams}
div={item.json}
templateContext={item.templateContext}
origJson={item.origJson}
/>
{/each}
</div>
{/each}
</div>
</div>
{#if orientation === 'horizontal'}
@@ -30,7 +30,35 @@
const rootCtx = getContext<RootCtxValue>(ROOT_CTX);
let hasItemsError = false;
let columnCount = 1;
let childStore: Readable<ChildInfo[]>;
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<Size> | undefined;
height: MaybeMissing<Size> | undefined;
}
let childStore: Readable<ChildInfo[]>;
$: {
let children: Readable<ChildInfo>[] = [];
@@ -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<string, boolean> = {};
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}
<Unknown
layoutParams={item.layoutParams}
div={item.json}
templateContext={item.templateContext}
origJson={item.origJson}
/>
{/each}
{/key}
{#each resultItems as item}
<Unknown
layoutParams={item.layoutParams}
div={item.json}
templateContext={item.templateContext}
origJson={item.origJson}
/>
{/each}
</Outer>
{/if}
@@ -1,3 +1,14 @@
<script lang="ts" context="module">
const FALLBACK_IMAGE = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7';
const EMPTY_IMAGE = 'empty://';
// const DEFAULT_PLACEHOLDER_COLOR = correctColor('#14000000');
const DEFAULT_PLACEHOLDER_COLOR = 'rgba(0,0,0,0.08)';
const STATE_LOADING = 0;
const STATE_LOADED = 1;
const STATE_ERROR = 2;
</script>
<script lang="ts">
import { afterUpdate, getContext, onDestroy } from 'svelte';
@@ -27,15 +38,6 @@
export let origJson: DivBase | undefined = undefined;
export let layoutParams: LayoutParams | undefined = undefined;
const FALLBACK_IMAGE = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7';
const EMPTY_IMAGE = 'empty://';
// const DEFAULT_PLACEHOLDER_COLOR = correctColor('#14000000');
const DEFAULT_PLACEHOLDER_COLOR = 'rgba(0,0,0,0.08)';
const STATE_LOADING = 0;
const STATE_LOADED = 1;
const STATE_ERROR = 2;
const rootCtx = getContext<RootCtxValue>(ROOT_CTX);
let img: HTMLImageElement;
@@ -44,10 +46,47 @@
let isLottie = false;
let placeholderColor = DEFAULT_PLACEHOLDER_COLOR;
let hasError = false;
let imageUrl: string | undefined;
let backgroundImage = '';
// Exactly "none", "scale-down" would not match android
let scale = 'none';
let position = '50% 50%';
let aspectPaddingBottom = '0';
let tintColor: string | undefined = undefined;
let tintMode: TintMode = 'source_in';
let svgFilterId = '';
let animationInterpolator = '';
let animationFadeStart = 0;
let animationDelay = 0;
let animationDuration = 0;
let filter = '';
let filterClipPath = '';
$: if (json) {
scale = 'none';
position = '50% 50%';
tintMode = 'source_in';
}
$: jsonImageUrl = rootCtx.getDerivedFromVars(json.image_url);
$: jsonGifUrl = rootCtx.getDerivedFromVars(json.gif_url);
$: jsonWidth = rootCtx.getDerivedFromVars(json.width);
$: jsonHeight = rootCtx.getDerivedFromVars(json.height);
$: jsonPreview = rootCtx.getDerivedFromVars(json.preview);
$: jsonPlaceholderColor = rootCtx.getDerivedFromVars(json.placeholder_color);
$: jsonScale = rootCtx.getDerivedFromVars(json.scale);
$: jsonPosition = rootCtx.getDerivedFromVars({
content_alignment_horizontal: json.content_alignment_horizontal,
content_alignment_vertical: json.content_alignment_vertical
});
$: jsonA11y = rootCtx.getDerivedFromVars(json.accessibility);
$: jsonAspect = rootCtx.getDerivedFromVars(json.aspect);
$: jsonTintColor = rootCtx.getDerivedFromVars(json.tint_color);
$: jsonTintMode = rootCtx.getDerivedFromVars(json.tint_mode);
$: jsonAppearanceAnimation = rootCtx.getDerivedFromVars(json.appearance_animation);
$: jsonFilters = rootCtx.getDerivedFromVars(json.filters);
let imageUrl: string | undefined;
$: {
let img = json.type === 'gif' ? $jsonGifUrl : $jsonImageUrl;
isEmpty = img === EMPTY_IMAGE;
@@ -62,7 +101,6 @@
}
$: updateImageUrl(imageUrl);
let hasError = false;
$: {
if (!imageUrl) {
hasError = true;
@@ -72,14 +110,10 @@
}
}
$: jsonWidth = rootCtx.getDerivedFromVars(json.width);
$: isWidthContent = $jsonWidth?.type === 'wrap_content';
$: jsonHeight = rootCtx.getDerivedFromVars(json.height);
$: isHeightContent = $jsonHeight?.type === 'wrap_content';
$: jsonPreview = rootCtx.getDerivedFromVars(json.preview);
let backgroundImage = '';
$: {
const preview = $jsonPreview;
@@ -90,25 +124,16 @@
}
}
$: jsonPlaceholderColor = rootCtx.getDerivedFromVars(json.placeholder_color);
$: if (state === STATE_LOADING || state === STATE_ERROR || isEmpty) {
placeholderColor = correctColor($jsonPlaceholderColor, 1, placeholderColor);
} else {
placeholderColor = '';
}
$: jsonScale = rootCtx.getDerivedFromVars(json.scale);
// Exactly "none", "scale-down" would not match android
let scale = 'none';
$: {
scale = imageSize($jsonScale) || scale;
}
$: jsonPosition = rootCtx.getDerivedFromVars({
content_alignment_horizontal: json.content_alignment_horizontal,
content_alignment_vertical: json.content_alignment_vertical
});
let position = '50% 50%';
function updatePosition(pos: {
content_alignment_horizontal?: AlignmentHorizontal;
content_alignment_vertical?: AlignmentVertical;
@@ -117,11 +142,8 @@
}
$: updatePosition($jsonPosition);
$: jsonA11y = rootCtx.getDerivedFromVars(json.accessibility);
$: alt = $jsonA11y?.description || '';
$: jsonAspect = rootCtx.getDerivedFromVars(json.aspect);
let aspectPaddingBottom = '0';
$: {
const newRatio = $jsonAspect?.ratio;
if (newRatio && isPositiveNumber(newRatio)) {
@@ -131,11 +153,6 @@
}
}
$: jsonTintColor = rootCtx.getDerivedFromVars(json.tint_color);
let tintColor: string | undefined = undefined;
$: jsonTintMode = rootCtx.getDerivedFromVars(json.tint_mode);
let tintMode: TintMode = 'source_in';
let svgFilterId = '';
$: {
const val = $jsonTintColor;
const newTintColor = val ? correctColor(val) : undefined;
@@ -148,12 +165,6 @@
}
}
$: jsonAppearanceAnimation = rootCtx.getDerivedFromVars(json.appearance_animation);
let animationInterpolator = '';
let animationFadeStart = 0;
let animationDelay = 0;
let animationDuration = 0;
$: if ($jsonAppearanceAnimation && $jsonAppearanceAnimation.type === 'fade') {
const animation = $jsonAppearanceAnimation;
@@ -163,9 +174,6 @@
animationFadeStart = correctNonNegativeNumber(animation.alpha, 0);
}
$: jsonFilters = rootCtx.getDerivedFromVars(json.filters);
let filter = '';
let filterClipPath = '';
$: {
let newFilter = '';
let newClipPath = '';
@@ -1,3 +1,10 @@
<script lang="ts" context="module">
const AVAIL_SHAPES = [
'rounded_rectangle',
'circle'
];
</script>
<script lang="ts">
import { getContext, tick } from 'svelte';
@@ -26,16 +33,8 @@
export let origJson: DivBase | undefined = undefined;
export let layoutParams: LayoutParams | undefined = undefined;
const AVAIL_SHAPES = ['rounded_rectangle', 'circle'];
const rootCtx = getContext<RootCtxValue>(ROOT_CTX);
$: jsonShape = rootCtx.getDerivedFromVars(json.shape);
$: jsonActiveItemColor = rootCtx.getDerivedFromVars(json.active_item_color);
$: jsonInactiveItemColor = rootCtx.getDerivedFromVars(json.inactive_item_color);
$: jsonActiveItemSize = rootCtx.getDerivedFromVars(json.active_item_size);
$: jsonActiveShape = rootCtx.getDerivedFromVars(json.active_shape);
$: jsonInactiveShape = rootCtx.getDerivedFromVars(json.inactive_shape);
let activeStyle: DrawableStyle = {
width: 13,
height: 13,
@@ -48,6 +47,44 @@
borderRadius: 5,
background: '#33919cb5'
};
let placement: 'default' | 'stretch' = 'default';
let spaceBetweenCenters = 15;
let maxVisibleItems = 10;
let itemSpacing = 5;
let scroller: HTMLElement;
let indicatorItemsWrapper: HTMLElement;
let pagerData: PagerData;
$: if (json) {
placement = 'default';
spaceBetweenCenters = 15;
maxVisibleItems = 10;
itemSpacing = 5;
activeStyle = {
width: 13,
height: 13,
borderRadius: 6.5,
background: '#ffdc60'
};
inactiveStyle = {
width: 10,
height: 10,
borderRadius: 5,
background: '#33919cb5'
};
}
$: jsonShape = rootCtx.getDerivedFromVars(json.shape);
$: jsonActiveItemColor = rootCtx.getDerivedFromVars(json.active_item_color);
$: jsonInactiveItemColor = rootCtx.getDerivedFromVars(json.inactive_item_color);
$: jsonActiveItemSize = rootCtx.getDerivedFromVars(json.active_item_size);
$: jsonActiveShape = rootCtx.getDerivedFromVars(json.active_shape);
$: jsonInactiveShape = rootCtx.getDerivedFromVars(json.inactive_shape);
$: jsonSpaceBetweenCenters = rootCtx.getDerivedFromVars(json.space_between_centers);
$: jsonItemsPlacement = rootCtx.getDerivedFromVars(json.items_placement);
$: {
if ($jsonActiveShape) {
activeStyle = correctDrawableStyle<DrawableStyle>({
@@ -80,12 +117,6 @@
}
}
$: jsonSpaceBetweenCenters = rootCtx.getDerivedFromVars(json.space_between_centers);
$: jsonItemsPlacement = rootCtx.getDerivedFromVars(json.items_placement);
let placement: 'default' | 'stretch' = 'default';
let spaceBetweenCenters = 15;
let maxVisibleItems = 10;
let itemSpacing = 5;
$: if ($jsonItemsPlacement && ($jsonItemsPlacement.type === 'default' || $jsonItemsPlacement.type === 'stretch')) {
placement = $jsonItemsPlacement.type;
if (placement === 'default') {
@@ -104,11 +135,6 @@
spaceBetweenCenters = correctNonNegativeNumber($jsonSpaceBetweenCenters.value, spaceBetweenCenters);
}
}
let scroller: HTMLElement;
let indicatorItemsWrapper: HTMLElement;
let pagerData: PagerData;
$: pagers = rootCtx.getStore<Map<string, PagerData>>('pagers');
$: {
onPagerDataUpdate($pagers);
@@ -1,3 +1,16 @@
<script lang="ts" context="module">
const isSupportInputMode = typeof document !== 'undefined' && 'inputMode' in document.createElement('input');
const KEYBOARD_MAP: Record<KeyboardType, string> = {
email: 'email',
number: 'number',
phone: 'tel',
single_line_text: 'text',
multi_line_text: 'text',
uri: 'url'
};
</script>
<script lang="ts">
import { getContext, onDestroy, onMount, tick } from 'svelte';
import type { HTMLAttributes } from 'svelte/elements';
@@ -41,25 +54,79 @@
export let layoutParams: LayoutParams | undefined = undefined;
const rootCtx = getContext<RootCtxValue>(ROOT_CTX);
let prevId: string | undefined;
let input: HTMLInputElement | HTMLSpanElement;
let isPressed = false;
let inputMask: BaseInputMask | null = null;
const variable = json.text_variable;
const rawVariable = json.mask?.raw_text_variable;
let value = '';
let contentEditableValue = '';
let hasError = false;
if (!variable) {
let hintColor = '';
let fontSize = 12;
let fontWeight: number | undefined = undefined;
let fontFamily = '';
let lineHeight: number | undefined = undefined;
let letterSpacing = '';
let textColor = '#000';
let highlightColor = '';
let alignmentHorizontal: AlignmentHorizontal = 'left';
let alignmentVertical: AlignmentVertical = 'center';
let keyboardType = 'multi_line_text';
let inputType = 'text';
let inputMode: HTMLAttributes<HTMLInputElement>['inputmode'] = undefined;
let maxHeight = '';
let selfPadding: EdgeInsets | null = null;
let padding = '';
let verticalPadding = '';
let description = '';
$: if (json) {
hintColor = '';
fontSize = 12;
fontWeight = undefined;
fontFamily = '';
lineHeight = undefined;
textColor = '#000';
highlightColor = '';
alignmentHorizontal = 'left';
alignmentVertical = 'center';
keyboardType = 'multi_line_text';
inputType = 'text';
inputMode = undefined;
}
$: variable = json.text_variable;
$: rawVariable = json.mask?.raw_text_variable;
$: valueVariable = variable && rootCtx.getVariable(variable, 'string') || createVariable('temp', 'string', '');
$: rawValueVariable = rawVariable && rootCtx.getVariable(rawVariable, 'string') || createVariable('temp', 'string', '');
$: 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);
$: jsonHighlightColor = rootCtx.getDerivedFromVars(json.highlight_color);
$: jsonAlignmentHorizontal = rootCtx.getDerivedFromVars(json.text_alignment_horizontal);
$: jsonAlignmentVertical = rootCtx.getDerivedFromVars(json.text_alignment_vertical);
$: jsonKeyboardType = rootCtx.getDerivedFromVars(json.keyboard_type);
$: jsonMask = rootCtx.getDerivedFromVars(json.mask);
$: jsonVisibleMaxLines = rootCtx.getDerivedFromVars(json.max_visible_lines);
$: jsonPaddings = rootCtx.getDerivedFromVars(json.paddings);
$: jsonAccessibility = rootCtx.getDerivedFromVars(json.accessibility);
$: jsonSelectAll = rootCtx.getDerivedFromVars(json.select_all_on_focus);
$: if (variable) {
hasError = false;
} else {
hasError = true;
rootCtx.logError(wrapError(new Error('Missing "text_variable" in "input"')));
}
let valueVariable = variable && rootCtx.getVariable(variable, 'string') || createVariable('temp', 'string', '');
let rawValueVariable = rawVariable && rootCtx.getVariable(rawVariable, 'string') || createVariable('temp', 'string', '');
let value = '';
let contentEditableValue = '';
const jsonMask = rootCtx.getDerivedFromVars(json.mask);
function updateMaskData(mask: MaybeMissing<InputMask> | undefined): void {
if (mask?.type === 'fixed_length') {
inputMask = updateFixedMask(mask, rootCtx.logError, inputMask as FixedLengthInputMask);
@@ -81,25 +148,16 @@
runRawValueMask();
}
const jsonHintText = rootCtx.getDerivedFromVars(json.hint_text);
$: placeholder = $jsonHintText;
const jsonHintColor = rootCtx.getDerivedFromVars(json.hint_color);
let hintColor = '';
$: {
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') {
@@ -111,8 +169,6 @@
}
}
const jsonLineHeight = rootCtx.getDerivedFromVars(json.line_height);
let lineHeight: number | undefined = undefined;
$: {
const val = $jsonLineHeight;
if (isPositiveNumber(val)) {
@@ -120,51 +176,28 @@
}
}
const jsonLetterSpacing = rootCtx.getDerivedFromVars(json.letter_spacing);
let letterSpacing = '';
$: {
if (isNumber($jsonLetterSpacing)) {
letterSpacing = pxToEm($jsonLetterSpacing);
}
}
const jsonTextColor = rootCtx.getDerivedFromVars(json.text_color);
let textColor = '#000';
$: {
textColor = correctColor($jsonTextColor, 1, textColor);
}
const jsonHighlightColor = rootCtx.getDerivedFromVars(json.highlight_color);
let highlightColor = '';
$: {
highlightColor = correctColor($jsonHighlightColor, 1, highlightColor);
}
const jsonAlignmentHorizontal = rootCtx.getDerivedFromVars(json.text_alignment_horizontal);
let alignmentHorizontal: AlignmentHorizontal = 'left';
$: {
alignmentHorizontal = correctAlignmentHorizontal($jsonAlignmentHorizontal, alignmentHorizontal);
}
const jsonAlignmentVertical = rootCtx.getDerivedFromVars(json.text_alignment_vertical);
let alignmentVertical: AlignmentVertical = 'center';
$: {
alignmentVertical = correctAlignmentVertical($jsonAlignmentVertical, alignmentVertical);
}
const jsonKeyboardType = rootCtx.getDerivedFromVars(json.keyboard_type);
const KEYBOARD_MAP: Record<KeyboardType, string> = {
email: 'email',
number: 'number',
phone: 'tel',
single_line_text: 'text',
multi_line_text: 'text',
uri: 'url'
};
let keyboardType = 'multi_line_text';
let inputType = 'text';
const isSupportInputMode = typeof document !== 'undefined' && 'inputMode' in document.createElement('input');
let inputMode: HTMLAttributes<HTMLInputElement>['inputmode'] = undefined;
$: {
if ($jsonKeyboardType && $jsonKeyboardType in KEYBOARD_MAP) {
inputType = KEYBOARD_MAP[$jsonKeyboardType as KeyboardType];
@@ -177,17 +210,13 @@
}
}
const jsonVisibleMaxLines = rootCtx.getDerivedFromVars(json.max_visible_lines);
$: isMultiline = keyboardType === 'multi_line_text'/* && isPositiveNumber($jsonVisibleMaxLines) && $jsonVisibleMaxLines > 1*/;
$: jsonPaddings = rootCtx.getDerivedFromVars(json.paddings);
let maxHeight = '';
let selfPadding: EdgeInsets | null = null;
let padding = '';
let verticalPadding = '';
$: {
if (isPositiveNumber($jsonVisibleMaxLines)) {
maxHeight = `calc(${$jsonVisibleMaxLines * (lineHeight || 1.25) * (fontSize / 10) + 'em'} + ${pxToEmWithUnits(correctNonNegativeNumber($jsonPaddings?.top, 0) + correctNonNegativeNumber($jsonPaddings?.bottom, 0))})`;
} else {
maxHeight = '';
}
selfPadding = correctEdgeInsertsObject(($jsonPaddings) ? $jsonPaddings : undefined, selfPadding);
padding = selfPadding ? edgeInsertsToCss({
@@ -202,8 +231,6 @@
}) : '';
}
$: jsonAccessibility = rootCtx.getDerivedFromVars(json.accessibility);
let description = '';
$: if ($jsonAccessibility?.description) {
description = $jsonAccessibility.description;
} else {
@@ -212,8 +239,6 @@
}));
}
const jsonSelectAll = rootCtx.getDerivedFromVars(json.select_all_on_focus);
$: mods = {
'highlight-color': Boolean(highlightColor),
multiline: isMultiline,
@@ -344,15 +369,14 @@
}
}
onMount(() => {
if (input && inputMask) {
if ($rawValueVariable) {
inputMask.overrideRawValue($rawValueVariable);
$valueVariable = value = contentEditableValue = inputMask.value;
}
$: if (input && json) {
if (prevId) {
rootCtx.unregisterFocusable(prevId);
prevId = undefined;
}
if (json.id) {
if (json.id && !layoutParams?.fakeElement) {
prevId = json.id;
rootCtx.registerFocusable(json.id, {
focus() {
if (input) {
@@ -361,11 +385,21 @@
}
});
}
}
onMount(() => {
if (input && inputMask) {
if ($rawValueVariable) {
inputMask.overrideRawValue($rawValueVariable);
$valueVariable = value = contentEditableValue = inputMask.value;
}
}
});
onDestroy(() => {
if (json.id) {
rootCtx.unregisterFocusable(json.id);
if (prevId) {
rootCtx.unregisterFocusable(prevId);
prevId = undefined;
}
});
</script>
@@ -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<Map<string, PagerData>>('pagers');
function pagerDataUpdate(size: number, currentItem: number): void {
@@ -248,18 +259,26 @@
scrollToPagerItem(nextItem);
}
if (json.id && !layoutParams?.fakeElement) {
rootCtx.registerInstance<SwitchElements>(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<SwitchElements>(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;
}
});
</script>
@@ -304,18 +324,16 @@
bind:this={pagerItemsWrapper}
on:scroll={onScrollDebounced}
>
{#key items}
{#each items as item}
<div class={css.pager__item}>
<Unknown
div={item.json}
templateContext={item.templateContext}
origJson={item.origJson}
layoutParams={layoutParams?.fakeElement ? { fakeElement: true } : undefined}
/>
</div>
{/each}
{/key}
{#each items as item}
<div class={css.pager__item}>
<Unknown
div={item.json}
templateContext={item.templateContext}
origJson={item.origJson}
layoutParams={layoutParams?.fakeElement ? { fakeElement: true } : undefined}
/>
</div>
{/each}
</div>
{#if hasScrollLeft && shouldCheckArrows}
@@ -29,28 +29,61 @@
const rootCtx = getContext<RootCtxValue>(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;
}
});
</script>
@@ -199,7 +217,7 @@
>
<span class={css['select__select-text']} style={makeStyle(innerStl)} aria-hidden="true">
<!--Space holder should have height even it has no value-->
{selectText || hint || '​'}
{selectText || $jsonHintText || '​'}
</span>
<select
@@ -22,8 +22,17 @@
const rootCtx = getContext<RootCtxValue>(ROOT_CTX);
$: jsonDelimiterStyle = rootCtx.getDerivedFromVars(json.delimiter_style);
let orientation: Orientation = 'horizontal';
// let background = correctColor('#14000000');
let background = 'rgba(0,0,0,0.08)';
$: if (json) {
orientation = 'horizontal';
background = 'rgba(0,0,0,0.08)';
}
$: jsonDelimiterStyle = rootCtx.getDerivedFromVars(json.delimiter_style);
$: {
orientation = correctGeneralOrientation($jsonDelimiterStyle?.orientation, orientation);
}
@@ -40,8 +49,6 @@
)
);
// let background = correctColor('#14000000');
let background = 'rgba(0,0,0,0.08)';
$: {
background = correctColor($jsonDelimiterStyle?.color, 1, background);
}
@@ -1,3 +1,19 @@
<script lang="ts" context="module">
const DEFAULT_DRAWABLE_STYLE: DrawableStyle = {
width: 10,
height: 10,
borderRadius: 5,
background: '#000'
};
const THUMB_MARK_SHAPES = [
'rounded_rectangle',
'circle'
];
const TRACK_SHAPES = [
'rounded_rectangle'
];
</script>
<script lang="ts">
import { getContext, onDestroy, onMount } from 'svelte';
@@ -25,38 +41,67 @@
export let origJson: DivBase | undefined = undefined;
export let layoutParams: LayoutParams | undefined = undefined;
const DEFAULT_DRAWABLE_STYLE: DrawableStyle = {
width: 10,
height: 10,
borderRadius: 5,
background: '#000'
};
const THUMB_MARK_SHAPES = ['rounded_rectangle', 'circle'];
const TRACK_SHAPES = ['rounded_rectangle'];
const rootCtx = getContext<RootCtxValue>(ROOT_CTX);
const actionCtx = getContext<ActionCtxValue>(ACTION_CTX);
let prevId: string | undefined;
let input: HTMLInputElement;
let tracksInner: HTMLElement;
let switchedTracks = false;
let minValue = 0;
let maxValue = 100;
let thumbStyle = DEFAULT_DRAWABLE_STYLE;
let thumbSecondaryStyle = thumbStyle;
let trackInactiveStyle = DEFAULT_DRAWABLE_STYLE;
let trackActiveStyle = DEFAULT_DRAWABLE_STYLE;
let markActiveTicks: number[];
let markActiveStyle: DrawableStyle | null = null;
let markInactiveTicks: number[];
let markInactiveStyle: DrawableStyle | null = null;
let textStyle: TransformedSliderTextStyle | undefined = undefined;
let textSecondaryStyle: TransformedSliderTextStyle | undefined = textStyle;
let description = '';
let secondaryDescription = '';
let hasError = false;
$: if (json) {
thumbStyle = DEFAULT_DRAWABLE_STYLE;
thumbSecondaryStyle = thumbStyle;
trackInactiveStyle = DEFAULT_DRAWABLE_STYLE;
trackActiveStyle = DEFAULT_DRAWABLE_STYLE;
markActiveStyle = null;
markInactiveStyle = null;
textStyle = undefined;
textSecondaryStyle = undefined;
description = '';
secondaryDescription = '';
}
$: firstVariable = json.thumb_value_variable;
$: secondVariable = json.thumb_secondary_value_variable;
$: valueVariable = firstVariable && rootCtx.getVariable(firstVariable, 'integer') || createVariable('temp', 'integer', 0);
$: value2Variable = secondVariable && rootCtx.getVariable(secondVariable, 'integer') || createVariable('temp', 'integer', 0);
$: jsonMinValue = rootCtx.getDerivedFromVars(json.min_value);
$: jsonMaxValue = rootCtx.getDerivedFromVars(json.max_value);
let minValue = 0;
let maxValue = 100;
$: jsonThumbStyle = rootCtx.getDerivedFromVars(json.thumb_style);
$: jsonThumbSecondaryStyle = rootCtx.getDerivedFromVars(json.thumb_secondary_style);
$: jsonTrackInactiveStyle = rootCtx.getDerivedFromVars(json.track_inactive_style);
$: jsonTrackActiveStyle = rootCtx.getDerivedFromVars(json.track_active_style);
$: jsonMarkActiveStyle = rootCtx.getDerivedFromVars(json.tick_mark_active_style);
$: jsonMarkInactiveStyle = rootCtx.getDerivedFromVars(json.tick_mark_inactive_style);
$: jsonTextStyle = rootCtx.getDerivedFromVars(json.thumb_text_style);
$: jsonSecondaryTextStyle = rootCtx.getDerivedFromVars(json.thumb_secondary_text_style);
$: jsonAccessibility = rootCtx.getDerivedFromVars(json.accessibility);
$: jsonSecondaryAccessibility = rootCtx.getDerivedFromVars(json.secondary_value_accessibility);
$: {
minValue = correctNumber($jsonMinValue, minValue);
maxValue = correctNumber($jsonMaxValue, maxValue);
checkTicks();
}
const firstVariable = json.thumb_value_variable;
const secondVariable = json.thumb_secondary_value_variable;
let valueVariable = firstVariable && rootCtx.getVariable(firstVariable, 'integer') || createVariable('temp', 'integer', 0);
let value2Variable = secondVariable && rootCtx.getVariable(secondVariable, 'integer') || createVariable('temp', 'integer', 0);
let value = clamp($valueVariable || 0, minValue, maxValue);
let value2 = clamp($value2Variable || 0, minValue, maxValue);
@@ -74,26 +119,18 @@
}
}
$: jsonThumbStyle = rootCtx.getDerivedFromVars(json.thumb_style);
let thumbStyle = DEFAULT_DRAWABLE_STYLE;
$: {
thumbStyle = correctDrawableStyle($jsonThumbStyle, THUMB_MARK_SHAPES, thumbStyle);
}
$: jsonThumbSecondaryStyle = rootCtx.getDerivedFromVars(json.thumb_secondary_style);
let thumbSecondaryStyle = thumbStyle;
$: {
thumbSecondaryStyle = correctDrawableStyle($jsonThumbSecondaryStyle, THUMB_MARK_SHAPES, thumbStyle);
}
$: jsonTrackInactiveStyle = rootCtx.getDerivedFromVars(json.track_inactive_style);
let trackInactiveStyle = DEFAULT_DRAWABLE_STYLE;
$: {
trackInactiveStyle = correctDrawableStyle($jsonTrackInactiveStyle, TRACK_SHAPES, trackInactiveStyle);
}
$: jsonTrackActiveStyle = rootCtx.getDerivedFromVars(json.track_active_style);
let trackActiveStyle = DEFAULT_DRAWABLE_STYLE;
$: {
trackActiveStyle = correctDrawableStyle($jsonTrackActiveStyle, TRACK_SHAPES, trackActiveStyle);
}
@@ -117,9 +154,6 @@
return res;
}
$: jsonMarkActiveStyle = rootCtx.getDerivedFromVars(json.tick_mark_active_style);
let markActiveTicks: number[];
let markActiveStyle: DrawableStyle | null = null;
$: {
let newStyle = correctDrawableStyle($jsonMarkActiveStyle, THUMB_MARK_SHAPES, DEFAULT_DRAWABLE_STYLE);
@@ -136,9 +170,6 @@
markActiveTicks = [];
}
$: jsonMarkInactiveStyle = rootCtx.getDerivedFromVars(json.tick_mark_inactive_style);
let markInactiveTicks: number[];
let markInactiveStyle: DrawableStyle | null = null;
$: {
let newStyle = correctDrawableStyle($jsonMarkInactiveStyle, THUMB_MARK_SHAPES, DEFAULT_DRAWABLE_STYLE);
@@ -155,20 +186,14 @@
markInactiveTicks = [];
}
$: jsonTextStyle = rootCtx.getDerivedFromVars(json.thumb_text_style);
let textStyle: TransformedSliderTextStyle | undefined = undefined;
$: {
textStyle = correctSliderTextStyle($jsonTextStyle, textStyle);
}
$: jsonSecondaryTextStyle = rootCtx.getDerivedFromVars(json.thumb_secondary_text_style);
let textSecondaryStyle: TransformedSliderTextStyle | undefined = textStyle;
$: {
textSecondaryStyle = correctSliderTextStyle($jsonSecondaryTextStyle, textStyle);
}
$: jsonAccessibility = rootCtx.getDerivedFromVars(json.accessibility);
let description = '';
$: if ($jsonAccessibility?.description) {
description = $jsonAccessibility.description;
} else {
@@ -177,8 +202,6 @@
}));
}
$: jsonSecondaryAccessibility = rootCtx.getDerivedFromVars(json.secondary_value_accessibility);
let secondaryDescription = '';
$: if ($jsonSecondaryAccessibility?.description) {
secondaryDescription = $jsonSecondaryAccessibility.description;
} else if (secondVariable) {
@@ -187,7 +210,6 @@
}));
}
let hasError = false;
$: {
let newHasError = false;
@@ -313,10 +335,14 @@
const checkTicksDebounced = debounce(checkTicks, 50);
onMount(() => {
checkTicks();
$: if (json && input) {
if (prevId) {
rootCtx.unregisterFocusable(prevId);
prevId = undefined;
}
if (json.id) {
if (json.id && !layoutParams?.fakeElement) {
prevId = json.id;
rootCtx.registerFocusable(json.id, {
focus() {
if (input) {
@@ -325,11 +351,16 @@
}
});
}
}
onMount(() => {
checkTicks();
});
onDestroy(() => {
if (json.id) {
rootCtx.unregisterFocusable(json.id);
if (prevId) {
rootCtx.unregisterFocusable(prevId);
prevId = undefined;
}
});
</script>
@@ -25,19 +25,50 @@
const rootCtx = getContext<RootCtxValue>(ROOT_CTX);
const stateCtx = getContext<StateCtxValue>(STATE_CTX);
let hasError = false;
let animationRoot: HTMLElement | undefined;
let transitionChangeBoxes: Map<string, DOMRect> = new Map();
let childrenIds = new Set<string>();
let childStateMap: Map<string, StateInterface> | null = null;
let animationList: (AnimationItemWithMaxDuration | ChangeBoundsItem)[] = [];
let childrenWithTransitionIn: ChildWithTransition[] = [];
let childrenWithTransitionOut: ChildWithTransition[] = [];
let childrenWithTransitionChange: ChildWithTransitionChange[] = [];
let prevStateId: string | undefined;
$: stateId = json.div_id || json.id;
let selectedId: string | undefined;
let selectedState: State | null = null;
$: jsonDefaultStateId = rootCtx.getJsonWithVars(json.default_state_id);
$: stateVariableName = json.state_id_variable;
$: stateVariable = stateVariableName ?
rootCtx.getVariable(stateVariableName, 'string') :
null;
let inited = false;
$: if (json) {
inited = false;
}
$: if (stateId) {
hasError = false;
} else {
hasError = true;
rootCtx.logError(wrapError(new Error('Missing "id" prop for div "state"')));
}
$: if (json) {
childrenIds = new Set<string>();
}
let hasError = false;
$: items = json.states || [];
$: parentOfItems = items.map(it => {
return it.div;
});
$: {
if (!items?.length) {
hasError = true;
@@ -70,8 +101,6 @@
}
}
let childStateMap: Map<string, StateInterface> | null = null;
interface AnimationItem {
json: DivBaseData;
templateContext: TemplateContext;
@@ -100,7 +129,6 @@
resolvePromise?: (val?: void) => void;
node: HTMLElement;
}
let animationList: (AnimationItemWithMaxDuration | ChangeBoundsItem)[] = [];
interface ChildWithTransition {
json: DivBaseData;
@@ -117,9 +145,6 @@
node: HTMLElement;
resolvePromise?: (val?: void) => void;
}
let childrenWithTransitionIn: ChildWithTransition[] = [];
let childrenWithTransitionOut: ChildWithTransition[] = [];
let childrenWithTransitionChange: ChildWithTransitionChange[] = [];
function haveFadeTransition(list: AnyTransition[]): boolean {
return list.some(it => it.type === 'fade');
@@ -163,8 +188,6 @@
return null;
}
const stateId = json.div_id || json.id;
async function setState(stateId: string) {
if (selectedId === stateId) {
return;
@@ -278,167 +301,166 @@
rootCtx.setRunning('stateChange', false);
}
if (!stateId) {
hasError = true;
rootCtx.logError(wrapError(new Error('Missing "id" prop for div "state"')));
} else if (!layoutParams?.fakeElement) {
stateCtx.registerInstance(stateId, {
setState,
getChild(id: string): StateInterface | undefined {
if (childStateMap && childStateMap.has(id)) {
return childStateMap.get(id);
}
function getChild(id: string): StateInterface | undefined {
if (childStateMap && childStateMap.has(id)) {
return childStateMap.get(id);
}
rootCtx.logError(wrapError(new Error('Missing state block with id'), {
rootCtx.logError(wrapError(new Error('Missing state block with id'), {
additional: {
id
}
}));
return undefined;
}
$: if (json) {
if (prevStateId) {
stateCtx.unregisterInstance(prevStateId);
prevStateId = undefined;
}
if (stateId && !layoutParams?.fakeElement) {
prevStateId = stateId;
stateCtx.registerInstance(stateId, {
setState,
getChild
});
}
}
setContext<StateCtxValue>(STATE_CTX, {
registerInstance(id: string, block: StateInterface) {
if (!childStateMap) {
childStateMap = new Map();
}
if (childStateMap.has(id)) {
rootCtx.logError(wrapError(new Error('Duplicate state with id'), {
additional: {
id
}
}));
return undefined;
} else {
childStateMap.set(id, block);
}
});
setContext<StateCtxValue>(STATE_CTX, {
registerInstance(id: string, block: StateInterface) {
if (!childStateMap) {
childStateMap = new Map();
}
if (childStateMap.has(id)) {
rootCtx.logError(wrapError(new Error('Duplicate state with id'), {
additional: {
id
}
}));
} else {
childStateMap.set(id, block);
}
},
unregisterInstance(id: string) {
childStateMap?.delete(id);
},
runVisibilityTransition(
json: DivBaseData,
templateContext: TemplateContext,
transitions: AppearanceTransition,
node: HTMLElement,
direction: 'in' | 'out'
) {
if (!animationRoot) {
return Promise.resolve();
}
const rootBbox = animationRoot.getBoundingClientRect();
const item: AnimationItem = getItemAnimation(
rootBbox,
{
json,
templateContext,
transitions,
node
},
direction
);
const maxDuration = calcMaxDuration(item.transitions);
const itemWithMaxDuration: AnimationItemWithMaxDuration = {
...item,
maxDuration
};
animationList = [
...animationList.filter(it => it.node !== item.node),
itemWithMaxDuration
];
return new Promise<void>(resolve => {
itemWithMaxDuration.resolvePromise = resolve;
});
},
registerChildWithTransitionIn(
json: DivBaseData,
templateContext: TemplateContext,
transitions: AppearanceTransition,
node: HTMLElement
) {
const item: ChildWithTransition = {
json,
templateContext,
transitions,
node
};
childrenWithTransitionIn.push(item);
return new Promise<void>(resolve => {
item.resolvePromise = resolve;
});
},
registerChildWithTransitionOut(
json: DivBaseData,
templateContext: TemplateContext,
transitions: AppearanceTransition,
node: HTMLElement
) {
const item: ChildWithTransition = {
json,
templateContext,
transitions,
node
};
childrenWithTransitionOut.push(item);
return new Promise<void>(resolve => {
item.resolvePromise = resolve;
});
},
registerChildWithTransitionChange(
json: DivBaseData,
templateContext: TemplateContext,
transitions: TransitionChange,
node: HTMLElement
) {
const id = json.id;
if (!id) {
return Promise.resolve();
}
const item: ChildWithTransitionChange = {
id,
json,
templateContext,
transitions,
node
};
childrenWithTransitionChange.push(item);
return new Promise<void>(resolve => {
item.resolvePromise = resolve;
});
},
hasTransitionChange(id?: string) {
if (!id) {
return false;
}
return transitionChangeBoxes.has(id);
},
registerChild(id: string): void {
childrenIds.add(id);
},
unregisterChild(id: string): void {
childrenIds.delete(id);
},
unregisterInstance(id: string) {
childStateMap?.delete(id);
},
runVisibilityTransition(
json: DivBaseData,
templateContext: TemplateContext,
transitions: AppearanceTransition,
node: HTMLElement,
direction: 'in' | 'out'
) {
if (!animationRoot) {
return Promise.resolve();
}
});
}
let selectedId: string | undefined;
let selectedState: State | null = null;
const jsonDefaultStateId = rootCtx.getJsonWithVars(json.default_state_id);
const stateVariableName = json.state_id_variable;
const stateVariable = stateVariableName ?
rootCtx.getVariable(stateVariableName, 'string') :
null;
let inited = false;
const rootBbox = animationRoot.getBoundingClientRect();
const item: AnimationItem = getItemAnimation(
rootBbox,
{
json,
templateContext,
transitions,
node
},
direction
);
const maxDuration = calcMaxDuration(item.transitions);
const itemWithMaxDuration: AnimationItemWithMaxDuration = {
...item,
maxDuration
};
animationList = [
...animationList.filter(it => it.node !== item.node),
itemWithMaxDuration
];
return new Promise<void>(resolve => {
itemWithMaxDuration.resolvePromise = resolve;
});
},
registerChildWithTransitionIn(
json: DivBaseData,
templateContext: TemplateContext,
transitions: AppearanceTransition,
node: HTMLElement
) {
const item: ChildWithTransition = {
json,
templateContext,
transitions,
node
};
childrenWithTransitionIn.push(item);
return new Promise<void>(resolve => {
item.resolvePromise = resolve;
});
},
registerChildWithTransitionOut(
json: DivBaseData,
templateContext: TemplateContext,
transitions: AppearanceTransition,
node: HTMLElement
) {
const item: ChildWithTransition = {
json,
templateContext,
transitions,
node
};
childrenWithTransitionOut.push(item);
return new Promise<void>(resolve => {
item.resolvePromise = resolve;
});
},
registerChildWithTransitionChange(
json: DivBaseData,
templateContext: TemplateContext,
transitions: TransitionChange,
node: HTMLElement
) {
const id = json.id;
if (!id) {
return Promise.resolve();
}
const item: ChildWithTransitionChange = {
id,
json,
templateContext,
transitions,
node
};
childrenWithTransitionChange.push(item);
return new Promise<void>(resolve => {
item.resolvePromise = resolve;
});
},
hasTransitionChange(id?: string) {
if (!id) {
return false;
}
return transitionChangeBoxes.has(id);
},
registerChild(id: string): void {
childrenIds.add(id);
},
unregisterChild(id: string): void {
childrenIds.delete(id);
}
});
function initDefaultState(items: State[]): void {
if (inited) {
return;
@@ -470,7 +492,7 @@
}
}
}
$: initDefaultState(items);
$: !inited && initDefaultState(items);
function onOutro(item: AnimationItem | ChangeBoundsItem): void {
animationList = animationList.filter(it => it !== item);
@@ -481,8 +503,8 @@
}
onDestroy(() => {
if (stateId && !layoutParams?.fakeElement) {
stateCtx.unregisterInstance(stateId);
if (prevStateId) {
stateCtx.unregisterInstance(prevStateId);
}
});
</script>
@@ -499,7 +521,7 @@
{replaceItems}
>
{#if selectedState?.div}
{#key selectedState}
{#key selectedId}
<Unknown div={selectedState.div} templateContext={templateContext} />
{/key}
{/if}
+122 -93
View File
@@ -13,6 +13,7 @@
import type { SwitchElements, Overflow } from '../../types/switch-elements';
import type { TabItem } from '../../types/tabs';
import type { MaybeMissing } from '../../expressions/json';
import type { DivBaseData } from '../../types/base';
import { ROOT_CTX, RootCtxValue } from '../../context/root';
import Outer from '../utilities/Outer.svelte';
import Unknown from '../utilities/Unknown.svelte';
@@ -34,28 +35,98 @@
import { correctEdgeInsertsObject } from '../../utils/correctEdgeInsertsObject';
import { correctNonNegativeNumber } from '../../utils/correctNonNegativeNumber';
import { edgeInsertsToCss } from '../../utils/edgeInsertsToCss';
import { DivBaseData } from '../../types/base';
export let json: Partial<DivTabsData> = {};
export let templateContext: TemplateContext;
export let origJson: DivBase | undefined = undefined;
export let layoutParams: LayoutParams | undefined = undefined;
const rootCtx = getContext<RootCtxValue>(ROOT_CTX);
const instId = rootCtx.genId('tabs');
let hasError = false;
$: items = json.items || [];
$: parentOfItems = items.map(it => {
return it.div;
});
interface ChildInfo {
index: number;
title: MaybeMissing<string> | undefined;
title_click_action?: MaybeMissing<Action> | undefined;
}
const rootCtx = getContext<RootCtxValue>(ROOT_CTX);
const instId = rootCtx.genId('tabs');
let prevId: string | undefined;
let hasError = false;
let childStore = writable<ChildInfo[]>([]);
let childLayoutParams: LayoutParams = {};
let tabsElem: HTMLElement;
let panelsWrapper: HTMLElement;
let swiperElem: HTMLElement;
let mods: Mods = {};
let tabFontSize = 12;
let tabPaddings = '';
let tabLineHeight = '';
let tabLetterSpacing = '';
let tabBorderRadius = '';
let tabActiveFontWeight: number | undefined = undefined;
let tabActiveFontFamily = '';
let tabInactiveFontWeight: number | undefined = undefined;
let tabInactiveFontFamily = '';
let tabActiveTextColor = '';
let tabInactiveTextColor = '';
let tabActiveBackground = '';
let tabInactiveBackground = '';
let tabItemSpacing = 0;
let separatorBackground = '';
let separatorMargins = '';
let titlePadding: EdgeInsets | null = null;
let isSwipeInitialized = false;
let isAnimated = false;
let previousSelected: number | undefined;
let showedPanels: boolean[] = [];
let visiblePanels: boolean[] = [];
let hidePanelsTimeout: number | null = null;
let startCoords: Coords | null = null;
let moveCoords: Coords | null = null;
let swipeStartTime: number;
let isSwipeStarted = false;
let isSwipeCanceled = false;
let startTransform: number;
let currentTransform: number;
$: if (json) {
tabFontSize = 12;
tabPaddings = '';
tabBorderRadius = '';
tabActiveFontWeight = undefined;
tabActiveFontFamily = '';
tabInactiveFontWeight = undefined;
tabInactiveFontFamily = '';
tabActiveTextColor = '';
tabInactiveTextColor = '';
tabActiveBackground = '';
tabInactiveBackground = '';
tabItemSpacing = 0;
separatorBackground = '';
separatorMargins = '';
titlePadding = null;
}
$: items = json.items || [];
$: parentOfItems = items.map(it => {
return it.div;
});
$: jsonWidth = rootCtx.getDerivedFromVars(json.width);
$: jsonHeight = rootCtx.getDerivedFromVars(json.height);
$: jsonSelectedTab = rootCtx.getJsonWithVars(json.selected_tab);
$: jsonTabStyle = rootCtx.getDerivedFromVars(json.tab_title_style);
$: jsonSeparator = rootCtx.getDerivedFromVars(json.has_separator);
$: jsonSeparatorColor = rootCtx.getDerivedFromVars(json.separator_color);
$: jsonSeparatorPaddings = rootCtx.getDerivedFromVars(json.separator_paddings);
$: jsonSwipeEnabled = rootCtx.getDerivedFromVars(json.switch_tabs_by_content_swipe_enabled);
$: jsonRestrictParentScroll = rootCtx.getDerivedFromVars(json.restrict_parent_scroll);
$: jsonTitlePaddings = rootCtx.getDerivedFromVars(json.title_paddings);
$: selected = jsonSelectedTab && Number(jsonSelectedTab) || 0;
$: if (Array.isArray(items) && items.length) {
let children: ChildInfo[] = [];
@@ -106,9 +177,6 @@
}
}
$: jsonWidth = rootCtx.getDerivedFromVars(json.width);
$: jsonHeight = rootCtx.getDerivedFromVars(json.height);
let childLayoutParams: LayoutParams = {};
$: {
let newLayoutParams: LayoutParams = {};
@@ -125,13 +193,6 @@
childLayoutParams = assignIfDifferent(newLayoutParams, childLayoutParams);
}
let tabsElem: HTMLElement;
let panelsWrapper: HTMLElement;
let swiperElem: HTMLElement;
let mods: Mods = {};
const jsonSelectedTab = rootCtx.getJsonWithVars(json.selected_tab);
let selected = jsonSelectedTab && Number(jsonSelectedTab) || 0;
$: if (!hasError && (selected < 0 || selected >= items.length)) {
rootCtx.logError(wrapError(new Error('Incorrect "selected_tab" prop for div "tabs"'), {
additional: {
@@ -151,16 +212,12 @@
selected = $childStore[0]?.index || 0;
}
$: jsonTabStyle = rootCtx.getDerivedFromVars(json.tab_title_style);
$: tabStyle = $jsonTabStyle || {};
let tabFontSize = 12;
$: {
tabFontSize = correctPositiveNumber(tabStyle.font_size, tabFontSize);
}
let tabPaddings = '';
$: {
if (tabStyle.font_size || tabStyle.paddings) {
const paddings: EdgeInsets = tabStyle.paddings || {
@@ -181,7 +238,6 @@
}
}
let tabLineHeight = '';
$: {
const lineHeight = tabStyle.line_height;
if (lineHeight !== undefined && isPositiveNumber(lineHeight)) {
@@ -189,7 +245,6 @@
}
}
let tabLetterSpacing = '';
$: {
const letterSpacing = tabStyle.letter_spacing;
if (letterSpacing !== undefined && isNonNegativeNumber(letterSpacing)) {
@@ -197,7 +252,6 @@
}
}
let tabBorderRadius = '';
$: {
if (tabStyle.corner_radius || tabStyle.corners_radius || tabStyle.font_size) {
const defaultRadius = tabStyle.corner_radius ?? 1000;
@@ -215,8 +269,6 @@
}
}
let tabActiveFontWeight: number | undefined = undefined;
let tabActiveFontFamily = '';
$: {
tabActiveFontWeight = correctFontWeight(
tabStyle.active_font_weight || tabStyle.font_weight,
@@ -231,8 +283,6 @@
}
}
let tabInactiveFontWeight: number | undefined = undefined;
let tabInactiveFontFamily = '';
$: {
tabInactiveFontWeight = correctFontWeight(
tabStyle.inactive_font_weight || tabStyle.font_weight,
@@ -247,36 +297,26 @@
}
}
let tabActiveTextColor = '';
$: {
tabActiveTextColor = correctColor(tabStyle.active_text_color, 1, tabActiveTextColor);
}
let tabInactiveTextColor = '';
$: {
tabInactiveTextColor = correctColor(tabStyle.inactive_text_color, 1, tabInactiveTextColor);
}
let tabActiveBackground = '';
$: {
tabActiveBackground = correctColor(tabStyle.active_background_color, 1, tabActiveBackground);
}
let tabInactiveBackground = '';
$: {
tabInactiveBackground = correctColor(tabStyle.inactive_background_color, 1, tabInactiveBackground);
}
let tabItemSpacing = 0;
$: {
tabItemSpacing = correctNonNegativeNumber(tabStyle.item_spacing, tabItemSpacing);
}
$: jsonSeparator = rootCtx.getDerivedFromVars(json.has_separator);
$: jsonSeparatorColor = rootCtx.getDerivedFromVars(json.separator_color);
$: jsonSeparatorPaddings = rootCtx.getDerivedFromVars(json.separator_paddings);
let separatorBackground = '';
let separatorMargins = '';
$: {
if ($jsonSeparator) {
if ($jsonSeparatorColor) {
@@ -292,24 +332,14 @@
margin: separatorMargins
};
$: jsonSwipeEnabled = rootCtx.getDerivedFromVars(json.switch_tabs_by_content_swipe_enabled);
$: isSwipeEnabled = typeof $jsonSwipeEnabled === 'undefined' ?
true :
Boolean($jsonSwipeEnabled);
$: jsonRestrictParentScroll = rootCtx.getDerivedFromVars(json.restrict_parent_scroll);
$: jsonTitlePaddings = rootCtx.getDerivedFromVars(json.title_paddings);
let titlePadding: EdgeInsets | null = null;
$: {
titlePadding = correctEdgeInsertsObject($jsonTitlePaddings ? $jsonTitlePaddings : undefined, titlePadding);
}
let isSwipeInitialized = false;
let isAnimated = false;
let previousSelected = selected;
let showedPanels: boolean[] = [];
let visiblePanels: boolean[] = [];
function updateItems(_items: TabItem[]): void {
if (hasError) {
return;
@@ -383,10 +413,10 @@
function updateShowedPanels(around = false): void {
const start = around ?
Math.max(0, selected - 1) :
Math.min(selected, previousSelected);
Math.min(selected, previousSelected ?? selected);
const end = around ?
Math.min(items.length - 1, selected + 1) :
Math.max(selected, previousSelected);
Math.max(selected, previousSelected ?? selected);
showedPanels = showedPanels.map((isShowed, index) => isShowed || index >= start && index <= end);
visiblePanels = visiblePanels.map((_, index) => index >= start && index <= end);
@@ -401,8 +431,6 @@
}
}
let hidePanelsTimeout: number | null = null;
function hideNonVisiblePanels(): void {
if (hidePanelsTimeout) {
clearTimeout(hidePanelsTimeout);
@@ -444,17 +472,9 @@
isSwipeInitialized = true;
panelsWrapper.style.height = pxToEm(panelsWrapper.clientHeight);
swiperElem.style.transform = `translate3d(${-previousSelected * 100}%,0,0)`;
swiperElem.style.transform = `translate3d(${-(previousSelected ?? selected) * 100}%,0,0)`;
}
let startCoords: Coords | null = null;
let moveCoords: Coords | null = null;
let swipeStartTime: number;
let isSwipeStarted = false;
let isSwipeCanceled = false;
let startTransform: number;
let currentTransform: number;
function onTouchStart(event: TouchEvent): void {
const target = event.target as HTMLElement | null;
const restrictClosest = target?.closest?.(`.${rootCss['root_restrict-scroll']}`);
@@ -554,39 +574,48 @@
}
}
if (json.id && !hasError && !layoutParams?.fakeElement) {
rootCtx.registerInstance<SwitchElements>(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;
}
if (json.id && !hasError && !layoutParams?.fakeElement) {
prevId = json.id;
rootCtx.registerInstance<SwitchElements>(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');
}
setSelected(item);
},
setPreviousItem(overflow: Overflow) {
let previousItem = selected - 1;
if (previousItem < 0) {
previousItem = overflow === 'ring' ? items.length - 1 : selected;
}
setSelected(previousItem);
},
setNextItem(overflow: Overflow) {
let nextItem = selected + 1;
if (nextItem > items.length - 1) {
nextItem = overflow === 'ring' ? 0 : selected;
}
setSelected(nextItem);
}
setSelected(item);
},
setPreviousItem(overflow: Overflow) {
let previousItem = selected - 1;
if (previousItem < 0) {
previousItem = overflow === 'ring' ? items.length - 1 : selected;
}
setSelected(previousItem);
},
setNextItem(overflow: Overflow) {
let nextItem = selected + 1;
if (nextItem > items.length - 1) {
nextItem = overflow === 'ring' ? 0 : selected;
}
setSelected(nextItem);
}
});
});
}
}
onDestroy(() => {
if (json.id && !layoutParams?.fakeElement) {
rootCtx.unregisterInstance(json.id);
if (prevId) {
rootCtx.unregisterInstance(prevId);
prevId = undefined;
}
});
</script>
@@ -34,14 +34,52 @@
const rootCtx = getContext<RootCtxValue>(ROOT_CTX);
$: jsonText = rootCtx.getDerivedFromVars(json.text);
let text = '';
$: {
text = propToString($jsonText);
let fontSize = 12;
let lineHeight = 1.25;
let customLineHeight = false;
let maxHeight = '';
let lineClamp: string | number = '';
let multiline = false;
let halign: AlignmentHorizontal = 'left';
let valign: AlignmentVertical = 'top';
let rootTextColor = '';
let focusTextColor = '';
let gradient = '';
let selectable = false;
let renderList: ({
text: string;
textStyles: TextStyles;
actions?: MaybeMissing<Action[]>;
} | {
image: {
url: string;
width: string;
height: string;
wrapperStyle: Style;
svgFilterId: string;
};
})[] = [];
let usedTintColors: [string, TintMode][] = [];
$: if (json) {
fontSize = 12;
lineHeight = 1.25;
customLineHeight = false;
maxHeight = '';
lineClamp = '';
multiline = false;
halign = 'left';
valign = 'top';
rootTextColor = '';
gradient = '';
selectable = false;
}
$: jsonText = rootCtx.getDerivedFromVars(json.text);
$: jsonRanges = rootCtx.getDerivedFromVars(json.ranges);
$: jsonImages = rootCtx.getDerivedFromVars(json.images);
$: jsonRootTextStyles = rootCtx.getDerivedFromVars({
font_size: json.font_size,
letter_spacing: json.letter_spacing,
@@ -53,16 +91,25 @@
line_height: json.line_height,
text_shadow: json.text_shadow
});
$: jsonTextSize = rootCtx.getDerivedFromVars(json.font_size);
let fontSize = 12;
$: jsonLineHeight = rootCtx.getDerivedFromVars(json.line_height);
$: jsonMaxLines = rootCtx.getDerivedFromVars(json.max_lines);
$: jsonHAlign = rootCtx.getDerivedFromVars(json.text_alignment_horizontal);
$: jsonVAlign = rootCtx.getDerivedFromVars(json.text_alignment_vertical);
$: jsonTextColor = rootCtx.getDerivedFromVars(json.text_color);
$: jsonFocusTextColor = rootCtx.getDerivedFromVars(json.focused_text_color);
$: jsonTruncate = rootCtx.getDerivedFromVars(json.truncate);
$: jsonTextGradient = rootCtx.getDerivedFromVars(json.text_gradient);
$: jsonSelectable = rootCtx.getDerivedFromVars(json.selectable);
$: {
text = propToString($jsonText);
}
$: {
fontSize = correctPositiveNumber($jsonTextSize, fontSize);
}
$: jsonLineHeight = rootCtx.getDerivedFromVars(json.line_height);
let lineHeight = 1.25;
let customLineHeight = false;
$: {
const newLineHeight = $jsonLineHeight;
if (isPositiveNumber(newLineHeight)) {
@@ -73,11 +120,7 @@
}
}
$: jsonMaxLines = rootCtx.getDerivedFromVars(json.max_lines);
$: singleline = $jsonMaxLines === 1;
let maxHeight = '';
let lineClamp: string | number = '';
let multiline = false;
$: {
let newMaxHeight = '';
let newLineClamp: string | number = '';
@@ -96,14 +139,10 @@
multiline = newMultiline;
}
let halign: AlignmentHorizontal = 'left';
$: jsonHAlign = rootCtx.getDerivedFromVars(json.text_alignment_horizontal);
$: {
halign = correctAlignmentHorizontal($jsonHAlign, halign);
}
let valign: AlignmentVertical = 'top';
$: jsonVAlign = rootCtx.getDerivedFromVars(json.text_alignment_vertical);
$: {
valign = correctAlignmentVertical($jsonVAlign, valign);
}
@@ -115,12 +154,10 @@
$jsonRanges[0].start === 0 && typeof $jsonRanges[0].end === 'number' && $jsonRanges[0].end >= text.length
);
$: jsonTextColor = rootCtx.getDerivedFromVars(json.text_color);
$: isOnlyOneColorDefined = Boolean($jsonTextColor) !==
Boolean($jsonRanges && $jsonRanges[0] && $jsonRanges[0].text_color);
let rootTextColor = '';
$: {
let newRootTextColor = '';
@@ -136,17 +173,12 @@
rootTextColor = newRootTextColor;
}
let focusTextColor = '';
$: jsonFocusTextColor = rootCtx.getDerivedFromVars(json.focused_text_color);
$: {
focusTextColor = correctColor($jsonFocusTextColor, 1, focusTextColor);
}
$: jsonTruncate = rootCtx.getDerivedFromVars(json.truncate);
$: truncate = $jsonTruncate === 'none' ? 'none' : '';
$: jsonTextGradient = rootCtx.getDerivedFromVars(json.text_gradient);
let gradient = '';
$: {
let newGradient = '';
@@ -160,27 +192,10 @@
gradient = newGradient;
}
$: jsonSelectable = rootCtx.getDerivedFromVars(json.selectable);
let selectable = false;
$: {
selectable = correctBooleanInt($jsonSelectable, selectable);
}
let renderList: ({
text: string;
textStyles: TextStyles;
actions?: MaybeMissing<Action[]>;
} | {
image: {
url: string;
width: string;
height: string;
wrapperStyle: Style;
svgFilterId: string;
};
})[] = [];
let usedTintColors: [string, TintMode][] = [];
function updateRenderList(
text: string,
textRanges: MaybeMissing<TextRange[]> | undefined,
@@ -403,6 +418,7 @@
{#if 'text' in item}
{#if item.text}
<TextRangeView
{json}
text={item.text}
rootFontSize={fontSize}
textStyles={item.textStyles}
@@ -432,6 +448,7 @@
{/each}
{:else}
<TextRangeView
{json}
{text}
rootFontSize={fontSize}
textStyles={$jsonRootTextStyles}
@@ -2,7 +2,7 @@
import { getContext } from 'svelte';
import css from './TextRange.module.css';
import type { TextRange } from '../../types/text';
import type { DivTextData, TextRange } from '../../types/text';
import type { Action } from '../../../typings/common';
import type { MaybeMissing } from '../../expressions/json';
import Actionable from '../utilities/Actionable.svelte';
@@ -18,6 +18,7 @@
import { ROOT_CTX, RootCtxValue } from '../../context/root';
import { shadowToCssFilter } from '../../utils/shadow';
export let json: Partial<DivTextData>;
export let text: string;
export let rootFontSize: number;
export let textStyles: MaybeMissing<Partial<TextRange>> = {};
@@ -27,6 +28,28 @@
const rootCtx = getContext<RootCtxValue>(ROOT_CTX);
let decoration = 'none';
let fontSize = 12;
let lineHeight = 1.25;
let letterSpacing = '';
let fontWeight: number | undefined = undefined;
let fontFamily = '';
let color = '';
let border: {
color: string;
width: number;
} | null = null;
$: if (json) {
decoration = 'none';
fontSize = 12;
lineHeight = 1.25;
letterSpacing = '';
fontWeight = undefined;
fontFamily = '';
color = '';
border = null;
}
$: {
let newDecoration = 'none';
@@ -43,27 +66,22 @@
decoration = newDecoration;
}
let fontSize = 12;
$: {
fontSize = correctPositiveNumber(textStyles.font_size, fontSize);
}
let lineHeight = 1.25;
$: {
if (isPositiveNumber(textStyles.line_height)) {
lineHeight = Number(textStyles.line_height) / fontSize;
}
}
let letterSpacing = '';
$: {
if (isNonNegativeNumber(textStyles.letter_spacing)) {
letterSpacing = pxToEm(textStyles.letter_spacing);
}
}
let fontWeight: number | undefined = undefined;
let fontFamily = '';
$: {
fontWeight = correctFontWeight(textStyles.font_weight, fontWeight);
if (typeof textStyles.font_family === 'string' && textStyles.font_family) {
@@ -75,7 +93,6 @@
}
}
let color = '';
$: {
color = correctColor(textStyles.text_color, 1, color);
}
@@ -84,10 +101,6 @@
$: bg = textStyles.background ? getBackground([textStyles.background]) : null;
let border: {
color: string;
width: number;
} | null = null;
$: if (
textStyles.border?.stroke &&
textStyles.border.stroke.color &&
@@ -1,5 +1,16 @@
<script lang="ts" context="module">
const DEFAULT_ANIMATION: Animation = {
name: 'set',
items: [{
name: 'translate'
}, {
name: 'fade'
}]
};
</script>
<script lang="ts">
import { afterUpdate, createEventDispatcher, getContext, onDestroy, onMount } from 'svelte';
import { afterUpdate, getContext, onDestroy, onMount } from 'svelte';
import rootCss from '../Root.module.css';
import css from './Tooltip.module.css';
@@ -19,26 +30,10 @@
const rootCtx = getContext<RootCtxValue>(ROOT_CTX);
const DEFAULT_ANIMATION: Animation = {
name: 'set',
items: [{
name: 'translate'
}, {
name: 'fade'
}]
};
const isDesktop = rootCtx.isDesktop;
const creationTime = Date.now();
$: isDesktop = rootCtx.isDesktop;
$: position = rootCtx.getDerivedFromVars(data.position);
$: offsetX = rootCtx.getDerivedFromVars(data.offset?.x?.value);
$: offsetY = rootCtx.getDerivedFromVars(data.offset?.y?.value);
$: animationIn = rootCtx.getDerivedFromVars(data.animation_in);
$: animationOut = rootCtx.getDerivedFromVars(data.animation_out);
let tooltipNode: HTMLElement;
let visible = false;
let tooltipX = '';
@@ -47,6 +42,13 @@
let tooltipHeight = '';
let resizeObserver: ResizeObserver | null = null;
$: position = rootCtx.getDerivedFromVars(data.position);
$: offsetX = rootCtx.getDerivedFromVars(data.offset?.x?.value);
$: offsetY = rootCtx.getDerivedFromVars(data.offset?.y?.value);
$: animationIn = rootCtx.getDerivedFromVars(data.animation_in);
$: animationOut = rootCtx.getDerivedFromVars(data.animation_out);
$: mods = {
visible
};
@@ -1,3 +1,8 @@
<script lang="ts" context="module">
const MIN_SWIPE_PX = 8;
const MIN_LONG_TAP_DURATION = 400;
</script>
<script lang="ts">
import { getContext, onDestroy, onMount, setContext } from 'svelte';
@@ -33,9 +38,6 @@
}
});
const MIN_SWIPE_PX = 8;
const MIN_LONG_TAP_DURATION = 400;
let node: HTMLElement;
let href = '';
let target: string | undefined = undefined;
@@ -1,9 +1,24 @@
<script lang="ts" context="module">
const HORIZONTAL_ALIGN_TO_GENERAL = {
left: 'start',
center: 'center',
right: 'end'
};
const VERTICAL_ALIGN_TO_GENERAL = {
top: 'start',
center: 'center',
bottom: 'end',
baseline: 'baseline'
};
</script>
<script lang="ts">
import { getContext, onDestroy, tick } from 'svelte';
import css from './Outer.module.css';
import type { DivBaseData } from '../../types/base';
import type { DivBaseData, Extension } from '../../types/base';
import type { Mods, Style } from '../../types/general';
import type { DivActionableData } from '../../types/actionable';
import type { LayoutParams } from '../../types/layoutParams';
@@ -42,6 +57,7 @@
import { isNonNegativeNumber } from '../../utils/isNonNegativeNumber';
import { Truthy } from '../../utils/truthy';
import { shadowToCssBoxShadow } from '../../utils/shadow';
import { isDeepEqual } from '../../utils/isDeepEqual';
import Actionable from './Actionable.svelte';
import OuterBackground from './OuterBackground.svelte';
@@ -61,27 +77,132 @@
export let replaceItems: ((items: (DivBaseData | undefined)[]) => void) | undefined = undefined;
export let hasInnerFocusable = false;
const HORIZONTAL_ALIGN_TO_GENERAL = {
left: 'start',
center: 'center',
right: 'end'
};
const VERTICAL_ALIGN_TO_GENERAL = {
top: 'start',
center: 'center',
bottom: 'end',
baseline: 'baseline'
};
const rootCtx = getContext<RootCtxValue>(ROOT_CTX);
const stateCtx = getContext<StateCtxValue>(STATE_CTX);
const isPointerFocus = rootCtx.isPointerFocus;
let currentNode: HTMLElement;
let attrs: Record<string, string> | undefined;
let extensions: DivExtension[] | null = null;
let prevChilds: string[] = [];
let borderStyle: Style = {};
let borderElemStyle: Style = {};
let hasBorder = false;
let strokeWidth = 1;
let strokeColor = 'transparent';
let cornerRadius = 0;
let cornersRadius: CornersRadius = {
'top-left': 0,
'top-right': 0,
'bottom-right': 0,
'bottom-left': 0
};
let backgroundRadius = '';
let selfPadding: EdgeInsets | null = null;
let margin = '';
let widthMods: Mods = {};
let width: string | undefined;
let widthMin: string | undefined;
let widthMax: string | undefined;
let widthNum = 0;
let widthFlexGrow = 0;
let widthFlexShrink = 0;
let widthFill = false;
let hasWidthError = false;
let heightMods: Mods = {};
let height: string | undefined;
let heightMin: string | undefined;
let heightMax: string | undefined;
let heightNum = 0;
let heightFlexGrow = 0;
let heightFlexShrink = 0;
let heightFill = false;
let hasHeightError = false;
let alpha = 1;
let opacity: number | undefined;
let background: MaybeMissing<Background[]> | undefined;
let backgroundStyle: Style;
let hasSeparateBg: boolean;
let jsonTransitionTriggers = [];
let hasStateChangeTrigger = false;
let hasVisibilityChangeTrigger = false;
let stateChangingInProgress: boolean | undefined;
let visibilityChangingInProgress: boolean | undefined;
let transitionChangeInProgress: boolean | undefined;
let actions: MaybeMissing<Action>[] = [];
let doubleTapActions: MaybeMissing<Action>[] = [];
let longTapActions: MaybeMissing<Action>[] = [];
let focusActions: MaybeMissing<Action>[] = [];
let blurActions: MaybeMissing<Action>[] = [];
let actionAnimationList: MaybeMissing<AnyAnimation>[] = [];
let actionAnimationTransition = '';
let animationOpacityStart: number | undefined = undefined;
let animationOpacityEnd: number | undefined = undefined;
let animationScaleStart: number | undefined = undefined;
let animationScaleEnd: number | undefined = undefined;
let isVisibilityInited = false;
let visibility: Visibility = 'visible';
let pivotXNum = 0;
let pivotYNum = 0;
let transformOrigin: string | undefined;
let transform: string | undefined;
let hasCustomFocus = false;
let prevExtensionsVal: Extension[] | undefined = undefined;
let dev: DevtoolResult | null = null;
$: if (json && layoutParams) {
selfPadding = null;
margin = '';
alpha = 1;
isVisibilityInited = false;
visibility = 'visible';
pivotXNum = 0;
pivotYNum = 0;
transformOrigin = undefined;
transform = undefined;
jsonTransitionTriggers = layoutParams.fakeElement ?
[] :
(json.transition_triggers || ['state_change', 'visibility_change']);
hasStateChangeTrigger = Boolean(jsonTransitionTriggers.indexOf('state_change') !== -1 && json.id);
hasVisibilityChangeTrigger = Boolean(jsonTransitionTriggers.indexOf('visibility_change') !== -1 && json.id);
}
$: jsonFocus = rootCtx.getDerivedFromVars(json.focus);
$: jsonBorder = rootCtx.getDerivedFromVars(json.border);
$: jsonPaddings = rootCtx.getDerivedFromVars(json.paddings);
$: jsonMargins = rootCtx.getDerivedFromVars(json.margins);
$: jsonWidth = rootCtx.getDerivedFromVars(json.width);
$: jsonAlignmentHorizontal = rootCtx.getDerivedFromVars(json.alignment_horizontal);
$: jsonHeight = rootCtx.getDerivedFromVars(json.height);
$: jsonAlignmentVertical = rootCtx.getDerivedFromVars(json.alignment_vertical);
$: jsonAlpha = rootCtx.getDerivedFromVars(json.alpha);
$: jsonAccessibility = rootCtx.getDerivedFromVars(json.accessibility);
$: jsonBackground = rootCtx.getDerivedFromVars(json.background);
$: jsonAction = rootCtx.getDerivedFromVars(json.action);
$: jsonActions = rootCtx.getDerivedFromVars(json.actions);
$: jsonDoubleTapActions = rootCtx.getDerivedFromVars(json.doubletap_actions);
$: jsonLongTapActions = rootCtx.getDerivedFromVars(json.longtap_actions);
$: jsonActionAnimation = rootCtx.getDerivedFromVars(json.action_animation);
$: jsonVisibility = rootCtx.getDerivedFromVars(json.visibility);
$: jsonTransform = rootCtx.getDerivedFromVars(json.transform);
$: {
prevChilds.forEach(id => {
rootCtx.unregisterParentOf(id);
@@ -121,22 +242,6 @@
replaceItems(newItems);
}
$: jsonFocus = rootCtx.getDerivedFromVars(json.focus);
$: jsonBorder = rootCtx.getDerivedFromVars(json.border);
let borderStyle: Style = {};
let borderElemStyle: Style = {};
let hasBorder = false;
let strokeWidth = 1;
let strokeColor = 'transparent';
let cornerRadius = 0;
let cornersRadius: CornersRadius = {
'top-left': 0,
'top-right': 0,
'bottom-right': 0,
'bottom-left': 0
};
let backgroundRadius = '';
$: {
const border = hasCustomFocus && $jsonFocus?.border ? $jsonFocus.border : $jsonBorder;
let newBorderStyle: Style = {};
@@ -195,8 +300,6 @@
backgroundRadius = newBackgroundRadius;
}
$: jsonPaddings = rootCtx.getDerivedFromVars(json.paddings);
let selfPadding: EdgeInsets | null = null;
$: {
selfPadding = correctEdgeInsertsObject(
($jsonPaddings && !customPaddings) ?
@@ -208,23 +311,9 @@
$: padding = edgeInsertsToCss(sumEdgeInsets(selfPadding, additionalPaddings));
$: jsonMargins = rootCtx.getDerivedFromVars(json.margins);
let margin = '';
$: {
margin = correctEdgeInserts($jsonMargins, margin);
}
$: jsonWidth = rootCtx.getDerivedFromVars(json.width);
$: jsonAlignmentHorizontal = rootCtx.getDerivedFromVars(json.alignment_horizontal);
let widthMods: Mods = {};
let width: string | undefined;
let widthMin: string | undefined;
let widthMax: string | undefined;
let widthNum = 0;
let widthFlexGrow = 0;
let widthFlexShrink = 0;
let widthFill = false;
let hasWidthError = false;
$: {
let widthType: 'parent' | 'content' | undefined = undefined;
let newWidth: string | undefined = undefined;
@@ -314,17 +403,6 @@
hasWidthError = newWidthError;
}
$: jsonHeight = rootCtx.getDerivedFromVars(json.height);
$: jsonAlignmentVertical = rootCtx.getDerivedFromVars(json.alignment_vertical);
let heightMods: Mods = {};
let height: string | undefined;
let heightMin: string | undefined;
let heightMax: string | undefined;
let heightNum = 0;
let heightFlexGrow = 0;
let heightFlexShrink = 0;
let heightFill = false;
let hasHeightError = false;
$: {
let heightType: 'parent' | 'content' | undefined = undefined;
let newHeight: string | undefined = undefined;
@@ -429,15 +507,11 @@
`${layoutParams.gridArea.y + 1}/${layoutParams.gridArea.x + 1}/span ${layoutParams.gridArea.rowSpan}/span ${layoutParams.gridArea.colSpan}` :
undefined;
$: jsonAlpha = rootCtx.getDerivedFromVars(json.alpha);
let alpha = 1;
let opacity: number | undefined;
$: {
alpha = correctAlpha($jsonAlpha, alpha);
opacity = alpha === 1 ? undefined : alpha;
}
$: jsonAccessibility = rootCtx.getDerivedFromVars(json.accessibility);
$: {
attrs = undefined;
if ($jsonAccessibility && !customDescription && $jsonAccessibility.description) {
@@ -446,10 +520,6 @@
}
}
$: jsonBackground = rootCtx.getDerivedFromVars(json.background);
let background: MaybeMissing<Background[]> | undefined;
let backgroundStyle: Style;
let hasSeparateBg: boolean;
$: {
background = hasCustomFocus && $jsonFocus?.background ? $jsonFocus.background : $jsonBackground;
backgroundStyle = {};
@@ -470,15 +540,6 @@
}
}
const jsonTransitionTriggers = layoutParams.fakeElement ?
[] :
(json.transition_triggers || ['state_change', 'visibility_change']);
const hasStateChangeTrigger = jsonTransitionTriggers.indexOf('state_change') !== -1 && json.id;
const hasVisibilityChangeTrigger = jsonTransitionTriggers.indexOf('visibility_change') !== -1 && json.id;
let stateChangingInProgress: boolean | undefined;
let visibilityChangingInProgress: boolean | undefined;
let transitionChangeInProgress: boolean | undefined;
$: {
stateChangingInProgress = undefined;
if (hasStateChangeTrigger && json.transition_in && rootCtx.isRunning('stateChange')) {
@@ -495,15 +556,6 @@
}
}
$: jsonAction = rootCtx.getDerivedFromVars(json.action);
$: jsonActions = rootCtx.getDerivedFromVars(json.actions);
$: jsonDoubleTapActions = rootCtx.getDerivedFromVars(json.doubletap_actions);
$: jsonLongTapActions = rootCtx.getDerivedFromVars(json.longtap_actions);
let actions: MaybeMissing<Action>[] = [];
let doubleTapActions: MaybeMissing<Action>[] = [];
let longTapActions: MaybeMissing<Action>[] = [];
let focusActions: MaybeMissing<Action>[] = [];
let blurActions: MaybeMissing<Action>[] = [];
$: {
let newActions = $jsonActions || $jsonAction && [$jsonAction] || [];
let newDoubleTapActions = $jsonDoubleTapActions || [];
@@ -555,13 +607,6 @@
blurActions = newBlurActions;
}
$: jsonActionAnimation = rootCtx.getDerivedFromVars(json.action_animation);
let actionAnimationList: MaybeMissing<AnyAnimation>[] = [];
let actionAnimationTransition = '';
let animationOpacityStart: number | undefined = undefined;
let animationOpacityEnd: number | undefined = undefined;
let animationScaleStart: number | undefined = undefined;
let animationScaleEnd: number | undefined = undefined;
$: {
if ($jsonActionAnimation) {
actionAnimationList = flattenAnimation($jsonActionAnimation as Animation);
@@ -602,9 +647,6 @@
}
}
let isVisibilityInited = false;
let visibility: Visibility = 'visible';
$: jsonVisibility = rootCtx.getDerivedFromVars(json.visibility);
$: {
const prevVisibility = visibility;
const nextVisibility = correctVisibility($jsonVisibility, visibility);
@@ -659,6 +701,48 @@
}
}
function unmountExtensions(): void {
if (extensions && currentNode) {
const ctx = rootCtx.getExtensionContext();
extensions.forEach(it => {
it.unmountView?.(currentNode, ctx);
});
extensions = null;
}
}
$: if (json && currentNode && !isDeepEqual(json.extensions, prevExtensionsVal)) {
unmountExtensions();
if (Array.isArray(json.extensions)) {
const ctx = rootCtx.getExtensionContext();
extensions = json.extensions.map(it => {
const instance = rootCtx.getExtension(it.id, it.params);
if (instance) {
instance.mountView?.(currentNode, ctx);
}
return instance;
}).filter(Truthy);
}
prevExtensionsVal = json.extensions;
}
function updateDevtool(): void {
if (dev) {
dev.update({
json,
origJson,
templateContext
});
}
}
$: if (json && origJson && templateContext) {
updateDevtool();
}
$: mods = {
...widthMods,
...heightMods,
@@ -675,11 +759,6 @@
'has-custom-focus': Boolean(hasCustomFocus && json.focus)
};
$: jsonTransform = rootCtx.getDerivedFromVars(json.transform);
let pivotXNum = 0;
let pivotYNum = 0;
let transformOrigin: string | undefined;
let transform: string | undefined;
$: {
if ($jsonTransform && $jsonTransform.rotation !== undefined) {
const pivotX = $jsonTransform.pivot_x || {
@@ -797,8 +876,6 @@
rootCtx.registerTooltip(node, tooltip);
});
let dev: DevtoolResult | null = null;
if (devtool && !layoutParams.fakeElement) {
dev = devtool(node, {
json,
@@ -808,31 +885,11 @@
});
}
if (Array.isArray(json.extensions)) {
const ctx = rootCtx.getExtensionContext();
extensions = json.extensions.map(it => {
const instance = rootCtx.getExtension(it.id, it.params);
if (instance) {
instance.mountView?.(node, ctx);
}
return instance;
}).filter(Truthy);
}
return {
destroy() {
if (id) {
stateCtx.unregisterChild(id);
}
if (extensions) {
const ctx = rootCtx.getExtensionContext();
extensions.forEach(it => {
it.unmountView?.(node, ctx);
});
extensions = null;
}
if (dev) {
dev.destroy();
}
@@ -840,9 +897,6 @@
};
}
let hasCustomFocus: boolean;
$: isPointerFocus = rootCtx.isPointerFocus;
function focusHandler() {
if (!json.focus) {
return;
@@ -873,6 +927,8 @@
json.tooltips?.forEach(tooltip => {
rootCtx.unregisterTooltip(tooltip);
});
unmountExtensions();
});
</script>
@@ -1,5 +1,6 @@
<script lang="ts">
import { getContext, onDestroy } from 'svelte';
import type { Unsubscriber } from 'svelte/store';
import css from './Video.module.css';
@@ -24,12 +25,53 @@
export let layoutParams: LayoutParams | undefined = undefined;
const rootCtx = getContext<RootCtxValue>(ROOT_CTX);
let prevId: string | undefined;
let hasError = false;
let isSelfVariableSet = false;
let videoElem: HTMLVideoElement;
const jsonSource = rootCtx.getDerivedFromVars(json.video_sources);
let sources: PreparedVideoSource[] = [];
let loop = false;
let autoplay = false;
let muted = false;
let poster: string | undefined = undefined;
let scale = 'fit';
let aspectPaddingBottom = '0';
let elapsedVariableUnsubscriber: Unsubscriber | undefined;
$: if (json) {
loop = false;
autoplay = false;
muted = false;
poster = undefined;
scale = 'fit';
}
$: elapsedVariableName = json.elapsed_time_variable;
$: elapsedVariable = elapsedVariableName && rootCtx.getVariable(elapsedVariableName, 'integer') || createVariable('temp', 'integer', 0);
$: if (elapsedVariable) {
if (elapsedVariableUnsubscriber) {
elapsedVariableUnsubscriber();
}
elapsedVariableUnsubscriber = elapsedVariable.subscribe(val => {
if (isSelfVariableSet) {
isSelfVariableSet = false;
return;
}
if (videoElem) {
videoElem.currentTime = Number(val) / 1000;
}
});
}
$: jsonSource = rootCtx.getDerivedFromVars(json.video_sources);
$: jsonRepeatable = rootCtx.getDerivedFromVars(json.repeatable);
$: jsonAutostart = rootCtx.getDerivedFromVars(json.autostart);
$: jsonMuted = rootCtx.getDerivedFromVars(json.muted);
$: jsonPreview = rootCtx.getDerivedFromVars(json.preview);
$: jsonScale = rootCtx.getDerivedFromVars(json.scale);
$: jsonAspect = rootCtx.getDerivedFromVars(json.aspect);
$: {
sources = correctVideoSource($jsonSource, sources);
@@ -42,30 +84,18 @@
}
}
const jsonRepeatable = rootCtx.getDerivedFromVars(json.repeatable);
let loop = false;
$: loop = correctBooleanInt($jsonRepeatable, loop);
const jsonAutostart = rootCtx.getDerivedFromVars(json.autostart);
let autoplay = false;
$: autoplay = correctBooleanInt($jsonAutostart, autoplay);
const jsonMuted = rootCtx.getDerivedFromVars(json.muted);
let muted = false;
$: muted = correctBooleanInt($jsonMuted, muted);
const jsonPreview = rootCtx.getDerivedFromVars(json.preview);
let poster: string | undefined = undefined;
$: poster = typeof $jsonPreview === 'string' ? prepareBase64($jsonPreview) : poster;
$: jsonScale = rootCtx.getDerivedFromVars(json.scale);
let scale = 'fit';
$: {
scale = videoSize($jsonScale) || scale;
}
$: jsonAspect = rootCtx.getDerivedFromVars(json.aspect);
let aspectPaddingBottom = '0';
$: {
const newRatio = $jsonAspect?.ratio;
if (newRatio && isPositiveNumber(newRatio)) {
@@ -75,38 +105,33 @@
}
}
const elapsedVariableName = json.elapsed_time_variable;
let elapsedVariable = elapsedVariableName && rootCtx.getVariable(elapsedVariableName, 'integer') || createVariable('temp', 'integer', 0);
elapsedVariable.subscribe(val => {
if (isSelfVariableSet) {
isSelfVariableSet = false;
return;
$: if (json) {
if (prevId) {
rootCtx.unregisterInstance(prevId);
prevId = undefined;
}
if (videoElem) {
videoElem.currentTime = Number(val) / 1000;
}
});
if (json.id && !hasError && !layoutParams?.fakeElement) {
rootCtx.registerInstance<VideoElements>(json.id, {
pause() {
videoElem?.pause();
},
start() {
const res = videoElem?.play();
if (res) {
res.catch(err => {
rootCtx.logError(wrapError(new Error('Video playing error'), {
level: 'error',
additional: {
originalText: String(err)
}
}));
});
if (json.id && !hasError && !layoutParams?.fakeElement) {
prevId = json.id;
rootCtx.registerInstance<VideoElements>(json.id, {
pause() {
videoElem?.pause();
},
start() {
const res = videoElem?.play();
if (res) {
res.catch(err => {
rootCtx.logError(wrapError(new Error('Video playing error'), {
level: 'error',
additional: {
originalText: String(err)
}
}));
});
}
}
}
});
});
}
}
$: mods = {
@@ -150,8 +175,14 @@
}
onDestroy(() => {
if (json.id && !hasError && !layoutParams?.fakeElement) {
rootCtx.unregisterInstance(json.id);
if (prevId) {
rootCtx.unregisterInstance(prevId);
prevId = undefined;
}
if (elapsedVariableUnsubscriber) {
elapsedVariableUnsubscriber();
elapsedVariableUnsubscriber = undefined;
}
});
</script>
+3 -6
View File
@@ -62,20 +62,17 @@ export interface RootCtxValue {
customComponents: Map<string, CustomComponentDescription> | undefined;
// Devtool
registerComponent?({
componentDevtool?({
type,
node,
json,
origJson,
templateContext
}: {
type: 'mount' | 'update' | 'destroy';
node: HTMLElement;
json: Partial<DivBaseData>;
origJson: DivBase | undefined;
templateContext: TemplateContext;
}): void;
unregisterComponent?({
node
}: {
node: HTMLElement;
}): void;
}
+35 -2
View File
@@ -3,6 +3,15 @@ import type { DivBase, TemplateContext } from '../../typings/common';
import type { DivBaseData } from '../types/base';
export interface DevtoolResult {
update({
json,
origJson,
templateContext
}: {
json: Partial<DivBaseData>;
origJson?: DivBase | undefined;
templateContext: TemplateContext;
}): void;
destroy(): void;
}
@@ -17,7 +26,8 @@ function devtoolReal(node: HTMLElement, {
templateContext: TemplateContext;
rootCtx: RootCtxValue;
}): DevtoolResult {
rootCtx.registerComponent?.({
rootCtx.componentDevtool?.({
type: 'mount',
node,
json,
origJson,
@@ -25,8 +35,31 @@ function devtoolReal(node: HTMLElement, {
});
return {
update({
json,
origJson,
templateContext
}: {
json: Partial<DivBaseData>;
origJson?: DivBase | undefined;
templateContext: TemplateContext;
}) {
rootCtx.componentDevtool?.({
type: 'update',
node,
json,
origJson,
templateContext
});
},
destroy() {
rootCtx.unregisterComponent?.({ node });
rootCtx.componentDevtool?.({
type: 'destroy',
node,
json,
origJson,
templateContext
});
}
};
}
@@ -12,3 +12,5 @@ export function constStore<T>(val: T): Readable<T> {
}
};
}
export const constUndefStore = constStore(undefined);
+1
View File
@@ -18,6 +18,7 @@ import type { CustomComponentDescription } from './custom';
export interface DivkitDebugInstance extends DivkitInstance {
getDebugVariables(): Map<string, Variable>;
getDebugAllVariables(): Map<string, Variable>;
}
export function render(opts: {
+4 -4
View File
@@ -228,14 +228,11 @@ export type StatCallback = (details: {
export type CustomActionCallback = (action: Action & { url: string }) => void;
export type ComponentCallback = (details: {
type: 'mount';
type: 'mount' | 'update' | 'destroy';
node: HTMLElement;
json: DivBase;
origJson: DivBase | undefined;
templateContext: TemplateContext;
} | {
type: 'destroy';
node: HTMLElement;
}) => void;
export interface WrappedError extends Error {
@@ -256,7 +253,10 @@ export type FetchInit = RequestInit | ((url: string) => RequestInit);
export interface DivkitInstance {
$destroy(): void;
execAction(action: Action | VisibilityAction): void;
/** @deprecated */
setTheme(theme: Theme): void;
/** Experimental */
setData(json: DivJson): void;
}
export type Platform = 'desktop' | 'touch' | 'auto';