Restrictions for InlineStyleToolbar (#3828)

Signed-off-by: Alexander Platov <sas_lord@mail.ru>
This commit is contained in:
Alexander Platov 2023-10-12 06:48:51 +03:00 committed by GitHub
parent 4d2ec444b4
commit b4795677ad
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 103 additions and 27 deletions

View File

@ -52,9 +52,9 @@
export let isFullSize = false
export let embedded = false
export let contentClasses: string | undefined = undefined
export let content: HTMLElement | undefined = undefined
let lastHref: string
let scroll: HTMLDivElement
let timer: any
let lastScrollHeight: number = -1
let count: number = 0
@ -68,16 +68,16 @@
if (scroll == null) {
return
}
if (lastScrollHeight <= scroll.scrollHeight && count <= waitCount) {
count = lastScrollHeight < scroll.scrollHeight ? 0 : count + 1
lastScrollHeight = scroll.scrollHeight
if (content !== undefined && lastScrollHeight <= content?.scrollHeight && count <= waitCount) {
count = lastScrollHeight < content.scrollHeight ? 0 : count + 1
lastScrollHeight = content.scrollHeight
startScrollHeightCheck()
} else {
} else if (content !== undefined) {
lastScrollHeight = -1
count = 0
scroll.scrollTop = $PanelScrollTop[window.location.href]
content.scrollTop = $PanelScrollTop[window.location.href]
$PanelScrollTop[window.location.href] = 0
lastHref = window.location.href
}
@ -195,7 +195,7 @@
</svelte:fragment>
{#if $deviceInfo.isMobile}
<div class="popupPanel-body__mobile-content clear-mins" class:max={useMaxWidth}>
<div bind:this={content} class="popupPanel-body__mobile-content clear-mins" class:max={useMaxWidth}>
<slot />
{#if !withoutActivity}
{#key object._id}
@ -208,7 +208,7 @@
</div>
{:else}
<Scroller
bind:divScroll={scroll}
bind:divScroll={content}
on:divScrollTop={(event) => {
if (lastHref === window.location.href && event && event.detail !== $PanelScrollTop[lastHref]) {
$PanelScrollTop[lastHref] = event.detail
@ -221,7 +221,13 @@
{#key object._id}
<Component
is={activity.component.Activity}
props={{ object, showCommenInput: !withoutInput, shouldScroll: embedded, focusIndex: 1000 }}
props={{
object,
showCommenInput: !withoutInput,
shouldScroll: embedded,
focusIndex: 1000,
boundary: content
}}
/>
{/key}
{/if}

View File

@ -61,6 +61,7 @@
export let textNodeActions: TextNodeAction[] = []
export let editorAttributes: { [name: string]: string } = {}
export let onExtensions: () => AnyExtension[] = () => []
export let boundary: HTMLElement | undefined = undefined
let element: HTMLElement
@ -227,6 +228,21 @@
...defaultExtensions,
Placeholder.configure({ placeholder: placeHolderStr }),
InlineStyleToolbarExtension.configure({
tippyOptions: {
popperOptions: {
modifiers: [
{
name: 'preventOverflow',
options: {
boundary,
padding: 8,
altAxis: true,
tether: false
}
}
]
}
},
element: inlineToolbar,
isSupported: () => !readonly,
isSelectionOnly: () => false

View File

@ -51,6 +51,7 @@
export let extraActions: RefAction[] | undefined = undefined
export let loading: boolean = false
export let focusable: boolean = false
export let boundary: HTMLElement | undefined = undefined
const client = getClient()
const dispatch = createEventDispatcher()
@ -169,6 +170,7 @@
<TextEditor
bind:content
bind:this={textEditor}
{boundary}
on:content={(ev) => {
if (!isEmpty || haveAttachment) {
dispatch('message', ev.detail)

View File

@ -45,6 +45,7 @@
export let enableBackReferences: boolean = false
export let enableEmojiReplace: boolean = true
export let isScrollable: boolean = true
export let boundary: HTMLElement | undefined = undefined
export let attachFile: FileAttachFunction | undefined = undefined
@ -212,6 +213,7 @@
{autofocus}
{isScrollable}
{extensions}
{boundary}
bind:content={rawValue}
bind:this={textEditor}
on:attach

View File

@ -49,6 +49,7 @@
export let full = false
export let extensions: AnyExtension[] = []
export let editorAttributes: { [name: string]: string } = {}
export let boundary: HTMLElement | undefined = undefined
export let textFormatCategories: TextFormatCategory[] = [
TextFormatCategory.Heading,
TextFormatCategory.TextDecoration,
@ -249,6 +250,7 @@
on:blur
on:focus
supportSubmit={false}
{boundary}
/>
{/if}
</div>

View File

@ -37,6 +37,7 @@
export let textFormatCategories: TextFormatCategory[] = []
export let supportSubmit = true
export let editorAttributes: { [name: string]: string } = {}
export let boundary: HTMLElement | undefined = undefined
let element: HTMLElement
let editor: Editor
@ -137,6 +138,21 @@
Placeholder.configure({ placeholder: placeHolderStr }),
...extensions,
InlineStyleToolbarExtension.configure({
tippyOptions: {
popperOptions: {
modifiers: [
{
name: 'preventOverflow',
options: {
boundary,
padding: 8,
altAxis: true,
tether: false
}
}
]
}
},
element: textEditorToolbar,
isSupported: () => true,
isSelectionOnly: () => false

View File

@ -20,7 +20,8 @@
deviceOptionsStore as deviceInfo,
getSeparators,
saveSeparator,
SeparatedElement
SeparatedElement,
separatorsStore
} from '..'
export let prevElementSize: SeparatedItem | undefined = undefined
@ -28,6 +29,7 @@
export let separatorSize: number = 1
export let color: string = 'var(--theme-divider-color)'
export let name: string
export let disabledWhen: string[] = []
export let index: number // index = -1 ; for custom sizes without saving to a localStorage
const direction: 'horizontal' | 'vertical' = 'horizontal'
@ -52,6 +54,7 @@
maxEnd: -1
}
let parentSize: { start: number; end: number; size: number } | null = null
let disabled: boolean = false
const fetchSeparators = (): void => {
separators = getSeparators(name)
@ -384,9 +387,14 @@
mounted = true
}
document.addEventListener('resize', checkSizes)
if ($separatorsStore.filter((f) => f === name).length === 0) $separatorsStore = [...$separatorsStore, name]
disabled = $separatorsStore.filter((f) => disabledWhen.findIndex((d) => d === f) !== -1).length > 0
})
onDestroy(() => {
document.removeEventListener('resize', checkSizes)
if ($separatorsStore.filter((f) => f === name).length > 0) {
$separatorsStore = $separatorsStore.filter((f) => f !== name)
}
})
afterUpdate(() => {
if (mounted) checkSibling()
@ -397,6 +405,7 @@
bind:this={separator}
style:--separator-size={`${separatorSize}px`}
style:background-color={color}
style:pointer-events={disabled ? 'none' : 'all'}
class="antiSeparator {direction}"
class:hovered={isSeparate}
data-size={separatorSize}

View File

@ -10,6 +10,7 @@
//
// See the License for the specific language governing permissions and
import { writable } from 'svelte/store'
import { DelayedCaller } from './utils'
import type { SeparatedItem, DefSeparators } from './types'
@ -123,3 +124,5 @@ export const timeSeparators: DefSeparators = [
{ minSize: 25, size: 35, maxSize: 45 },
null
]
export const separatorsStore = writable<string[]>([])

View File

@ -30,6 +30,7 @@
export let transparent: boolean = false
export let shouldScroll: boolean = false
export let focusIndex: number = -1
export let boundary: HTMLElement | undefined = undefined
getResource(notification.function.GetNotificationClient).then((res) => {
updatesStore = res().docUpdatesStore
@ -156,6 +157,7 @@
isNew={newTxIndexes.includes(i)}
isNextNew={newTxIndexes.includes(i + 1)}
shouldScroll={i === scrollIndex}
{boundary}
/>
</Lazy>
{/each}
@ -164,7 +166,7 @@
</div>
{#if showCommenInput}
<div class="ref-input">
<Component is={chunter.component.CommentInput} props={{ object, focusIndex }} />
<Component is={chunter.component.CommentInput} props={{ object, focusIndex, boundary }} />
</div>
{/if}

View File

@ -49,6 +49,7 @@
export let isNextNew: boolean = false
export let contentHidden: boolean = false
export let shouldScroll: boolean = false
export let boundary: HTMLElement | undefined = undefined
// export let showDocument = false
let ptx: DisplayTx | undefined
@ -80,7 +81,7 @@
const employeeQuery = createQuery()
function getProps (props: any, edit: boolean): any {
return { ...props, edit, attr: tx.collectionAttribute }
return { ...props, edit, attr: tx.collectionAttribute, boundary }
}
$: updateViewlet(client, viewlets, tx).then((result) => {
@ -501,7 +502,6 @@
}
.activity-content {
overflow: hidden;
visibility: visible;
min-width: 0;
max-height: max-content;

View File

@ -41,6 +41,7 @@
}
export let placeholder: IntlString | undefined = undefined
export let extraActions: RefAction[] | undefined = undefined
export let boundary: HTMLElement | undefined = undefined
let refInput: ReferenceInput
@ -285,6 +286,7 @@
{showSend}
showHeader={attachments.size > 0 || progress}
{loading}
{boundary}
on:focus
on:blur
on:message={onMessage}

View File

@ -27,6 +27,7 @@
export let key: KeyedAttribute
export let placeholder: IntlString
export let focusIndex = -1
export let boundary: HTMLElement | undefined = undefined
const client = getClient()
const queryClient = createQuery()
@ -125,6 +126,7 @@
focusable
bind:content={description}
{placeholder}
{boundary}
on:open-document={async (event) => {
save(object, description)
const doc = await client.findOne(event.detail._class, { _id: event.detail._id })

View File

@ -48,6 +48,7 @@
export let isScrollable = true
export let useDirectAttachDelete = false
export let boundary: HTMLElement | undefined = undefined
let progress = false
@ -384,6 +385,7 @@
{kind}
{enableBackReferences}
{isScrollable}
{boundary}
on:changeSize
on:changeContent
on:blur

View File

@ -32,8 +32,8 @@
export let savedMessagesIds: Ref<ChunterMessage>[]
export let savedAttachmentsIds: Ref<Attachment>[]
export let isScrollForced = false
export let content: HTMLElement | undefined = undefined
let div: HTMLDivElement | undefined
let autoscroll: boolean = false
const unsubscribe = locationStore.subscribe((newLocation) => {
@ -54,7 +54,7 @@
onDestroy(unsubscribe)
beforeUpdate(() => {
autoscroll = div !== undefined && div.offsetHeight + div.scrollTop > div.scrollHeight - 20
autoscroll = content !== undefined && content.offsetHeight + content.scrollTop > content.scrollHeight - 20
})
afterUpdate(() => {
@ -63,8 +63,8 @@
return
}
if (div && (autoscroll || isScrollForced)) {
div.scrollTo(0, div.scrollHeight)
if (content && (autoscroll || isScrollForced)) {
content.scrollTo(0, content.scrollHeight)
isScrollForced = false
}
})
@ -135,7 +135,7 @@
return
}
const dateSelectors = div?.getElementsByClassName('dateSelector')
const dateSelectors = content?.getElementsByClassName('dateSelector')
if (!dateSelectors) return
let closestDate: Timestamp | undefined = parseInt(dateSelectors[dateSelectors.length - 1].id)
@ -161,7 +161,7 @@
if (offset) {
offset = offset - headerHeight - dateSelectorHeight / 2
if (pinnedIds.length > 0) offset = offset - pinnedHeight
div?.scrollTo({ left: 0, top: offset })
content?.scrollTo({ left: 0, top: offset })
}
}
@ -176,10 +176,10 @@
const dateSelectorHeight = 30
function getFirstVisible (): Element | undefined {
if (!div) return
if (!content) return
const clientRect = div.getBoundingClientRect()
const dateSelectors = div.getElementsByClassName('dateSelector')
const clientRect = content.getBoundingClientRect()
const dateSelectors = content.getElementsByClassName('dateSelector')
const firstVisible = Array.from(dateSelectors)
.reverse()
.find((child) => {
@ -198,7 +198,7 @@
}
</script>
<div class="flex-col vScroll" bind:this={div} on:scroll={handleScroll}>
<div class="flex-col vScroll" bind:this={content} on:scroll={handleScroll}>
<div class="grower" />
{#if showFixed}
<div class="ml-2 pr-2 fixed">

View File

@ -83,11 +83,13 @@
savedAttachmentsIds = res.map((r) => r.attachedTo)
})
let loading = false
let content: HTMLElement
</script>
<PinnedMessages {space} {pinnedIds} />
<Channel
bind:isScrollForced
bind:content
{space}
on:openThread={(e) => {
openThread(e.detail)
@ -97,7 +99,7 @@
{savedAttachmentsIds}
/>
<div class="reference">
<AttachmentRefInput bind:loading {space} {_class} objectId={_id} on:message={onMessage} />
<AttachmentRefInput bind:loading {space} {_class} objectId={_id} boundary={content} on:message={onMessage} />
</div>
<style lang="scss">

View File

@ -23,6 +23,7 @@
export let object: Doc
export let shouldSaveDraft: boolean = true
export let focusIndex: number = -1
export let boundary: HTMLElement | undefined = undefined
const client = getClient()
const _class = chunter.class.Comment
@ -124,6 +125,7 @@
space={object.space}
bind:objectId={_id}
{shouldSaveDraft}
{boundary}
on:message={onMessage}
on:update={onUpdate}
on:focus

View File

@ -26,6 +26,7 @@
export let tx: TxCreateDoc<Comment>
export let value: Comment
export let edit: boolean = false
export let boundary: HTMLElement | undefined = undefined
const client = getClient()
const dispatch = createEventDispatcher()
@ -89,15 +90,16 @@
content={value.message}
on:message={onMessage}
showSend={false}
{boundary}
/>
<div class="flex-row-reverse gap-2 reverse">
<div class="flex-row-center gap-2 justify-end mt-2">
<Button
label={chunter.string.EditCancel}
on:click={() => {
dispatch('close', false)
}}
/>
<Button label={chunter.string.EditUpdate} on:click={() => refInput.submit()} />
<Button label={chunter.string.EditUpdate} accent on:click={() => refInput.submit()} />
</div>
{:else}
<MessageViewer message={value.message} />

View File

@ -132,7 +132,7 @@
departmentById={departments}
on:selected={(e) => departmentSelected(e.detail)}
/>
<Separator name={'workbench'} index={0} color={'var(--theme-navpanel-border)'} />
<Separator name={'workbench'} disabledWhen={['panel-aside']} index={0} color={'var(--theme-navpanel-border)'} />
{/if}
<div class="antiPanel-component filled">

View File

@ -161,6 +161,8 @@
return undefined
}
$: editorFooter = getEditorFooter(issue?._class)
let content: HTMLElement
</script>
{#if !embedded}
@ -180,6 +182,7 @@
isSub={false}
withoutActivity={false}
withoutTitle
bind:content
{embedded}
bind:innerWidth
on:open
@ -233,6 +236,7 @@
key={{ key: 'description', attr: descriptionKey }}
bind:this={descriptionBox}
placeholder={tracker.string.IssueDescriptionPlaceholder}
boundary={content}
on:saved={(evt) => {
saved = evt.detail
}}

View File

@ -279,6 +279,7 @@
}
$: finalTitle = title ?? rawTitle
let content: HTMLElement
</script>
{#if !embedded}
@ -297,6 +298,7 @@
{embedded}
isHeader={mainEditor?.pinned ?? false}
isAside={true}
bind:content
bind:panelWidth
bind:innerWidth
on:open