mirror of
https://github.com/hcengineering/platform.git
synced 2025-06-07 16:30:49 +00:00
Restrictions for InlineStyleToolbar (#3828)
Signed-off-by: Alexander Platov <sas_lord@mail.ru>
This commit is contained in:
parent
4d2ec444b4
commit
b4795677ad
@ -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}
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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>
|
||||
|
@ -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
|
||||
|
@ -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}
|
||||
|
@ -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[]>([])
|
||||
|
@ -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}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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}
|
||||
|
@ -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 })
|
||||
|
@ -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
|
||||
|
@ -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">
|
||||
|
@ -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">
|
||||
|
@ -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
|
||||
|
@ -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} />
|
||||
|
@ -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">
|
||||
|
@ -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
|
||||
}}
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user