@oslokommune/punkt-react 16.13.3 → 16.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +35 -0
- package/dist/{dialog-polyfill.esm-1hPr0gl9-EorOF9Wq.js → dialog-polyfill.esm-BmVHGRTX-CuhXqEqJ.js} +1 -1
- package/dist/index.d.ts +50 -47
- package/dist/punkt-react.es.js +4560 -3038
- package/dist/punkt-react.umd.js +670 -274
- package/dist/shared-types/fileupload.d.ts +73 -0
- package/dist/shared-types/index.d.ts +2 -0
- package/dist/shared-utils/fileupload/announcements.d.ts +11 -0
- package/dist/shared-utils/fileupload/filename.d.ts +13 -0
- package/dist/shared-utils/fileupload/focus-trap.d.ts +12 -0
- package/dist/shared-utils/fileupload/formats.d.ts +30 -0
- package/dist/shared-utils/fileupload/index.d.ts +15 -0
- package/dist/shared-utils/fileupload/navigation.d.ts +5 -0
- package/dist/shared-utils/fileupload/size.d.ts +12 -0
- package/dist/shared-utils/fileupload/transfers.d.ts +17 -0
- package/dist/shared-utils/fileupload/truncate.d.ts +13 -0
- package/dist/shared-utils/fileupload/validation.d.ts +24 -0
- package/package.json +4 -4
- package/src/components/fileupload/DropZone.tsx +35 -22
- package/src/components/fileupload/FileUpload.test.tsx +165 -10
- package/src/components/fileupload/FileUpload.tsx +116 -184
- package/src/components/fileupload/QueueDisplay.test.tsx +51 -8
- package/src/components/fileupload/QueueDisplay.tsx +71 -99
- package/src/components/fileupload/QueueItemContent.tsx +120 -135
- package/src/components/fileupload/Subcomponents.tsx +175 -144
- package/src/components/fileupload/Truncate.test.tsx +65 -207
- package/src/components/fileupload/Truncate.tsx +24 -120
- package/src/components/fileupload/extensions/Comments.tsx +94 -106
- package/src/components/fileupload/extensions/Remove.tsx +3 -5
- package/src/components/fileupload/extensions/Rename.tsx +45 -42
- package/src/components/fileupload/hooks/index.ts +1 -0
- package/src/components/fileupload/hooks/useFileAttributes.ts +37 -29
- package/src/components/fileupload/hooks/useImagePreview.ts +3 -7
- package/src/components/fileupload/hooks/useOperationState.ts +29 -8
- package/src/components/fileupload/hooks/useRequiredFormValidation.ts +43 -0
- package/src/components/fileupload/hooks.ts +1 -0
- package/src/components/fileupload/types.ts +49 -32
- package/src/components/fileupload/utils.ts +3 -54
- package/src/components/inputwrapper/InputWrapper.tsx +4 -1
- package/src/components/modal/Modal.tsx +27 -15
- package/src/components/searchinput/SearchInput.tsx +14 -13
- package/src/components/types.ts +1 -1
- package/src/components/fileupload/texts.ts +0 -20
|
@@ -1,256 +1,114 @@
|
|
|
1
1
|
import '@testing-library/jest-dom'
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { render, screen } from '@testing-library/react'
|
|
4
4
|
import { axe, toHaveNoViolations } from 'jest-axe'
|
|
5
5
|
|
|
6
|
-
import { Truncate } from './Truncate'
|
|
6
|
+
import { Truncate, TruncateContext } from './Truncate'
|
|
7
7
|
|
|
8
8
|
expect.extend(toHaveNoViolations)
|
|
9
9
|
|
|
10
|
-
describe('
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
let originalGetComputedStyle: typeof window.getComputedStyle
|
|
10
|
+
describe('Truncate', () => {
|
|
11
|
+
test('renders a short filename as a single span with no tail', () => {
|
|
12
|
+
const { container } = render(<Truncate tail={5}>file.txt</Truncate>)
|
|
14
13
|
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
const first = container.querySelector('[data-pkt-truncate-part="first"]')
|
|
15
|
+
const tail = container.querySelector('[data-pkt-truncate-part="tail"]')
|
|
17
16
|
|
|
18
|
-
|
|
19
|
-
//
|
|
20
|
-
window.getComputedStyle = ((...args: any[]) => {
|
|
21
|
-
const style = originalGetComputedStyle(...(args as [Element]))
|
|
22
|
-
// Keep CSSStyleDeclaration methods (like getPropertyValue) intact.
|
|
23
|
-
return new Proxy(style, {
|
|
24
|
-
get(target, prop) {
|
|
25
|
-
if (prop === 'lineHeight') return '16px'
|
|
26
|
-
return (target as any)[prop]
|
|
27
|
-
},
|
|
28
|
-
})
|
|
29
|
-
}) as any
|
|
30
|
-
|
|
31
|
-
// Mock ResizeObserver
|
|
32
|
-
global.ResizeObserver = class ResizeObserver {
|
|
33
|
-
constructor(callback: ResizeObserverCallback) {
|
|
34
|
-
resizeObserverCallback = callback
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
observe(element: Element) {
|
|
38
|
-
observedElements.add(element)
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
unobserve(element: Element) {
|
|
42
|
-
observedElements.delete(element)
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
disconnect() {
|
|
46
|
-
observedElements.clear()
|
|
47
|
-
}
|
|
48
|
-
} as any
|
|
49
|
-
})
|
|
50
|
-
|
|
51
|
-
afterEach(() => {
|
|
52
|
-
observedElements.clear()
|
|
53
|
-
window.getComputedStyle = originalGetComputedStyle
|
|
54
|
-
})
|
|
55
|
-
|
|
56
|
-
const triggerResize = (element: HTMLElement) => {
|
|
57
|
-
resizeObserverCallback(
|
|
58
|
-
[
|
|
59
|
-
{
|
|
60
|
-
target: element,
|
|
61
|
-
contentRect: {} as DOMRectReadOnly,
|
|
62
|
-
borderBoxSize: [] as ResizeObserverSize[],
|
|
63
|
-
contentBoxSize: [] as ResizeObserverSize[],
|
|
64
|
-
devicePixelContentBoxSize: [] as ResizeObserverSize[],
|
|
65
|
-
},
|
|
66
|
-
],
|
|
67
|
-
{} as ResizeObserver,
|
|
68
|
-
)
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
test('renders text content correctly', () => {
|
|
72
|
-
render(<Truncate>short-filename.txt</Truncate>)
|
|
73
|
-
|
|
74
|
-
expect(
|
|
75
|
-
screen.getByText(
|
|
76
|
-
(content, element) =>
|
|
77
|
-
content === 'short-filename.txt' && element?.getAttribute('data-pkt-truncate-part') === 'first',
|
|
78
|
-
),
|
|
79
|
-
).toBeInTheDocument()
|
|
80
|
-
})
|
|
81
|
-
|
|
82
|
-
test('renders with custom tail length', () => {
|
|
83
|
-
const longText = 'very-long-filename-that-needs-truncation.txt'
|
|
84
|
-
const { container } = render(<Truncate tail={3}>{longText}</Truncate>)
|
|
85
|
-
|
|
86
|
-
// Check that the full text is in the first part
|
|
87
|
-
const wrapper = container.querySelector('[data-pkt-truncate-part="wrapper"]')
|
|
88
|
-
const firstPart = container.querySelector('[data-pkt-truncate-part="first"]')
|
|
89
|
-
expect(wrapper).toBeTruthy()
|
|
90
|
-
expect(firstPart).toBeTruthy()
|
|
91
|
-
expect(firstPart).toHaveTextContent(longText)
|
|
92
|
-
expect(wrapper as HTMLElement).toContainElement(firstPart as HTMLElement)
|
|
93
|
-
|
|
94
|
-
// Check that the tail part contains the last 3 characters
|
|
95
|
-
const tailSpan = container.querySelector('[data-pkt-truncate-part="tail"]')
|
|
96
|
-
expect(tailSpan).toHaveTextContent('txt')
|
|
17
|
+
expect(first).toHaveTextContent('file.txt')
|
|
18
|
+
expect(tail).toBeNull() // no split when filename is not longer than tail + 3
|
|
97
19
|
})
|
|
98
20
|
|
|
99
|
-
test('
|
|
100
|
-
const
|
|
101
|
-
const { container } = render(<Truncate>{
|
|
102
|
-
|
|
103
|
-
const tailSpan = container.querySelector('[data-pkt-truncate-part="tail"]')
|
|
104
|
-
expect(tailSpan).toHaveTextContent('e.txt') // last 5 characters
|
|
105
|
-
})
|
|
21
|
+
test('splits a long filename into head + tail spans that reconstruct the original', () => {
|
|
22
|
+
const name = 'very-long-filename-that-needs-truncation.txt'
|
|
23
|
+
const { container } = render(<Truncate tail={3}>{name}</Truncate>)
|
|
106
24
|
|
|
107
|
-
|
|
108
|
-
const
|
|
109
|
-
const { container } = render(<Truncate tail={5}>{shortText}</Truncate>)
|
|
25
|
+
const head = container.querySelector('[data-pkt-truncate-part="first"]')
|
|
26
|
+
const tail = container.querySelector('[data-pkt-truncate-part="tail"]')
|
|
110
27
|
|
|
111
|
-
|
|
112
|
-
expect(
|
|
28
|
+
expect(head).toHaveTextContent(name.slice(0, name.length - 3))
|
|
29
|
+
expect(tail).toHaveTextContent('txt')
|
|
30
|
+
expect((head?.textContent ?? '') + (tail?.textContent ?? '')).toBe(name)
|
|
113
31
|
})
|
|
114
32
|
|
|
115
|
-
test('
|
|
116
|
-
const { container } = render(<Truncate>
|
|
117
|
-
|
|
118
|
-
const tailSpan = container.querySelector('[data-pkt-truncate-part="tail"]')
|
|
119
|
-
|
|
120
|
-
expect(tailSpan).toHaveStyle({ display: 'none' })
|
|
121
|
-
})
|
|
122
|
-
|
|
123
|
-
test('tail becomes visible when overflowing', async () => {
|
|
124
|
-
const { container } = render(<Truncate>very-long-filename-that-overflows.txt</Truncate>)
|
|
125
|
-
|
|
126
|
-
const wrapper = container.querySelector('[data-pkt-truncate-part="wrapper"]') as HTMLElement
|
|
127
|
-
const measure = container.querySelector('[data-pkt-truncate-part="measure"]') as HTMLElement
|
|
128
|
-
const tail = container.querySelector('[data-pkt-truncate-part="tail"]') as HTMLElement
|
|
129
|
-
expect(wrapper).toBeTruthy()
|
|
130
|
-
expect(measure).toBeTruthy()
|
|
131
|
-
expect(tail).toBeTruthy()
|
|
132
|
-
|
|
133
|
-
// Simulate > 2 lines (lineHeight=16px -> maxHeight=32px)
|
|
134
|
-
measure.getBoundingClientRect = () => ({ height: 40 } as any)
|
|
135
|
-
|
|
136
|
-
// Trigger overflow recalculation (ResizeObserver observes wrapper)
|
|
137
|
-
act(() => triggerResize(wrapper))
|
|
138
|
-
|
|
139
|
-
await waitFor(() => {
|
|
140
|
-
expect(tail).toHaveStyle({ display: 'inline' })
|
|
141
|
-
})
|
|
142
|
-
})
|
|
143
|
-
|
|
144
|
-
test('observes the wrapper element with ResizeObserver', () => {
|
|
145
|
-
const { container } = render(<Truncate>filename.txt</Truncate>)
|
|
146
|
-
|
|
147
|
-
const wrapper = container.querySelector('[data-pkt-truncate-part="wrapper"]')
|
|
148
|
-
expect(observedElements.has(wrapper!)).toBe(true)
|
|
149
|
-
})
|
|
150
|
-
|
|
151
|
-
test('disconnects ResizeObserver on unmount', () => {
|
|
152
|
-
const { unmount } = render(<Truncate>filename.txt</Truncate>)
|
|
153
|
-
|
|
154
|
-
expect(observedElements.size).toBe(1)
|
|
155
|
-
|
|
156
|
-
unmount()
|
|
33
|
+
test('defaults to a tail of 5 characters', () => {
|
|
34
|
+
const { container } = render(<Truncate>very-long-filename.txt</Truncate>)
|
|
157
35
|
|
|
158
|
-
|
|
36
|
+
const tail = container.querySelector('[data-pkt-truncate-part="tail"]')
|
|
37
|
+
expect(tail).toHaveTextContent('e.txt')
|
|
159
38
|
})
|
|
160
39
|
|
|
161
|
-
test('
|
|
162
|
-
render(
|
|
163
|
-
<
|
|
164
|
-
filename.txt
|
|
165
|
-
</
|
|
40
|
+
test('takes the tail prop over the context default', () => {
|
|
41
|
+
const { container } = render(
|
|
42
|
+
<TruncateContext.Provider value={{ tail: 10 }}>
|
|
43
|
+
<Truncate tail={3}>very-long-filename.txt</Truncate>
|
|
44
|
+
</TruncateContext.Provider>,
|
|
166
45
|
)
|
|
167
46
|
|
|
168
|
-
const
|
|
169
|
-
expect(
|
|
170
|
-
expect(wrapper.tagName).toBe('SPAN')
|
|
47
|
+
const tail = container.querySelector('[data-pkt-truncate-part="tail"]')
|
|
48
|
+
expect(tail).toHaveTextContent('txt')
|
|
171
49
|
})
|
|
172
50
|
|
|
173
|
-
test('
|
|
51
|
+
test('uses the context tail when no prop is given', () => {
|
|
174
52
|
const { container } = render(
|
|
175
|
-
<
|
|
53
|
+
<TruncateContext.Provider value={{ tail: 4 }}>
|
|
54
|
+
<Truncate>very-long-filename.txt</Truncate>
|
|
55
|
+
</TruncateContext.Provider>,
|
|
176
56
|
)
|
|
177
57
|
|
|
178
|
-
const
|
|
179
|
-
expect(
|
|
180
|
-
display: 'grid',
|
|
181
|
-
color: 'rgb(255, 0, 0)',
|
|
182
|
-
fontSize: '14px',
|
|
183
|
-
})
|
|
58
|
+
const tail = container.querySelector('[data-pkt-truncate-part="tail"]')
|
|
59
|
+
expect(tail).toHaveTextContent('.txt')
|
|
184
60
|
})
|
|
185
61
|
|
|
186
|
-
test('tail
|
|
187
|
-
const { container } = render(<Truncate>
|
|
188
|
-
|
|
189
|
-
const tailSpan = container.querySelector('[data-pkt-truncate-part="tail"]')
|
|
190
|
-
expect(tailSpan).toHaveAttribute('aria-hidden', 'true')
|
|
191
|
-
})
|
|
62
|
+
test('does not split when the filename is exactly `tail + 3` characters', () => {
|
|
63
|
+
const { container } = render(<Truncate tail={5}>{'12345678'}</Truncate>)
|
|
192
64
|
|
|
193
|
-
|
|
194
|
-
const
|
|
65
|
+
const head = container.querySelector('[data-pkt-truncate-part="first"]')
|
|
66
|
+
const tail = container.querySelector('[data-pkt-truncate-part="tail"]')
|
|
195
67
|
|
|
196
|
-
|
|
197
|
-
expect(
|
|
198
|
-
display: '-webkit-box',
|
|
199
|
-
overflow: 'hidden',
|
|
200
|
-
textOverflow: 'ellipsis',
|
|
201
|
-
whiteSpace: 'normal',
|
|
202
|
-
overflowWrap: 'anywhere',
|
|
203
|
-
})
|
|
204
|
-
expect(firstPart).toHaveStyle('-webkit-line-clamp: 2')
|
|
68
|
+
expect(head).toHaveTextContent('12345678')
|
|
69
|
+
expect(tail).toBeNull()
|
|
205
70
|
})
|
|
206
71
|
|
|
207
|
-
test('
|
|
208
|
-
const { container } = render(<Truncate>very-long-filename.txt</Truncate>)
|
|
72
|
+
test('does not split when tail is 0 or undefined', () => {
|
|
73
|
+
const { container: zero } = render(<Truncate tail={0}>very-long-filename.txt</Truncate>)
|
|
74
|
+
expect(zero.querySelector('[data-pkt-truncate-part="tail"]')).toBeNull()
|
|
209
75
|
|
|
210
|
-
const
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
76
|
+
const { container: none } = render(
|
|
77
|
+
<TruncateContext.Provider value={{ tail: undefined }}>
|
|
78
|
+
<Truncate>very-long-filename.txt</Truncate>
|
|
79
|
+
</TruncateContext.Provider>,
|
|
80
|
+
)
|
|
81
|
+
expect(none.querySelector('[data-pkt-truncate-part="tail"]')).toBeNull()
|
|
215
82
|
})
|
|
216
83
|
|
|
217
|
-
test('handles empty string', () => {
|
|
84
|
+
test('handles an empty string', () => {
|
|
218
85
|
const { container } = render(<Truncate>{''}</Truncate>)
|
|
219
86
|
|
|
220
|
-
const
|
|
221
|
-
expect(
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
test('handles string exactly equal to tail + 3', () => {
|
|
225
|
-
const text = '12345678' // 8 characters, tail=5, so 5+3=8
|
|
226
|
-
const { container } = render(<Truncate tail={5}>{text}</Truncate>)
|
|
227
|
-
|
|
228
|
-
const tailSpan = container.querySelector('[data-pkt-truncate-part="tail"]')
|
|
229
|
-
expect(tailSpan).toHaveTextContent('') // secondPart should be null
|
|
87
|
+
const first = container.querySelector('[data-pkt-truncate-part="first"]')
|
|
88
|
+
expect(first).toHaveTextContent('')
|
|
89
|
+
expect(container.querySelector('[data-pkt-truncate-part="tail"]')).toBeNull()
|
|
230
90
|
})
|
|
231
91
|
|
|
232
92
|
describe('accessibility', () => {
|
|
233
|
-
test('
|
|
234
|
-
const { container } = render(<Truncate>filename
|
|
93
|
+
test('has no wcag violations for a short filename', async () => {
|
|
94
|
+
const { container } = render(<Truncate>filename.txt</Truncate>)
|
|
235
95
|
const results = await axe(container)
|
|
236
|
-
|
|
237
96
|
expect(results).toHaveNoViolations()
|
|
238
97
|
})
|
|
239
98
|
|
|
240
|
-
test('
|
|
99
|
+
test('has no wcag violations for a split filename — both head and tail are readable text', async () => {
|
|
241
100
|
const { container } = render(
|
|
242
|
-
<Truncate>very-long-filename-that-definitely-
|
|
101
|
+
<Truncate>very-long-filename-that-definitely-needs-truncation.txt</Truncate>,
|
|
243
102
|
)
|
|
103
|
+
const results = await axe(container)
|
|
104
|
+
expect(results).toHaveNoViolations()
|
|
244
105
|
|
|
245
|
-
|
|
246
|
-
const
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
const results = await axe(container)
|
|
252
|
-
expect(results).toHaveNoViolations()
|
|
253
|
-
})
|
|
106
|
+
// head + tail together equal the original, so screen readers read the full name.
|
|
107
|
+
const head = screen.getByText(/very-long-filename-that-definitely-needs-trunc/)
|
|
108
|
+
const tail = screen.getByText('n.txt')
|
|
109
|
+
expect((head.textContent ?? '') + (tail.textContent ?? '')).toBe(
|
|
110
|
+
'very-long-filename-that-definitely-needs-truncation.txt',
|
|
111
|
+
)
|
|
254
112
|
})
|
|
255
113
|
})
|
|
256
114
|
})
|
|
@@ -1,137 +1,41 @@
|
|
|
1
|
-
import {
|
|
2
|
-
createContext,
|
|
3
|
-
HTMLAttributes,
|
|
4
|
-
useCallback,
|
|
5
|
-
useContext,
|
|
6
|
-
useLayoutEffect,
|
|
7
|
-
useMemo,
|
|
8
|
-
useRef,
|
|
9
|
-
useState,
|
|
10
|
-
} from 'react'
|
|
1
|
+
import { createContext, useContext } from 'react'
|
|
11
2
|
|
|
12
|
-
|
|
3
|
+
import { splitFilenameForTruncation } from 'shared-utils/fileupload'
|
|
13
4
|
|
|
14
|
-
|
|
15
|
-
const style = window.getComputedStyle(element)
|
|
16
|
-
const lineHeight = parseFloat(style.lineHeight)
|
|
17
|
-
if (!Number.isNaN(lineHeight) && lineHeight > 0) return lineHeight
|
|
18
|
-
|
|
19
|
-
// `line-height: normal` fallback (approx. 1.2 * font-size)
|
|
20
|
-
const fontSize = parseFloat(style.fontSize)
|
|
21
|
-
return (!Number.isNaN(fontSize) && fontSize > 0 ? fontSize : 16) * 1.2
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
const elementOverflowsLines = (measureEl: HTMLElement, lineHeightPx: number, maxLines: number): boolean => {
|
|
25
|
-
const maxHeight = lineHeightPx * maxLines
|
|
26
|
-
const height = measureEl.getBoundingClientRect().height
|
|
27
|
-
return height > maxHeight + 0.5
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export interface IMiddleTruncate extends HTMLAttributes<HTMLSpanElement> {
|
|
5
|
+
export interface IMiddleTruncate {
|
|
31
6
|
children: string
|
|
7
|
+
/** Trailing chars to preserve when the name is long enough to split. */
|
|
32
8
|
tail?: number
|
|
33
9
|
}
|
|
34
10
|
|
|
35
11
|
type TTruncateContext = { tail: number | undefined }
|
|
36
12
|
export const TruncateContext = createContext<TTruncateContext>({ tail: 5 })
|
|
37
13
|
|
|
38
|
-
|
|
39
|
-
|
|
14
|
+
/**
|
|
15
|
+
* Middle-truncate a filename. Renders as a React fragment — the parent element
|
|
16
|
+
* (typically a `<p className="…__title">`) is the grid container that drives
|
|
17
|
+
* the layout via CSS in `_fileupload.scss`. The split threshold is shared with
|
|
18
|
+
* the Lit implementation through `splitFilenameForTruncation`.
|
|
19
|
+
*
|
|
20
|
+
* For short filenames the tail span is omitted and the text renders unwrapped.
|
|
21
|
+
*/
|
|
22
|
+
export const Truncate = ({ children, tail: tailProp }: IMiddleTruncate): JSX.Element => {
|
|
40
23
|
const context = useContext(TruncateContext)
|
|
41
|
-
const tail = tailProp !== undefined ? tailProp : context.tail
|
|
42
|
-
const
|
|
43
|
-
const showingSecondPart = secondPart !== null && tail > 0
|
|
44
|
-
const wrapperRef = useRef<HTMLSpanElement>(null)
|
|
45
|
-
const measureRef = useRef<HTMLSpanElement>(null)
|
|
46
|
-
const [isOverflowing, setIsOverflowing] = useState(false)
|
|
47
|
-
|
|
48
|
-
const computeOverflow = useCallback(() => {
|
|
49
|
-
const wrapper = wrapperRef.current
|
|
50
|
-
const measureEl = measureRef.current
|
|
51
|
-
if (!wrapper || !measureEl) return
|
|
24
|
+
const tail = tailProp !== undefined ? tailProp : context.tail
|
|
25
|
+
const split = splitFilenameForTruncation(children, tail)
|
|
52
26
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
}
|
|
27
|
+
if (!split) {
|
|
28
|
+
return <span data-pkt-truncate-part="first">{children}</span>
|
|
29
|
+
}
|
|
56
30
|
|
|
57
|
-
const resizeObserver = useMemo(() => {
|
|
58
|
-
return new ResizeObserver((entries) => {
|
|
59
|
-
if (entries.some((entry) => entry.target === wrapperRef.current)) {
|
|
60
|
-
computeOverflow()
|
|
61
|
-
}
|
|
62
|
-
})
|
|
63
|
-
}, [computeOverflow])
|
|
64
|
-
useLayoutEffect(() => {
|
|
65
|
-
if (wrapperRef.current) {
|
|
66
|
-
resizeObserver.observe(wrapperRef.current)
|
|
67
|
-
}
|
|
68
|
-
computeOverflow()
|
|
69
|
-
return () => resizeObserver.disconnect()
|
|
70
|
-
}, [children, resizeObserver, computeOverflow])
|
|
71
31
|
return (
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
data-pkt-truncate-part="wrapper"
|
|
76
|
-
style={{
|
|
77
|
-
maxWidth: '100%',
|
|
78
|
-
width: '100%',
|
|
79
|
-
display: 'grid',
|
|
80
|
-
gridTemplateColumns: 'minmax(0, 1fr) auto',
|
|
81
|
-
alignItems: 'end',
|
|
82
|
-
columnGap: '0.25rem',
|
|
83
|
-
overflow: 'hidden',
|
|
84
|
-
position: 'relative',
|
|
85
|
-
...props.style,
|
|
86
|
-
}}
|
|
87
|
-
>
|
|
88
|
-
<span
|
|
89
|
-
data-pkt-truncate-part="first"
|
|
90
|
-
style={{
|
|
91
|
-
minWidth: 0,
|
|
92
|
-
display: '-webkit-box',
|
|
93
|
-
WebkitBoxOrient: 'vertical',
|
|
94
|
-
WebkitLineClamp: MAX_LINES,
|
|
95
|
-
overflow: 'hidden',
|
|
96
|
-
textOverflow: 'ellipsis',
|
|
97
|
-
whiteSpace: 'normal',
|
|
98
|
-
overflowWrap: 'anywhere',
|
|
99
|
-
}}
|
|
100
|
-
>
|
|
101
|
-
{firstPart}
|
|
32
|
+
<>
|
|
33
|
+
<span className="pkt-fileupload__queue-display__item__title__head" data-pkt-truncate-part="first">
|
|
34
|
+
{split.head}
|
|
102
35
|
</span>
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
data-pkt-truncate-part="tail"
|
|
106
|
-
style={{
|
|
107
|
-
whiteSpace: 'nowrap',
|
|
108
|
-
display: isOverflowing ? 'inline' : 'none',
|
|
109
|
-
paddingRight: '0.5rem',
|
|
110
|
-
}}
|
|
111
|
-
aria-hidden={true}
|
|
112
|
-
>
|
|
113
|
-
{secondPart}
|
|
114
|
-
</span>
|
|
115
|
-
)}
|
|
116
|
-
|
|
117
|
-
{/* Hidden measuring element (full text, wraps normally) */}
|
|
118
|
-
<span
|
|
119
|
-
ref={measureRef}
|
|
120
|
-
aria-hidden={true}
|
|
121
|
-
data-pkt-truncate-part="measure"
|
|
122
|
-
style={{
|
|
123
|
-
position: 'absolute',
|
|
124
|
-
top: 0,
|
|
125
|
-
left: 0,
|
|
126
|
-
width: '100%',
|
|
127
|
-
visibility: 'hidden',
|
|
128
|
-
pointerEvents: 'none',
|
|
129
|
-
whiteSpace: 'normal',
|
|
130
|
-
overflowWrap: 'anywhere',
|
|
131
|
-
}}
|
|
132
|
-
>
|
|
133
|
-
{children}
|
|
36
|
+
<span className="pkt-fileupload__queue-display__item__title__tail" data-pkt-truncate-part="tail">
|
|
37
|
+
{split.tail}
|
|
134
38
|
</span>
|
|
135
|
-
|
|
39
|
+
</>
|
|
136
40
|
)
|
|
137
41
|
}
|