mirror of
https://github.com/hcengineering/platform.git
synced 2025-04-24 01:07:50 +00:00
TSK-336: mobile UI adaptation (#3492)
Signed-off-by: Alexander Platov <sas_lord@mail.ru>
This commit is contained in:
parent
5dc40b1730
commit
b2edbadf05
@ -25,7 +25,9 @@
|
||||
handler,
|
||||
registerFocus,
|
||||
showPopup,
|
||||
tooltip
|
||||
tooltip,
|
||||
deviceOptionsStore as deviceInfo,
|
||||
checkAdaptiveMatching
|
||||
} from '@hcengineering/ui'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { Completion } from '../Completion'
|
||||
@ -73,6 +75,8 @@
|
||||
let isEmpty = true
|
||||
|
||||
$: setContent(content)
|
||||
$: devSize = $deviceInfo.size
|
||||
$: shrinkButtons = checkAdaptiveMatching(devSize, 'sm')
|
||||
|
||||
function setContent (content: string) {
|
||||
textEditor?.setContent(content)
|
||||
@ -379,7 +383,7 @@
|
||||
{/if}
|
||||
</div>
|
||||
<div class="flex-between clear-mins" style:margin={'.75rem .75rem 0'}>
|
||||
<div class="buttons-group large-gap">
|
||||
<div class="buttons-group {shrinkButtons ? 'medium-gap' : 'large-gap'}">
|
||||
{#each actions as a}
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<div
|
||||
@ -395,7 +399,7 @@
|
||||
{/each}
|
||||
</div>
|
||||
{#if extraActions && extraActions.length > 0}
|
||||
<div class="buttons-group large-gap">
|
||||
<div class="buttons-group {shrinkButtons ? 'medium-gap' : 'large-gap'}">
|
||||
{#each extraActions as a}
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<div
|
||||
|
@ -23,7 +23,9 @@
|
||||
IconSize,
|
||||
Scroller,
|
||||
SelectPopup,
|
||||
showPopup
|
||||
showPopup,
|
||||
deviceOptionsStore as deviceInfo,
|
||||
checkAdaptiveMatching
|
||||
} from '@hcengineering/ui'
|
||||
import { Level } from '@tiptap/extension-heading'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
@ -445,14 +447,9 @@
|
||||
}
|
||||
)
|
||||
}
|
||||
$: buttonsGap =
|
||||
buttonSize === 'large' || buttonSize === 'x-large' || buttonSize === 'full'
|
||||
? 'large-gap'
|
||||
: buttonSize === 'medium'
|
||||
? 'medium-gap'
|
||||
: buttonSize === 'small'
|
||||
? 'small-gap'
|
||||
: 'xsmall-gap'
|
||||
|
||||
$: devSize = $deviceInfo.size
|
||||
$: buttonsGap = checkAdaptiveMatching(devSize, 'sm') ? 'small-gap' : 'large-gap'
|
||||
$: buttonsHeight =
|
||||
buttonSize === 'large' || buttonSize === 'x-large' || buttonSize === 'full'
|
||||
? 'h-6 max-h-6'
|
||||
|
@ -263,7 +263,7 @@ input.search {
|
||||
}
|
||||
.justify-between { justify-content: space-between; }
|
||||
.justify-start { justify-content: flex-start; }
|
||||
.justify-end { justify-content: flex-end; }
|
||||
.justify-end { justify-content: flex-end !important; }
|
||||
.justify-center { justify-content: center; }
|
||||
.justify-stretch { justify-content: stretch; }
|
||||
.items-baseline { align-items: baseline; }
|
||||
|
@ -41,8 +41,8 @@
|
||||
&.only-icon { width: 2.75rem; }
|
||||
}
|
||||
|
||||
&.iconL:not(.iconR) { padding: 0 1rem 0 .75rem; }
|
||||
&.iconR:not(.iconL) { padding: 0 .75rem 0 1rem; }
|
||||
&.iconL:not(.iconR, .only-icon) { padding: 0 1rem 0 .75rem; }
|
||||
&.iconR:not(.iconL, .only-icon) { padding: 0 .75rem 0 1rem; }
|
||||
.btn-icon {
|
||||
color: var(--theme-content-color);
|
||||
transition: color .15s;
|
||||
@ -88,7 +88,10 @@
|
||||
&.bs-dashed { border-style: dashed; }
|
||||
&.jf-left { justify-content: flex-start; }
|
||||
&.jf-center { justify-content: center; }
|
||||
&.only-icon { padding: 0; }
|
||||
&.only-icon {
|
||||
flex-shrink: 0 !important;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
&.regular {
|
||||
background-color: var(--theme-button-default);
|
||||
|
@ -17,7 +17,16 @@
|
||||
import { onMount, ComponentType } from 'svelte'
|
||||
import { registerFocus } from '../focus'
|
||||
import { tooltip } from '../tooltips'
|
||||
import type { AnySvelteComponent, ButtonKind, ButtonShape, ButtonSize, LabelAndProps, IconProps } from '../types'
|
||||
import type {
|
||||
AnySvelteComponent,
|
||||
ButtonKind,
|
||||
ButtonShape,
|
||||
ButtonSize,
|
||||
LabelAndProps,
|
||||
IconProps,
|
||||
WidthType
|
||||
} from '../types'
|
||||
import { checkAdaptiveMatching, deviceOptionsStore as deviceInfo } from '..'
|
||||
import Icon from './Icon.svelte'
|
||||
import Label from './Label.svelte'
|
||||
import Spinner from './Spinner.svelte'
|
||||
@ -51,6 +60,7 @@
|
||||
export let shrink: number = 0
|
||||
export let accent: boolean = false
|
||||
export let noFocus: boolean = false
|
||||
export let adaptiveShrink: WidthType | null = null
|
||||
|
||||
$: iconSize =
|
||||
iconProps && iconProps.size !== undefined ? iconProps.size : size && size === 'inline' ? 'inline' : 'small'
|
||||
@ -62,6 +72,9 @@
|
||||
(icon !== undefined || iconRight !== undefined || $$slots.icon || $$slots.iconRight)
|
||||
$: primary = ['accented', 'brand', 'positive', 'negative'].some((p) => p === kind)
|
||||
|
||||
$: devSize = $deviceInfo.size
|
||||
$: adaptive = adaptiveShrink !== null ? checkAdaptiveMatching(devSize, adaptiveShrink) : false
|
||||
|
||||
onMount(() => {
|
||||
if (focus && input) {
|
||||
input.focus()
|
||||
@ -102,7 +115,7 @@
|
||||
use:tooltip={showTooltip}
|
||||
bind:this={input}
|
||||
class="antiButton {kind} {size} jf-{justify} sh-{shape ?? 'no-shape'} bs-{borderStyle}"
|
||||
class:only-icon={iconOnly}
|
||||
class:only-icon={iconOnly || adaptive}
|
||||
class:no-focus={noFocus}
|
||||
class:accent
|
||||
class:highlight
|
||||
@ -138,7 +151,7 @@
|
||||
<Spinner size={iconSize === 'inline' ? 'inline' : 'small'} />
|
||||
</div>
|
||||
{/if}
|
||||
{#if label}
|
||||
{#if label && !adaptive}
|
||||
<span class="overflow-label label disabled pointer-events-none" class:ml-2={loading}>
|
||||
<Label {label} params={labelParams} />
|
||||
</span>
|
||||
|
@ -27,10 +27,11 @@
|
||||
|
||||
export let icon: Asset | AnySvelteComponent | undefined = undefined
|
||||
export let label: IntlString = ui.string.DropdownDefaultLabel
|
||||
export let params: Record<string, any> = {}
|
||||
export let items: DropdownIntlItem[]
|
||||
export let selected: DropdownIntlItem['id'] | undefined = undefined
|
||||
export let disabled: boolean = false
|
||||
export let kind: ButtonKind = 'no-border'
|
||||
export let kind: ButtonKind = 'regular'
|
||||
export let size: ButtonSize = 'small'
|
||||
export let justify: 'left' | 'center' = 'center'
|
||||
export let width: string | undefined = undefined
|
||||
@ -86,7 +87,7 @@
|
||||
on:click={openPopup}
|
||||
>
|
||||
<span slot="content" class="overflow-label disabled flex-grow text-left mr-2">
|
||||
<Label label={selectedItem ? selectedItem.label : label} />
|
||||
<Label label={selectedItem ? selectedItem.label : label} {params} />
|
||||
</span>
|
||||
<svelte:fragment slot="iconRight">
|
||||
<DropdownIcon size={'small'} fill={'var(--theme-dark-color)'} />
|
||||
|
@ -50,7 +50,7 @@
|
||||
dispatch('close', item.id)
|
||||
}}
|
||||
>
|
||||
<div class="flex-grow caption-color nowrap"><Label label={item.label} /></div>
|
||||
<div class="flex-grow caption-color nowrap"><Label label={item.label} params={item.params} /></div>
|
||||
<div class="check">
|
||||
{#if item.id === selected}<IconCheck size={'small'} />{/if}
|
||||
</div>
|
||||
|
@ -19,6 +19,7 @@
|
||||
items={modeList}
|
||||
selected={props.mode}
|
||||
kind={'separated'}
|
||||
adaptiveShrink={'sm'}
|
||||
on:select={(result) => {
|
||||
if (result.detail !== undefined && result.detail.action) result.detail.action()
|
||||
}}
|
||||
|
@ -14,7 +14,7 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { afterUpdate, createEventDispatcher, onMount } from 'svelte'
|
||||
import { deviceOptionsStore as deviceInfo } from '../../'
|
||||
import { deviceOptionsStore as deviceInfo, checkAdaptiveMatching } from '../../'
|
||||
import { resizeObserver } from '../resize'
|
||||
import Button from './Button.svelte'
|
||||
import Scroller from './Scroller.svelte'
|
||||
@ -41,7 +41,10 @@
|
||||
let asideFloat: boolean = false
|
||||
let asideShown: boolean = true
|
||||
let fullSize: boolean = false
|
||||
$: twoRows = $deviceInfo.minWidth
|
||||
|
||||
$: devSize = $deviceInfo.size
|
||||
$: twoRows = checkAdaptiveMatching(devSize, 'xs')
|
||||
$: moveUtils = checkAdaptiveMatching(devSize, 'sm')
|
||||
|
||||
let oldWidth = ''
|
||||
let hideTimer: number | undefined
|
||||
@ -109,7 +112,9 @@
|
||||
{#if !twoRows && !withoutTitle}<slot name="title" />{/if}
|
||||
</div>
|
||||
<div class="buttons-group xsmall-gap">
|
||||
<slot name="utils" />
|
||||
{#if !moveUtils}
|
||||
<slot name="utils" />
|
||||
{/if}
|
||||
{#if isFullSize || useMaxWidth !== undefined || ($$slots.aside && isAside)}
|
||||
<div class="buttons-divider" />
|
||||
{/if}
|
||||
@ -193,6 +198,11 @@
|
||||
{/if}
|
||||
{#if $$slots.aside && isAside && asideShown}
|
||||
<div class="popupPanel-body__aside" class:float={asideFloat} class:shown={asideShown}>
|
||||
{#if moveUtils}
|
||||
<div class="buttons-group justify-end xsmall-gap" style:margin={'.5rem 2rem 0'}>
|
||||
<slot name="utils" />
|
||||
</div>
|
||||
{/if}
|
||||
<slot name="aside" />
|
||||
</div>
|
||||
{/if}
|
||||
|
@ -15,9 +15,11 @@
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { tooltip } from '../tooltips'
|
||||
import type { TabItem, IconSize } from '../types'
|
||||
import type { TabItem, IconSize, WidthType, DropdownIntlItem } from '../types'
|
||||
import Icon from './Icon.svelte'
|
||||
import Label from './Label.svelte'
|
||||
import DropdownLabelsIntl from './DropdownLabelsIntl.svelte'
|
||||
import { checkAdaptiveMatching, deviceOptionsStore as deviceInfo } from '..'
|
||||
|
||||
export let selected: string | string[] = ''
|
||||
export let multiselect: boolean = false
|
||||
@ -25,6 +27,7 @@
|
||||
export let kind: 'normal' | 'regular' | 'plain' | 'separated' | 'separated-free' = 'normal'
|
||||
export let onlyIcons: boolean = false
|
||||
export let size: 'small' | 'medium' = 'medium'
|
||||
export let adaptiveShrink: WidthType | null = null
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
@ -41,52 +44,72 @@
|
||||
|
||||
let iconSize: IconSize
|
||||
$: iconSize = onlyIcons ? (size === 'small' ? 'small' : 'medium') : size === 'small' ? 'x-small' : 'small'
|
||||
|
||||
$: devSize = $deviceInfo.size
|
||||
$: adaptive = adaptiveShrink !== null ? checkAdaptiveMatching(devSize, adaptiveShrink) : false
|
||||
|
||||
let ddItems: DropdownIntlItem[]
|
||||
$: ddItems = items.map((it) => ({ id: it.id, label: it.labelIntl, params: it.labelParams } as DropdownIntlItem))
|
||||
</script>
|
||||
|
||||
{#if items.length > 0}
|
||||
<div class="tablist-container {kind} {size}">
|
||||
{#each items as item, i}
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<div
|
||||
bind:this={tabs[i]}
|
||||
class={kind === 'normal' || kind === 'regular' ? 'button' : 'plain'}
|
||||
class:separated={kind === 'separated' || kind === 'separated-free'}
|
||||
class:free={kind === 'separated-free'}
|
||||
class:onlyIcons
|
||||
class:selected={getSelected(item.id, selected)}
|
||||
data-view={item.tooltip}
|
||||
data-id={`tab-${item.id}`}
|
||||
use:tooltip={{ label: item.tooltip ?? undefined, element: tabs[i] ?? undefined }}
|
||||
on:click={() => {
|
||||
if (multiselect) {
|
||||
if (Array.isArray(selected)) {
|
||||
if (selected.includes(item.id)) selected = selected.filter((it) => it !== item.id)
|
||||
else selected.push(item.id)
|
||||
}
|
||||
} else selected = item.id
|
||||
dispatch('select', item)
|
||||
items = items
|
||||
}}
|
||||
>
|
||||
{#if item.icon}
|
||||
<div class="icon">
|
||||
<Icon icon={item.icon} size={iconSize} fill={item.color ?? 'currentColor'} />
|
||||
</div>
|
||||
{:else if item.color}
|
||||
<div class="color" style:background-color={item.color} />
|
||||
{/if}
|
||||
{#if item.label || item.labelIntl}
|
||||
<span class="overflow-label" class:ml-1-5={item.icon || item.color}>
|
||||
{#if item.label}
|
||||
{item.label}
|
||||
{:else if item.labelIntl}
|
||||
<Label label={item.labelIntl} params={item.labelParams} />
|
||||
{/if}
|
||||
</span>
|
||||
{/if}
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
{#if adaptive}
|
||||
<DropdownLabelsIntl
|
||||
items={ddItems}
|
||||
{size}
|
||||
selected={Array.isArray(selected) ? selected[0] : selected}
|
||||
on:selected={(e) => {
|
||||
const item = items.filter((it) => it.id === e.detail)[0]
|
||||
if (Array.isArray(selected)) selected[0] = item.id
|
||||
else selected = item.id
|
||||
dispatch('select', item)
|
||||
}}
|
||||
/>
|
||||
{:else}
|
||||
<div class="tablist-container {kind} {size}">
|
||||
{#each items as item, i}
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<div
|
||||
bind:this={tabs[i]}
|
||||
class={kind === 'normal' || kind === 'regular' ? 'button' : 'plain'}
|
||||
class:separated={kind === 'separated' || kind === 'separated-free'}
|
||||
class:free={kind === 'separated-free'}
|
||||
class:onlyIcons
|
||||
class:selected={getSelected(item.id, selected)}
|
||||
data-view={item.tooltip}
|
||||
data-id={`tab-${item.id}`}
|
||||
use:tooltip={{ label: item.tooltip ?? undefined, element: tabs[i] ?? undefined }}
|
||||
on:click={() => {
|
||||
if (multiselect) {
|
||||
if (Array.isArray(selected)) {
|
||||
if (selected.includes(item.id)) selected = selected.filter((it) => it !== item.id)
|
||||
else selected.push(item.id)
|
||||
}
|
||||
} else selected = item.id
|
||||
dispatch('select', item)
|
||||
items = items
|
||||
}}
|
||||
>
|
||||
{#if item.icon}
|
||||
<div class="icon">
|
||||
<Icon icon={item.icon} size={iconSize} fill={item.color ?? 'currentColor'} />
|
||||
</div>
|
||||
{:else if item.color}
|
||||
<div class="color" style:background-color={item.color} />
|
||||
{/if}
|
||||
{#if item.label || item.labelIntl}
|
||||
<span class="overflow-label" class:ml-1-5={item.icon || item.color}>
|
||||
{#if item.label}
|
||||
{item.label}
|
||||
{:else if item.labelIntl}
|
||||
<Label label={item.labelIntl} params={item.labelParams} />
|
||||
{/if}
|
||||
</span>
|
||||
{/if}
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
<style lang="scss">
|
||||
|
@ -2,6 +2,7 @@
|
||||
import platform, { addEventListener, getMetadata, OK, PlatformEvent, Status } from '@hcengineering/platform'
|
||||
import { onDestroy } from 'svelte'
|
||||
import type { AnyComponent, WidthType } from '../../types'
|
||||
import { deviceSizes, deviceWidths } from '../../types'
|
||||
// import { applicationShortcutKey } from '../../utils'
|
||||
import { getCurrentLocation, location, navigate, locationStorageKeyId } from '../../location'
|
||||
|
||||
@ -104,8 +105,6 @@
|
||||
let remove: any = null
|
||||
const sizes: Record<WidthType, boolean> = { xs: false, sm: false, md: false, lg: false, xl: false, xxl: false }
|
||||
const css: Record<WidthType, string> = { xs: '', sm: '', md: '', lg: '', xl: '', xxl: '' }
|
||||
const deviceSizes: WidthType[] = ['xs', 'sm', 'md', 'lg', 'xl', 'xxl']
|
||||
const deviceWidths = [480, 680, 760, 1024, 1208, -1]
|
||||
deviceSizes.forEach((ds, i) => {
|
||||
if (i === 0) css[ds] = `(max-width: ${deviceWidths[i]}px)`
|
||||
else if (i === deviceSizes.length - 1) css[ds] = `(min-width: ${deviceWidths[i - 1]}.01px)`
|
||||
|
@ -266,6 +266,7 @@ export interface DropdownTextItem {
|
||||
export interface DropdownIntlItem {
|
||||
id: string | number
|
||||
label: IntlString
|
||||
params?: Record<string, any>
|
||||
}
|
||||
|
||||
export interface PopupOptions {
|
||||
@ -304,6 +305,8 @@ export const issueSP: FadeOptions = { multipler: { top: 2.75, bottom: 0 } }
|
||||
export const emojiSP: FadeOptions = { multipler: { top: 1.5, bottom: 0 } }
|
||||
|
||||
export type WidthType = 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'xxl'
|
||||
export const deviceSizes: WidthType[] = ['xs', 'sm', 'md', 'lg', 'xl', 'xxl']
|
||||
export const deviceWidths = [480, 680, 760, 1024, 1208, -1]
|
||||
|
||||
export interface DeviceOptions {
|
||||
docWidth: number
|
||||
|
@ -19,7 +19,7 @@ import { IntlString, setMetadata } from '@hcengineering/platform'
|
||||
import autolinker from 'autolinker'
|
||||
import { writable } from 'svelte/store'
|
||||
import { Notification, NotificationPosition, NotificationSeverity, notificationsStore } from '.'
|
||||
import { AnyComponent, AnySvelteComponent } from './types'
|
||||
import { AnyComponent, AnySvelteComponent, WidthType, deviceSizes } from './types'
|
||||
|
||||
/**
|
||||
* @public
|
||||
@ -65,6 +65,14 @@ export function isSafari (): boolean {
|
||||
return navigator.userAgent.toLowerCase().includes('safari/')
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export function checkAdaptiveMatching (size: WidthType | null, limit: WidthType): boolean {
|
||||
const range = new Set(deviceSizes.slice(0, deviceSizes.findIndex((ds) => ds === limit) + 1))
|
||||
return size !== null ? range.has(size) : false
|
||||
}
|
||||
|
||||
export function floorFractionDigits (n: number | string, amount: number): number {
|
||||
return Number(Number(n).toFixed(amount))
|
||||
}
|
||||
|
@ -163,7 +163,7 @@
|
||||
</script>
|
||||
|
||||
<div
|
||||
class="ac-header full divide"
|
||||
class="ac-header full divide caption-height"
|
||||
class:header-with-mode-selector={modeSelectorProps !== undefined}
|
||||
class:header-without-label={!labelTasks}
|
||||
>
|
||||
|
@ -155,6 +155,7 @@
|
||||
isAside={true}
|
||||
isSub={false}
|
||||
withoutActivity={false}
|
||||
withoutTitle
|
||||
{embedded}
|
||||
bind:innerWidth
|
||||
on:open
|
||||
@ -252,15 +253,12 @@
|
||||
/>
|
||||
<Button
|
||||
kind={'ghost'}
|
||||
icon={IconMixin}
|
||||
selected={showAllMixins}
|
||||
on:click={() => {
|
||||
showAllMixins = !showAllMixins
|
||||
}}
|
||||
>
|
||||
<svelte:fragment slot="icon">
|
||||
<IconMixin size={'small'} />
|
||||
</svelte:fragment>
|
||||
</Button>
|
||||
/>
|
||||
</svelte:fragment>
|
||||
|
||||
<svelte:fragment slot="custom-attributes">
|
||||
|
@ -56,6 +56,7 @@
|
||||
icon={view.icon.ViewButton}
|
||||
label={view.string.View}
|
||||
{kind}
|
||||
adaptiveShrink={'sm'}
|
||||
showTooltip={{ label: view.string.CustomizeView, direction: 'bottom' }}
|
||||
bind:input={btn}
|
||||
on:click={clickHandler}
|
||||
|
@ -38,6 +38,7 @@
|
||||
label={view.string.Show}
|
||||
{kind}
|
||||
shrink={1}
|
||||
adaptiveShrink={'sm'}
|
||||
showTooltip={{ label: view.string.CustomizeView, direction: 'bottom' }}
|
||||
bind:input={btn}
|
||||
on:click={clickHandler}
|
||||
|
@ -16,7 +16,7 @@
|
||||
import { AnyAttribute, Doc, getObjectValue } from '@hcengineering/core'
|
||||
import notification from '@hcengineering/notification'
|
||||
import { getClient, updateAttribute } from '@hcengineering/presentation'
|
||||
import { CheckBox, Component, IconCircles, tooltip } from '@hcengineering/ui'
|
||||
import { CheckBox, Component, IconCircles, tooltip, deviceOptionsStore as deviceInfo } from '@hcengineering/ui'
|
||||
import { AttributeModel } from '@hcengineering/view'
|
||||
import { createEventDispatcher, onMount } from 'svelte'
|
||||
import view from '../../plugin'
|
||||
@ -63,6 +63,8 @@
|
||||
return (value: any) => onChange(value, docObject, attribute.key, attr)
|
||||
}
|
||||
|
||||
$: mobile = $deviceInfo.isMobile
|
||||
|
||||
onMount(() => {
|
||||
dispatch('on-mount')
|
||||
})
|
||||
@ -116,16 +118,18 @@
|
||||
{@const displayProps = attributeModel.displayProps}
|
||||
{#if !groupByKey || displayProps?.excludeByKey !== groupByKey}
|
||||
{#if displayProps?.grow}
|
||||
{#each model.filter((p) => p.displayProps?.suffix === true) as attrModel}
|
||||
<ListPresenter
|
||||
{docObject}
|
||||
attributeModel={attrModel}
|
||||
{props}
|
||||
{compactMode}
|
||||
value={getObjectValue(attrModel.key, docObject)}
|
||||
onChange={getOnChange(docObject, attrModel)}
|
||||
/>
|
||||
{/each}
|
||||
{#if !(compactMode && mobile)}
|
||||
{#each model.filter((p) => p.displayProps?.suffix === true) as attrModel}
|
||||
<ListPresenter
|
||||
{docObject}
|
||||
attributeModel={attrModel}
|
||||
{props}
|
||||
{compactMode}
|
||||
value={getObjectValue(attrModel.key, docObject)}
|
||||
onChange={getOnChange(docObject, attrModel)}
|
||||
/>
|
||||
{/each}
|
||||
{/if}
|
||||
<GrowPresenter />
|
||||
{#if !compactMode}
|
||||
<div class="compression-bar">
|
||||
@ -176,6 +180,18 @@
|
||||
<IconCircles />
|
||||
</div>
|
||||
<div class="scroll-box gap-2">
|
||||
{#if mobile}
|
||||
{#each model.filter((p) => p.displayProps?.suffix === true) as attrModel}
|
||||
<ListPresenter
|
||||
{docObject}
|
||||
attributeModel={attrModel}
|
||||
{props}
|
||||
{compactMode}
|
||||
value={getObjectValue(attrModel.key, docObject)}
|
||||
onChange={getOnChange(docObject, attrModel)}
|
||||
/>
|
||||
{/each}
|
||||
{/if}
|
||||
<div class="compression-bar">
|
||||
{#each model.filter((m) => m.displayProps?.compression) as attributeModel, j}
|
||||
{@const displayProps = attributeModel.displayProps}
|
||||
|
Loading…
Reference in New Issue
Block a user