@boyernick/standard-ui-react 0.4.2-canary.52 → 0.4.2-canary.54
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/package.json +1 -1
- package/src/icons.tsx +31 -1
- package/src/index.ts +1 -0
- package/src/modal.tsx +31 -5
package/package.json
CHANGED
package/src/icons.tsx
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
"use client"
|
|
2
2
|
|
|
3
|
-
import
|
|
3
|
+
import {
|
|
4
|
+
CentralIconBase,
|
|
5
|
+
type CentralIconBaseProps,
|
|
6
|
+
} from "@central-icons-react/round-outlined-radius-2-stroke-2/CentralIconBase"
|
|
4
7
|
import { IconArrowDown as IconArrowDownBase } from "@central-icons-react/round-outlined-radius-2-stroke-2/IconArrowDown"
|
|
5
8
|
import { IconBell as IconBellBase } from "@central-icons-react/round-outlined-radius-2-stroke-2/IconBell"
|
|
6
9
|
import { IconCalendar1 as IconCalendar1Base } from "@central-icons-react/round-outlined-radius-2-stroke-2/IconCalendar1"
|
|
@@ -63,6 +66,32 @@ export const withCentralIconDefaults = (
|
|
|
63
66
|
}
|
|
64
67
|
|
|
65
68
|
export const IconArrowDown = withCentralIconDefaults(IconArrowDownBase)
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Central ships a Small cut of the cross and the chevrons but not of the down
|
|
72
|
+
* arrow, whose full-size glyph is 16 units tall and outweighs IconCrossSmall
|
|
73
|
+
* beside it. Drawn on Central's base with the same 2px round stroke, 9 × 12
|
|
74
|
+
* units: a touch larger than the cross's 8 × 8 so it holds its own.
|
|
75
|
+
*/
|
|
76
|
+
const IconArrowDownSmallBase = (props: CentralIconBaseProps) => (
|
|
77
|
+
<CentralIconBase
|
|
78
|
+
{...props}
|
|
79
|
+
ariaLabel="arrow-down-small, download"
|
|
80
|
+
maskId="round-outlined-radius-2-stroke-2-IconArrowDownSmall"
|
|
81
|
+
>
|
|
82
|
+
<path
|
|
83
|
+
d="M12 6V18M7.5 13.5L12 18L16.5 13.5"
|
|
84
|
+
stroke="currentColor"
|
|
85
|
+
strokeWidth="2"
|
|
86
|
+
strokeLinecap="round"
|
|
87
|
+
strokeLinejoin="round"
|
|
88
|
+
/>
|
|
89
|
+
</CentralIconBase>
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
export const IconArrowDownSmall = withCentralIconDefaults(
|
|
93
|
+
IconArrowDownSmallBase,
|
|
94
|
+
)
|
|
66
95
|
export const IconBell = withCentralIconDefaults(IconBellBase)
|
|
67
96
|
export const IconCalendar1 = withCentralIconDefaults(IconCalendar1Base)
|
|
68
97
|
export const IconChainLink1 = withCentralIconDefaults(IconChainLink1Base)
|
|
@@ -124,6 +153,7 @@ export const iconGallery = [
|
|
|
124
153
|
{ name: "IconPlus", Icon: IconPlus },
|
|
125
154
|
{ name: "IconMinus", Icon: IconMinus },
|
|
126
155
|
{ name: "IconCrossSmall", Icon: IconCrossSmall },
|
|
156
|
+
{ name: "IconArrowDownSmall", Icon: IconArrowDownSmall },
|
|
127
157
|
{ name: "IconX", Icon: IconX },
|
|
128
158
|
{ name: "IconCheckmark1", Icon: IconCheckmark1 },
|
|
129
159
|
{ name: "IconChevronBottom", Icon: IconChevronBottom },
|
package/src/index.ts
CHANGED
package/src/modal.tsx
CHANGED
|
@@ -31,7 +31,7 @@ import {
|
|
|
31
31
|
DialogTrigger,
|
|
32
32
|
} from "./dialog"
|
|
33
33
|
import {
|
|
34
|
-
|
|
34
|
+
IconArrowDownSmall,
|
|
35
35
|
IconChevronRightSmall,
|
|
36
36
|
IconCrossSmall,
|
|
37
37
|
} from "./icons"
|
|
@@ -174,15 +174,39 @@ const ModalImage = ({
|
|
|
174
174
|
}: Pick<GalleryItem, "src" | "alt" | "imgProps"> & {
|
|
175
175
|
fit?: "viewport" | "contain"
|
|
176
176
|
}) => {
|
|
177
|
-
const { className, ...props } = imgProps ?? {}
|
|
177
|
+
const { className, onLoad, onError, ...props } = imgProps ?? {}
|
|
178
|
+
// Held transparent until decoded, then faded in: a large file otherwise
|
|
179
|
+
// paints in top-down bands inside the lightbox. The ref catches an image the
|
|
180
|
+
// browser already has (a cached file, or the one the trigger just showed)
|
|
181
|
+
// before first paint, so it appears at once with nothing to fade.
|
|
182
|
+
const [loaded, setLoaded] = useState(false)
|
|
183
|
+
const catchLoaded = useCallback((image: HTMLImageElement | null) => {
|
|
184
|
+
if (image?.complete && image.naturalWidth > 0) setLoaded(true)
|
|
185
|
+
}, [])
|
|
178
186
|
|
|
179
187
|
return (
|
|
180
188
|
// eslint-disable-next-line @next/next/no-img-element
|
|
181
189
|
<img
|
|
190
|
+
ref={catchLoaded}
|
|
182
191
|
src={src}
|
|
183
192
|
alt={alt}
|
|
184
193
|
draggable={false}
|
|
194
|
+
onLoad={(event) => {
|
|
195
|
+
onLoad?.(event)
|
|
196
|
+
event.currentTarget
|
|
197
|
+
.decode()
|
|
198
|
+
.catch(() => {})
|
|
199
|
+
.then(() => setLoaded(true))
|
|
200
|
+
}}
|
|
201
|
+
onError={(event) => {
|
|
202
|
+
onError?.(event)
|
|
203
|
+
// Reveal the broken image rather than leave an invisible one.
|
|
204
|
+
setLoaded(true)
|
|
205
|
+
}}
|
|
185
206
|
className={cn(
|
|
207
|
+
// Linear, like every fade in `motion` — see the note there.
|
|
208
|
+
"transition-opacity duration-[var(--duration-lg)] ease-linear motion-reduce:transition-none",
|
|
209
|
+
!loaded && "opacity-0",
|
|
186
210
|
"rounded-lg object-contain shadow-lg select-none",
|
|
187
211
|
fit === "contain"
|
|
188
212
|
? "max-h-full max-w-full"
|
|
@@ -233,10 +257,12 @@ const ModalDownload = ({
|
|
|
233
257
|
iconOnly: true,
|
|
234
258
|
}),
|
|
235
259
|
modalControlClassName,
|
|
236
|
-
|
|
260
|
+
// Download sits top-left and Close top-right, where dismissal is
|
|
261
|
+
// expected; the gallery's previous/next arrows stay vertically centred.
|
|
262
|
+
"absolute top-4 left-4 z-20",
|
|
237
263
|
)}
|
|
238
264
|
>
|
|
239
|
-
<
|
|
265
|
+
<IconArrowDownSmall aria-hidden />
|
|
240
266
|
</a>
|
|
241
267
|
</ModalControlTooltip>
|
|
242
268
|
)
|
|
@@ -254,7 +280,7 @@ const ModalDismiss = () => (
|
|
|
254
280
|
aria-label="Close image"
|
|
255
281
|
className={cn(
|
|
256
282
|
modalControlClassName,
|
|
257
|
-
"absolute top-4
|
|
283
|
+
"absolute top-4 right-4 z-20",
|
|
258
284
|
)}
|
|
259
285
|
/>
|
|
260
286
|
}
|