@linktr.ee/messaging-react 4.1.8-rc-1787240024 → 4.1.8
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/dist/{Card-BvMoNrpF.js → Card-B5YUxWCs.js} +2 -2
- package/dist/{Card-BvMoNrpF.js.map → Card-B5YUxWCs.js.map} +1 -1
- package/dist/{Card-DGr_puw8.cjs → Card-DjafcNS_.cjs} +2 -2
- package/dist/{Card-DGr_puw8.cjs.map → Card-DjafcNS_.cjs.map} +1 -1
- package/dist/assets/index.css +1 -1
- package/dist/index-Bwjn-YoV.cjs +3 -0
- package/dist/index-Bwjn-YoV.cjs.map +1 -0
- package/dist/{index-BSPkcgIW.js → index-l73T0G4A.js} +404 -404
- package/dist/index-l73T0G4A.js.map +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.js +1 -1
- package/package.json +7 -9
- package/src/components/AttachmentCard/AttachmentCard.stories.tsx +0 -36
- package/src/components/AttachmentCard/Thumbnail.tsx +2 -2
- package/src/components/CustomMessageInput/index.tsx +1 -1
- package/src/components/MessageAttachment/MessageAttachment.behavior.test.tsx +182 -0
- package/src/components/MessageAttachment/_shared/Bubble.test.tsx +111 -0
- package/src/components/MessageAttachment/_shared/CarouselNav.test.tsx +36 -0
- package/src/components/MessageAttachment/_shared/CompactDocumentRow.test.tsx +114 -0
- package/src/components/MessageAttachment/_shared/DismissButton.test.tsx +42 -0
- package/src/components/MessageAttachment/_shared/DownloadAction.test.tsx +168 -0
- package/src/components/MessageAttachment/_shared/ImageViewer.test.tsx +52 -0
- package/src/components/MessageAttachment/_shared/MediaStackGrid.test.tsx +69 -0
- package/src/components/MessageAttachment/_shared/PdfViewer.test.tsx +45 -0
- package/src/components/MessageAttachment/_shared/VideoViewer.test.tsx +55 -0
- package/src/components/MessageAttachment/_shared/ViewerShell.test.tsx +246 -0
- package/src/components/MessageAttachment/_shared/fileMeta.test.ts +44 -0
- package/src/components/MessageAttachment/_shared/triggerDownload.test.ts +113 -0
- package/src/components/MessageAttachment/_shared/useCarousel.test.tsx +68 -0
- package/src/components/MessageAttachment/_shared/viewerChromeClasses.test.ts +16 -0
- package/src/styles.css +46 -89
- package/dist/index-BSPkcgIW.js.map +0 -1
- package/dist/index-DKiY5_Ce.cjs +0 -3
- package/dist/index-DKiY5_Ce.cjs.map +0 -1
|
@@ -25,6 +25,14 @@ describe('formatFileSize', () => {
|
|
|
25
25
|
expect(formatFileSize(Infinity)).toBe('')
|
|
26
26
|
expect(formatFileSize(-1)).toBe('')
|
|
27
27
|
})
|
|
28
|
+
|
|
29
|
+
it('handles unit boundaries without crossing into the next unit early', () => {
|
|
30
|
+
expect(formatFileSize(1023)).toBe('1023 B')
|
|
31
|
+
expect(formatFileSize(1024 * 1024 - 1)).toBe('1024.0 KB')
|
|
32
|
+
expect(formatFileSize(1024 * 1024)).toBe('1.00 MB')
|
|
33
|
+
expect(formatFileSize(1024 ** 3 - 1)).toBe('1024.00 MB')
|
|
34
|
+
expect(formatFileSize(1024 ** 3)).toBe('1.00 GB')
|
|
35
|
+
})
|
|
28
36
|
})
|
|
29
37
|
|
|
30
38
|
describe('getFileExtensionLabel', () => {
|
|
@@ -43,6 +51,18 @@ describe('getFileExtensionLabel', () => {
|
|
|
43
51
|
expect(getFileExtensionLabel('application/octet-stream')).toBeUndefined()
|
|
44
52
|
expect(getFileExtensionLabel()).toBeUndefined()
|
|
45
53
|
})
|
|
54
|
+
|
|
55
|
+
it('handles dot boundaries, extension length, and MIME fallbacks', () => {
|
|
56
|
+
expect(getFileExtensionLabel('text/plain', '.gitignore')).toBe('TXT')
|
|
57
|
+
expect(getFileExtensionLabel('text/plain', 'notes.')).toBe('TXT')
|
|
58
|
+
expect(getFileExtensionLabel('image/webp', 'photo.webp')).toBe('WEBP')
|
|
59
|
+
expect(getFileExtensionLabel('image/webp', 'photo.abcdef')).toBe('WEBP')
|
|
60
|
+
expect(getFileExtensionLabel('application/json', 'data.12345')).toBe(
|
|
61
|
+
'12345'
|
|
62
|
+
)
|
|
63
|
+
expect(getFileExtensionLabel('image/*')).toBeUndefined()
|
|
64
|
+
expect(getFileExtensionLabel('application/octet-stream')).toBeUndefined()
|
|
65
|
+
})
|
|
46
66
|
})
|
|
47
67
|
|
|
48
68
|
describe('buildCompactMetaLabel', () => {
|
|
@@ -59,6 +79,23 @@ describe('buildCompactMetaLabel', () => {
|
|
|
59
79
|
it('returns undefined when nothing is renderable', () => {
|
|
60
80
|
expect(buildCompactMetaLabel()).toBeUndefined()
|
|
61
81
|
})
|
|
82
|
+
|
|
83
|
+
it('drops non-positive and non-number sizes while retaining size-only labels', () => {
|
|
84
|
+
expect(buildCompactMetaLabel('application/pdf', 'report.pdf', 0)).toBe(
|
|
85
|
+
'PDF'
|
|
86
|
+
)
|
|
87
|
+
expect(buildCompactMetaLabel('application/pdf', 'report.pdf', -1)).toBe(
|
|
88
|
+
'PDF'
|
|
89
|
+
)
|
|
90
|
+
expect(buildCompactMetaLabel(undefined, undefined, 1024)).toBe('1.0 KB')
|
|
91
|
+
expect(
|
|
92
|
+
buildCompactMetaLabel(
|
|
93
|
+
'application/octet-stream',
|
|
94
|
+
undefined,
|
|
95
|
+
'1024' as never
|
|
96
|
+
)
|
|
97
|
+
).toBeUndefined()
|
|
98
|
+
})
|
|
62
99
|
})
|
|
63
100
|
|
|
64
101
|
describe('filenameFromUrl', () => {
|
|
@@ -77,4 +114,11 @@ describe('filenameFromUrl', () => {
|
|
|
77
114
|
it('falls back to a default when the URL cannot be parsed', () => {
|
|
78
115
|
expect(filenameFromUrl('not a url at all')).toBe('download')
|
|
79
116
|
})
|
|
117
|
+
|
|
118
|
+
it('handles trailing slashes, deep paths, and query strings', () => {
|
|
119
|
+
expect(filenameFromUrl('https://cdn.example.com/folder/')).toBe('download')
|
|
120
|
+
expect(
|
|
121
|
+
filenameFromUrl('https://cdn.example.com/a/b/c/report.pdf?download=1')
|
|
122
|
+
).toBe('report.pdf')
|
|
123
|
+
})
|
|
80
124
|
})
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|
2
|
+
|
|
3
|
+
import { triggerDownload } from './triggerDownload'
|
|
4
|
+
|
|
5
|
+
describe('triggerDownload', () => {
|
|
6
|
+
const fetchMock = vi.fn()
|
|
7
|
+
const openMock = vi.fn()
|
|
8
|
+
const createObjectURLMock = vi.fn()
|
|
9
|
+
const revokeObjectURLMock = vi.fn()
|
|
10
|
+
const clickMock = vi
|
|
11
|
+
.spyOn(HTMLAnchorElement.prototype, 'click')
|
|
12
|
+
.mockImplementation(() => undefined)
|
|
13
|
+
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
vi.stubGlobal('fetch', fetchMock)
|
|
16
|
+
vi.spyOn(window, 'open').mockImplementation(openMock)
|
|
17
|
+
Object.defineProperty(URL, 'createObjectURL', {
|
|
18
|
+
configurable: true,
|
|
19
|
+
value: createObjectURLMock,
|
|
20
|
+
})
|
|
21
|
+
Object.defineProperty(URL, 'revokeObjectURL', {
|
|
22
|
+
configurable: true,
|
|
23
|
+
value: revokeObjectURLMock,
|
|
24
|
+
})
|
|
25
|
+
fetchMock.mockReset()
|
|
26
|
+
openMock.mockReset()
|
|
27
|
+
createObjectURLMock.mockReset()
|
|
28
|
+
revokeObjectURLMock.mockReset()
|
|
29
|
+
clickMock.mockClear()
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
it('downloads a successful response with the supplied filename', async () => {
|
|
33
|
+
const blob = new Blob(['file'])
|
|
34
|
+
fetchMock.mockResolvedValue({
|
|
35
|
+
ok: true,
|
|
36
|
+
blob: () => Promise.resolve(blob),
|
|
37
|
+
})
|
|
38
|
+
createObjectURLMock.mockReturnValue('blob:attachment')
|
|
39
|
+
const appendSpy = vi.spyOn(document.body, 'appendChild')
|
|
40
|
+
const removeSpy = vi.spyOn(document.body, 'removeChild')
|
|
41
|
+
|
|
42
|
+
await expect(
|
|
43
|
+
triggerDownload('https://cdn.example.com/file.pdf', 'report.pdf')
|
|
44
|
+
).resolves.toBeUndefined()
|
|
45
|
+
|
|
46
|
+
expect(fetchMock).toHaveBeenCalledWith('https://cdn.example.com/file.pdf', {
|
|
47
|
+
mode: 'cors',
|
|
48
|
+
})
|
|
49
|
+
expect(createObjectURLMock).toHaveBeenCalledWith(blob)
|
|
50
|
+
expect(clickMock).toHaveBeenCalledOnce()
|
|
51
|
+
expect(revokeObjectURLMock).toHaveBeenCalledWith('blob:attachment')
|
|
52
|
+
const anchor = appendSpy.mock.calls.at(-1)?.[0] as HTMLAnchorElement
|
|
53
|
+
expect(anchor.href).toBe('blob:attachment')
|
|
54
|
+
expect(anchor.download).toBe('report.pdf')
|
|
55
|
+
expect(anchor.style.display).toBe('none')
|
|
56
|
+
expect(removeSpy).toHaveBeenCalledWith(anchor)
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
it('uses the URL filename when no filename is supplied', async () => {
|
|
60
|
+
fetchMock.mockResolvedValue({
|
|
61
|
+
ok: true,
|
|
62
|
+
blob: () => Promise.resolve(new Blob()),
|
|
63
|
+
})
|
|
64
|
+
createObjectURLMock.mockReturnValue('blob:attachment')
|
|
65
|
+
const appendSpy = vi.spyOn(document.body, 'appendChild')
|
|
66
|
+
|
|
67
|
+
await triggerDownload('https://cdn.example.com/a/My%20file.pdf')
|
|
68
|
+
|
|
69
|
+
expect(
|
|
70
|
+
(appendSpy.mock.calls.at(-1)?.[0] as HTMLAnchorElement).download
|
|
71
|
+
).toBe('My file.pdf')
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
it.each(['a non-ok response', 'a rejected fetch'])(
|
|
75
|
+
'falls back to a new window for %s',
|
|
76
|
+
async (label) => {
|
|
77
|
+
if (label === 'a non-ok response') {
|
|
78
|
+
fetchMock.mockResolvedValue({ ok: false, status: 404 })
|
|
79
|
+
} else {
|
|
80
|
+
fetchMock.mockRejectedValue(new Error('offline'))
|
|
81
|
+
}
|
|
82
|
+
openMock.mockReturnValue({})
|
|
83
|
+
|
|
84
|
+
await expect(
|
|
85
|
+
triggerDownload('https://cdn.example.com/file.pdf')
|
|
86
|
+
).resolves.toBeUndefined()
|
|
87
|
+
|
|
88
|
+
expect(openMock).toHaveBeenCalledWith(
|
|
89
|
+
'https://cdn.example.com/file.pdf',
|
|
90
|
+
'_blank',
|
|
91
|
+
'noopener,noreferrer'
|
|
92
|
+
)
|
|
93
|
+
}
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
it('uses a raw-url anchor when the popup is blocked', async () => {
|
|
97
|
+
fetchMock.mockRejectedValue(new Error('offline'))
|
|
98
|
+
openMock.mockReturnValue(null)
|
|
99
|
+
const appendSpy = vi.spyOn(document.body, 'appendChild')
|
|
100
|
+
const removeSpy = vi.spyOn(document.body, 'removeChild')
|
|
101
|
+
|
|
102
|
+
await expect(
|
|
103
|
+
triggerDownload('https://cdn.example.com/file.pdf')
|
|
104
|
+
).resolves.toBeUndefined()
|
|
105
|
+
|
|
106
|
+
const anchor = appendSpy.mock.calls.at(-1)?.[0] as HTMLAnchorElement
|
|
107
|
+
expect(anchor.href).toBe('https://cdn.example.com/file.pdf')
|
|
108
|
+
expect(anchor.download).toBe('file.pdf')
|
|
109
|
+
expect(anchor.target).toBe('_blank')
|
|
110
|
+
expect(anchor.rel).toBe('noopener noreferrer')
|
|
111
|
+
expect(removeSpy).toHaveBeenCalledWith(anchor)
|
|
112
|
+
})
|
|
113
|
+
})
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
|
|
3
|
+
import { fireEvent, renderWithProviders, screen } from '../../../test/utils'
|
|
4
|
+
|
|
5
|
+
import { useCarousel } from './useCarousel'
|
|
6
|
+
|
|
7
|
+
const Harness = ({
|
|
8
|
+
open,
|
|
9
|
+
length,
|
|
10
|
+
initialIndex = 0,
|
|
11
|
+
}: {
|
|
12
|
+
open: boolean
|
|
13
|
+
length: number
|
|
14
|
+
initialIndex?: number
|
|
15
|
+
}) => {
|
|
16
|
+
const { index, prev, next } = useCarousel({ open, length, initialIndex })
|
|
17
|
+
return (
|
|
18
|
+
<div>
|
|
19
|
+
<span data-testid="index">{index}</span>
|
|
20
|
+
<button type="button" onClick={prev}>
|
|
21
|
+
Prev
|
|
22
|
+
</button>
|
|
23
|
+
<button type="button" onClick={next}>
|
|
24
|
+
Next
|
|
25
|
+
</button>
|
|
26
|
+
</div>
|
|
27
|
+
)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
describe('useCarousel', () => {
|
|
31
|
+
it('clamps initial index, wraps, and resets on reopen', () => {
|
|
32
|
+
const { rerender } = renderWithProviders(
|
|
33
|
+
<Harness open length={3} initialIndex={99} />
|
|
34
|
+
)
|
|
35
|
+
expect(screen.getByTestId('index')).toHaveTextContent('2')
|
|
36
|
+
fireEvent.click(screen.getByRole('button', { name: 'Next' }))
|
|
37
|
+
expect(screen.getByTestId('index')).toHaveTextContent('0')
|
|
38
|
+
fireEvent.click(screen.getByRole('button', { name: 'Prev' }))
|
|
39
|
+
expect(screen.getByTestId('index')).toHaveTextContent('2')
|
|
40
|
+
rerender(<Harness open={false} length={3} initialIndex={1} />)
|
|
41
|
+
rerender(<Harness open length={3} initialIndex={1} />)
|
|
42
|
+
expect(screen.getByTestId('index')).toHaveTextContent('1')
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
it('handles arrows only when open with multiple items', () => {
|
|
46
|
+
const { rerender } = renderWithProviders(
|
|
47
|
+
<Harness open length={2} initialIndex={0} />
|
|
48
|
+
)
|
|
49
|
+
const right = fireEvent.keyDown(window, { key: 'ArrowRight' })
|
|
50
|
+
expect(screen.getByTestId('index')).toHaveTextContent('1')
|
|
51
|
+
expect(right).toBe(false)
|
|
52
|
+
for (const tagName of ['video', 'audio'] as const) {
|
|
53
|
+
const media = document.createElement(tagName)
|
|
54
|
+
media.tabIndex = 0
|
|
55
|
+
document.body.appendChild(media)
|
|
56
|
+
media.focus()
|
|
57
|
+
fireEvent.keyDown(window, { key: 'ArrowLeft' })
|
|
58
|
+
expect(screen.getByTestId('index')).toHaveTextContent('1')
|
|
59
|
+
media.remove()
|
|
60
|
+
}
|
|
61
|
+
rerender(<Harness open length={1} />)
|
|
62
|
+
fireEvent.keyDown(window, { key: 'ArrowRight' })
|
|
63
|
+
expect(screen.getByTestId('index')).toHaveTextContent('0')
|
|
64
|
+
rerender(<Harness open={false} length={2} />)
|
|
65
|
+
fireEvent.keyDown(window, { key: 'ArrowRight' })
|
|
66
|
+
expect(screen.getByTestId('index')).toHaveTextContent('0')
|
|
67
|
+
})
|
|
68
|
+
})
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
|
|
3
|
+
import { VIEWER_ACTION_CLASS, VIEWER_NAV_CLASS } from './viewerChromeClasses'
|
|
4
|
+
|
|
5
|
+
describe('viewer chrome classes', () => {
|
|
6
|
+
it('keeps the load-bearing action button tokens', () => {
|
|
7
|
+
expect(VIEWER_ACTION_CLASS).toContain('size-10')
|
|
8
|
+
expect(VIEWER_ACTION_CLASS).toContain('rounded-full')
|
|
9
|
+
})
|
|
10
|
+
|
|
11
|
+
it('keeps the load-bearing navigation button tokens', () => {
|
|
12
|
+
expect(VIEWER_NAV_CLASS).toContain('absolute')
|
|
13
|
+
expect(VIEWER_NAV_CLASS).toContain('size-12')
|
|
14
|
+
expect(VIEWER_NAV_CLASS).toContain('-translate-y-1/2')
|
|
15
|
+
})
|
|
16
|
+
})
|
package/src/styles.css
CHANGED
|
@@ -120,10 +120,6 @@
|
|
|
120
120
|
}
|
|
121
121
|
}
|
|
122
122
|
|
|
123
|
-
.messaging-channel-layout {
|
|
124
|
-
--messaging-composer-fade-height: 2rem;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
123
|
.messaging-composer-chrome--floating {
|
|
128
124
|
position: relative;
|
|
129
125
|
right: 0;
|
|
@@ -138,21 +134,10 @@
|
|
|
138
134
|
|
|
139
135
|
.messaging-composer-chrome--floating .messaging-composer-backdrop {
|
|
140
136
|
position: absolute;
|
|
141
|
-
|
|
142
|
-
right: 0;
|
|
143
|
-
bottom: 0;
|
|
144
|
-
left: 0;
|
|
137
|
+
inset: 0;
|
|
145
138
|
overflow: hidden;
|
|
146
139
|
isolation: isolate;
|
|
147
140
|
pointer-events: none;
|
|
148
|
-
/* Frosted band height, from the backdrop's top edge down to the composer
|
|
149
|
-
pill: the fade-height above the chrome plus the chrome's own top padding
|
|
150
|
-
(p-4 = 1rem). Blur/gradient stops are anchored to this fixed band so the
|
|
151
|
-
frosting stays in the gap above the composer and does not scale with the
|
|
152
|
-
(opaque) composer height. */
|
|
153
|
-
--messaging-composer-fade-band: calc(
|
|
154
|
-
var(--messaging-composer-fade-height) + 1rem
|
|
155
|
-
);
|
|
156
141
|
}
|
|
157
142
|
|
|
158
143
|
.messaging-composer-backdrop-blur {
|
|
@@ -161,99 +146,71 @@
|
|
|
161
146
|
pointer-events: none;
|
|
162
147
|
}
|
|
163
148
|
|
|
149
|
+
/* Figma progressive background blur: (50%, 0%) → (50%, 100%), 0 → 16.
|
|
150
|
+
CSS has no spatially-varying blur, so five evenly spaced masked layers
|
|
151
|
+
approximate the linear ramp across the full parent height. */
|
|
164
152
|
.messaging-composer-backdrop-blur:nth-child(1) {
|
|
165
|
-
|
|
166
|
-
-webkit-backdrop-filter: blur(2px);
|
|
167
|
-
mask: linear-gradient(
|
|
168
|
-
to bottom,
|
|
169
|
-
transparent 0,
|
|
170
|
-
black calc(var(--messaging-composer-fade-band) * 0.12),
|
|
171
|
-
black calc(var(--messaging-composer-fade-band) * 0.32),
|
|
172
|
-
transparent calc(var(--messaging-composer-fade-band) * 0.5)
|
|
173
|
-
);
|
|
174
|
-
-webkit-mask: linear-gradient(
|
|
153
|
+
--messaging-composer-blur-mask: linear-gradient(
|
|
175
154
|
to bottom,
|
|
176
|
-
transparent 0
|
|
177
|
-
black
|
|
178
|
-
|
|
179
|
-
transparent calc(var(--messaging-composer-fade-band) * 0.5)
|
|
155
|
+
transparent 0%,
|
|
156
|
+
black 20%,
|
|
157
|
+
transparent 40%
|
|
180
158
|
);
|
|
159
|
+
backdrop-filter: blur(3.2px);
|
|
160
|
+
-webkit-backdrop-filter: blur(3.2px);
|
|
161
|
+
mask: var(--messaging-composer-blur-mask);
|
|
162
|
+
-webkit-mask: var(--messaging-composer-blur-mask);
|
|
181
163
|
}
|
|
182
164
|
|
|
183
165
|
.messaging-composer-backdrop-blur:nth-child(2) {
|
|
184
|
-
|
|
185
|
-
-webkit-backdrop-filter: blur(4px);
|
|
186
|
-
mask: linear-gradient(
|
|
187
|
-
to bottom,
|
|
188
|
-
transparent calc(var(--messaging-composer-fade-band) * 0.2),
|
|
189
|
-
black calc(var(--messaging-composer-fade-band) * 0.38),
|
|
190
|
-
black calc(var(--messaging-composer-fade-band) * 0.55),
|
|
191
|
-
transparent calc(var(--messaging-composer-fade-band) * 0.7)
|
|
192
|
-
);
|
|
193
|
-
-webkit-mask: linear-gradient(
|
|
166
|
+
--messaging-composer-blur-mask: linear-gradient(
|
|
194
167
|
to bottom,
|
|
195
|
-
transparent
|
|
196
|
-
black
|
|
197
|
-
|
|
198
|
-
transparent calc(var(--messaging-composer-fade-band) * 0.7)
|
|
168
|
+
transparent 20%,
|
|
169
|
+
black 40%,
|
|
170
|
+
transparent 60%
|
|
199
171
|
);
|
|
172
|
+
backdrop-filter: blur(6.4px);
|
|
173
|
+
-webkit-backdrop-filter: blur(6.4px);
|
|
174
|
+
mask: var(--messaging-composer-blur-mask);
|
|
175
|
+
-webkit-mask: var(--messaging-composer-blur-mask);
|
|
200
176
|
}
|
|
201
177
|
|
|
202
178
|
.messaging-composer-backdrop-blur:nth-child(3) {
|
|
203
|
-
|
|
204
|
-
-webkit-backdrop-filter: blur(8px);
|
|
205
|
-
mask: linear-gradient(
|
|
179
|
+
--messaging-composer-blur-mask: linear-gradient(
|
|
206
180
|
to bottom,
|
|
207
|
-
transparent
|
|
208
|
-
black
|
|
209
|
-
|
|
210
|
-
transparent calc(var(--messaging-composer-fade-band) * 0.85)
|
|
211
|
-
);
|
|
212
|
-
-webkit-mask: linear-gradient(
|
|
213
|
-
to bottom,
|
|
214
|
-
transparent calc(var(--messaging-composer-fade-band) * 0.4),
|
|
215
|
-
black calc(var(--messaging-composer-fade-band) * 0.58),
|
|
216
|
-
black calc(var(--messaging-composer-fade-band) * 0.72),
|
|
217
|
-
transparent calc(var(--messaging-composer-fade-band) * 0.85)
|
|
181
|
+
transparent 40%,
|
|
182
|
+
black 60%,
|
|
183
|
+
transparent 80%
|
|
218
184
|
);
|
|
185
|
+
backdrop-filter: blur(9.6px);
|
|
186
|
+
-webkit-backdrop-filter: blur(9.6px);
|
|
187
|
+
mask: var(--messaging-composer-blur-mask);
|
|
188
|
+
-webkit-mask: var(--messaging-composer-blur-mask);
|
|
219
189
|
}
|
|
220
190
|
|
|
221
191
|
.messaging-composer-backdrop-blur:nth-child(4) {
|
|
222
|
-
|
|
223
|
-
-webkit-backdrop-filter: blur(12px);
|
|
224
|
-
mask: linear-gradient(
|
|
225
|
-
to bottom,
|
|
226
|
-
transparent calc(var(--messaging-composer-fade-band) * 0.55),
|
|
227
|
-
black calc(var(--messaging-composer-fade-band) * 0.72),
|
|
228
|
-
black calc(var(--messaging-composer-fade-band) * 0.88),
|
|
229
|
-
transparent var(--messaging-composer-fade-band)
|
|
230
|
-
);
|
|
231
|
-
-webkit-mask: linear-gradient(
|
|
192
|
+
--messaging-composer-blur-mask: linear-gradient(
|
|
232
193
|
to bottom,
|
|
233
|
-
transparent
|
|
234
|
-
black
|
|
235
|
-
|
|
236
|
-
transparent var(--messaging-composer-fade-band)
|
|
194
|
+
transparent 60%,
|
|
195
|
+
black 80%,
|
|
196
|
+
transparent 100%
|
|
237
197
|
);
|
|
198
|
+
backdrop-filter: blur(12.8px);
|
|
199
|
+
-webkit-backdrop-filter: blur(12.8px);
|
|
200
|
+
mask: var(--messaging-composer-blur-mask);
|
|
201
|
+
-webkit-mask: var(--messaging-composer-blur-mask);
|
|
238
202
|
}
|
|
239
203
|
|
|
240
204
|
.messaging-composer-backdrop-blur:nth-child(5) {
|
|
241
|
-
|
|
242
|
-
-webkit-backdrop-filter: blur(16px);
|
|
243
|
-
mask: linear-gradient(
|
|
205
|
+
--messaging-composer-blur-mask: linear-gradient(
|
|
244
206
|
to bottom,
|
|
245
|
-
transparent
|
|
246
|
-
black
|
|
247
|
-
black var(--messaging-composer-fade-band),
|
|
248
|
-
transparent var(--messaging-composer-fade-band)
|
|
249
|
-
);
|
|
250
|
-
-webkit-mask: linear-gradient(
|
|
251
|
-
to bottom,
|
|
252
|
-
transparent calc(var(--messaging-composer-fade-band) * 0.7),
|
|
253
|
-
black calc(var(--messaging-composer-fade-band) * 0.9),
|
|
254
|
-
black var(--messaging-composer-fade-band),
|
|
255
|
-
transparent var(--messaging-composer-fade-band)
|
|
207
|
+
transparent 80%,
|
|
208
|
+
black 100%
|
|
256
209
|
);
|
|
210
|
+
backdrop-filter: blur(16px);
|
|
211
|
+
-webkit-backdrop-filter: blur(16px);
|
|
212
|
+
mask: var(--messaging-composer-blur-mask);
|
|
213
|
+
-webkit-mask: var(--messaging-composer-blur-mask);
|
|
257
214
|
}
|
|
258
215
|
|
|
259
216
|
.messaging-composer-backdrop-gradient {
|
|
@@ -261,9 +218,9 @@
|
|
|
261
218
|
inset: 0;
|
|
262
219
|
pointer-events: none;
|
|
263
220
|
background: linear-gradient(
|
|
264
|
-
|
|
265
|
-
rgba(251, 250, 249, 0) 0
|
|
266
|
-
#fbfaf9
|
|
221
|
+
to bottom,
|
|
222
|
+
rgba(251, 250, 249, 0) 0%,
|
|
223
|
+
#fbfaf9 100%
|
|
267
224
|
);
|
|
268
225
|
}
|
|
269
226
|
|