@prosopo/procaptcha-common 2.11.21 → 2.11.23
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/.turbo/turbo-build$colon$cjs.log +7 -6
- package/.turbo/turbo-build$colon$tsc.log +18 -18
- package/.turbo/turbo-build.log +7 -6
- package/CHANGELOG.md +41 -0
- package/dist/cjs/events/trust.cjs +20 -0
- package/dist/cjs/index.cjs +2 -0
- package/dist/cjs/reactComponents/Checkbox.cjs +41 -20
- package/dist/cjs/reactComponents/Reload.cjs +22 -16
- package/dist/events/trust.d.ts +5 -0
- package/dist/events/trust.d.ts.map +1 -0
- package/dist/events/trust.js +20 -0
- package/dist/events/trust.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/reactComponents/Checkbox.d.ts.map +1 -1
- package/dist/reactComponents/Checkbox.js +42 -21
- package/dist/reactComponents/Checkbox.js.map +1 -1
- package/dist/reactComponents/Reload.d.ts.map +1 -1
- package/dist/reactComponents/Reload.js +22 -16
- package/dist/reactComponents/Reload.js.map +1 -1
- package/package.json +5 -5
- package/src/events/trust.ts +46 -0
- package/src/index.ts +1 -0
- package/src/reactComponents/Checkbox.tsx +68 -23
- package/src/reactComponents/Reload.tsx +28 -25
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Reload.d.ts","sourceRoot":"","sources":["../../src/reactComponents/Reload.tsx"],"names":[],"mappings":"AAeA,OAAO,EAAE,KAAK,oBAAoB,EAAE,KAAK,EAAE,EAAqB,MAAM,OAAO,CAAC;AAE9E,UAAU,iBAAkB,SAAQ,oBAAoB,CAAC,iBAAiB,CAAC;IAC1E,UAAU,EAAE,OAAO,GAAG,MAAM,CAAC;IAC7B,QAAQ,EAAE,MAAM,IAAI,CAAC;CACrB;AAaD,eAAO,MAAM,YAAY,EAAE,EAAE,CAAC,iBAAiB,
|
|
1
|
+
{"version":3,"file":"Reload.d.ts","sourceRoot":"","sources":["../../src/reactComponents/Reload.tsx"],"names":[],"mappings":"AAeA,OAAO,EAAE,KAAK,oBAAoB,EAAE,KAAK,EAAE,EAAqB,MAAM,OAAO,CAAC;AAE9E,UAAU,iBAAkB,SAAQ,oBAAoB,CAAC,iBAAiB,CAAC;IAC1E,UAAU,EAAE,OAAO,GAAG,MAAM,CAAC;IAC7B,QAAQ,EAAE,MAAM,IAAI,CAAC;CACrB;AAaD,eAAO,MAAM,YAAY,EAAE,EAAE,CAAC,iBAAiB,CAoE9C,CAAC"}
|
|
@@ -4,17 +4,17 @@ import { jsx, jsxs } from "@emotion/react/jsx-runtime";
|
|
|
4
4
|
//#region src/reactComponents/Reload.tsx
|
|
5
5
|
var buttonStyleBase = {
|
|
6
6
|
border: "none",
|
|
7
|
-
|
|
8
|
-
paddingBottom: "6px",
|
|
7
|
+
padding: "8px",
|
|
9
8
|
cursor: "pointer",
|
|
10
|
-
height: "
|
|
11
|
-
width: "
|
|
9
|
+
height: "40px",
|
|
10
|
+
width: "40px",
|
|
12
11
|
borderRadius: "50%",
|
|
13
12
|
display: "flex"
|
|
14
13
|
};
|
|
15
14
|
var ReloadButton = ({ themeColor, onReload }) => {
|
|
16
15
|
const theme = useMemo(() => themeColor === "light" ? lightTheme : darkTheme, [themeColor]);
|
|
17
16
|
const [hover, setHover] = useState(false);
|
|
17
|
+
const [focusVisible, setFocusVisible] = useState(false);
|
|
18
18
|
return /* @__PURE__ */ jsx("button", {
|
|
19
19
|
className: "reload-button",
|
|
20
20
|
"aria-label": "Reload",
|
|
@@ -22,27 +22,33 @@ var ReloadButton = ({ themeColor, onReload }) => {
|
|
|
22
22
|
style: useMemo(() => {
|
|
23
23
|
return {
|
|
24
24
|
...buttonStyleBase,
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
25
|
+
...focusVisible ? {
|
|
26
|
+
outline: `3px solid ${theme.palette.primary.main}`,
|
|
27
|
+
outlineOffset: "2px"
|
|
28
|
+
} : {},
|
|
29
|
+
backgroundColor: hover ? theme.palette.primaryContainer.hover : theme.palette.primaryContainer.main,
|
|
30
|
+
color: theme.palette.primaryContainer.contrastText,
|
|
31
|
+
transition: "background-color 0.25s",
|
|
31
32
|
justifyContent: "center",
|
|
32
33
|
alignItems: "center",
|
|
33
|
-
margin: "0 auto"
|
|
34
|
-
backgroundColor: hover ? theme.palette.grey[700] : theme.palette.background.default
|
|
34
|
+
margin: "0 auto"
|
|
35
35
|
};
|
|
36
|
-
}, [
|
|
36
|
+
}, [
|
|
37
|
+
hover,
|
|
38
|
+
theme,
|
|
39
|
+
focusVisible
|
|
40
|
+
]),
|
|
37
41
|
onMouseEnter: () => setHover(true),
|
|
38
42
|
onMouseLeave: () => setHover(false),
|
|
43
|
+
onFocus: (e) => setFocusVisible(e.target.matches(":focus-visible")),
|
|
44
|
+
onBlur: () => setFocusVisible(false),
|
|
39
45
|
onClick: (e) => {
|
|
40
46
|
e.preventDefault();
|
|
41
47
|
onReload();
|
|
42
48
|
},
|
|
43
49
|
children: /* @__PURE__ */ jsxs("svg", {
|
|
44
|
-
width: "
|
|
45
|
-
height: "
|
|
50
|
+
width: "24px",
|
|
51
|
+
height: "24px",
|
|
46
52
|
viewBox: "0 0 16 16",
|
|
47
53
|
version: "1.1",
|
|
48
54
|
xmlns: "http://www.w3.org/2000/svg",
|
|
@@ -50,7 +56,7 @@ var ReloadButton = ({ themeColor, onReload }) => {
|
|
|
50
56
|
style: { display: "flex" },
|
|
51
57
|
children: [/* @__PURE__ */ jsx("title", { children: "reload" }), /* @__PURE__ */ jsx("path", {
|
|
52
58
|
shapeRendering: "optimizeQuality",
|
|
53
|
-
fill:
|
|
59
|
+
fill: theme.palette.primaryContainer.contrastText,
|
|
54
60
|
transform: "scale(0.0416)",
|
|
55
61
|
d: "M234.666667,149.333333 L234.666667,106.666667 L314.564847,106.664112 C287.579138,67.9778918 242.745446,42.6666667 192,42.6666667 C109.525477,42.6666667 42.6666667,109.525477 42.6666667,192 C42.6666667,274.474523 109.525477,341.333333 192,341.333333 C268.201293,341.333333 331.072074,284.258623 340.195444,210.526102 L382.537159,215.817985 C370.807686,310.617565 289.973536,384 192,384 C85.961328,384 1.42108547e-14,298.038672 1.42108547e-14,192 C1.42108547e-14,85.961328 85.961328,1.42108547e-14 192,1.42108547e-14 C252.316171,1.42108547e-14 306.136355,27.8126321 341.335366,71.3127128 L341.333333,1.42108547e-14 L384,1.42108547e-14 L384,149.333333 L234.666667,149.333333 Z"
|
|
56
62
|
})]
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Reload.js","sourceRoot":"","sources":["../../src/reactComponents/Reload.tsx"],"names":[],"mappings":";AAcA,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AACjE,OAAO,EAAsC,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"Reload.js","sourceRoot":"","sources":["../../src/reactComponents/Reload.tsx"],"names":[],"mappings":";AAcA,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AACjE,OAAO,EAAsC,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAQ9E,MAAM,eAAe,GAAG;IACvB,MAAM,EAAE,MAAM;IACd,OAAO,EAAE,KAAK;IACd,MAAM,EAAE,SAAS;IACjB,MAAM,EAAE,MAAM;IACd,KAAK,EAAE,MAAM;IACb,YAAY,EAAE,KAAK;IACnB,OAAO,EAAE,MAAM;CACf,CAAC;AAEF,MAAM,CAAC,MAAM,YAAY,GAA0B,CAAC,EACnD,UAAU,EACV,QAAQ,GACW,EAAE,EAAE;IACvB,MAAM,KAAK,GAAG,OAAO,CACpB,GAAG,EAAE,CAAC,CAAC,UAAU,KAAK,OAAO,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC,EACvD,CAAC,UAAU,CAAC,CACZ,CAAC;IACF,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAG1C,MAAM,CAAC,YAAY,EAAE,eAAe,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IACxD,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,EAAE;QAChC,OAAO;YACN,GAAG,eAAe;YAClB,GAAG,CAAC,YAAY;gBACf,CAAC,CAAC;oBAEA,OAAO,EAAE,aAAa,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,EAAE;oBAClD,aAAa,EAAE,KAAK;iBACpB;gBACF,CAAC,CAAC,EAAE,CAAC;YAGN,eAAe,EAAE,KAAK;gBACrB,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,gBAAgB,CAAC,KAAK;gBACtC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,gBAAgB,CAAC,IAAI;YACtC,KAAK,EAAE,KAAK,CAAC,OAAO,CAAC,gBAAgB,CAAC,YAAY;YAClD,UAAU,EAAE,wBAAwB;YACpC,cAAc,EAAE,QAAQ;YACxB,UAAU,EAAE,QAAQ;YACpB,MAAM,EAAE,QAAQ;SAChB,CAAC;IACH,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,YAAY,CAAC,CAAC,CAAC;IACjC,OAAO,CACN,iBACC,SAAS,EAAC,eAAe,gBACd,QAAQ,EACnB,IAAI,EAAC,QAAQ,EACb,KAAK,EAAE,WAAW,EAClB,YAAY,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,EAClC,YAAY,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,EACnC,OAAO,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,eAAe,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC,EACnE,MAAM,EAAE,GAAG,EAAE,CAAC,eAAe,CAAC,KAAK,CAAC,EACpC,OAAO,EAAE,CAAC,CAAC,EAAE,EAAE;YACd,CAAC,CAAC,cAAc,EAAE,CAAC;YACnB,QAAQ,EAAE,CAAC;QACZ,CAAC,YAED,eACC,KAAK,EAAC,MAAM,EACZ,MAAM,EAAC,MAAM,EACb,OAAO,EAAC,WAAW,EACnB,OAAO,EAAC,KAAK,EACb,KAAK,EAAC,4BAA4B,EAClC,UAAU,EAAC,8BAA8B,EACzC,KAAK,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,aAE1B,qCAAqB,EACrB,eACC,cAAc,EAAC,iBAAiB,EAChC,IAAI,EAAE,KAAK,CAAC,OAAO,CAAC,gBAAgB,CAAC,YAAY,EACjD,SAAS,EAAE,eAAe,EAC1B,CAAC,EAAC,mqBAAmqB,GACpqB,IACG,GACE,CACT,CAAC;AACH,CAAC,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@prosopo/procaptcha-common",
|
|
3
|
-
"version": "2.11.
|
|
3
|
+
"version": "2.11.23",
|
|
4
4
|
"author": "PROSOPO LIMITED <info@prosopo.io>",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -34,10 +34,10 @@
|
|
|
34
34
|
"dependencies": {
|
|
35
35
|
"@emotion/react": "11.11.1",
|
|
36
36
|
"@emotion/styled": "11.11.0",
|
|
37
|
-
"@prosopo/account": "2.8.
|
|
38
|
-
"@prosopo/load-balancer": "2.10.
|
|
39
|
-
"@prosopo/types": "5.0.
|
|
40
|
-
"@prosopo/widget-skeleton": "2.8.
|
|
37
|
+
"@prosopo/account": "2.8.69",
|
|
38
|
+
"@prosopo/load-balancer": "2.10.20",
|
|
39
|
+
"@prosopo/types": "5.0.3",
|
|
40
|
+
"@prosopo/widget-skeleton": "2.8.6",
|
|
41
41
|
"react": "18.3.1",
|
|
42
42
|
"react-dom": "18.3.1"
|
|
43
43
|
},
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
// Copyright 2021-2026 Prosopo (UK) Ltd.
|
|
2
|
+
//
|
|
3
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
// you may not use this file except in compliance with the License.
|
|
5
|
+
// You may obtain a copy of the License at
|
|
6
|
+
//
|
|
7
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
//
|
|
9
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
// See the License for the specific language governing permissions and
|
|
13
|
+
// limitations under the License.
|
|
14
|
+
|
|
15
|
+
// Replaced at bundle time by vite's `define` — see the `define` block in
|
|
16
|
+
// packages/procaptcha-bundle/vite.config.ts. Typed as possibly undefined
|
|
17
|
+
// because a build that doesn't define it leaves the identifier undeclared,
|
|
18
|
+
// which is why the read below is guarded by `typeof`.
|
|
19
|
+
declare const __PROSOPO_ALLOW_UNTRUSTED_EVENTS__: boolean | undefined;
|
|
20
|
+
|
|
21
|
+
export interface TrustableEvent {
|
|
22
|
+
readonly isTrusted: boolean;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Whether an event came from a real user rather than a script.
|
|
27
|
+
*
|
|
28
|
+
* The widget acts only on trusted input: a synthetic event is precisely how a
|
|
29
|
+
* bot drives a page, so honouring one would hand over the captcha for free.
|
|
30
|
+
*
|
|
31
|
+
* The single exception is the firefox cypress leg. cypress can only dispatch
|
|
32
|
+
* trusted input through the chrome devtools protocol, which it exposes for
|
|
33
|
+
* chromium browsers only, so on firefox every spec clicks synthetically.
|
|
34
|
+
* Building with PROSOPO_ALLOW_UNTRUSTED_EVENTS=1 opens the gate for that leg.
|
|
35
|
+
* Production builds pin the constant to false and the branch folds away, so
|
|
36
|
+
* the allowance cannot reach a shipped bundle.
|
|
37
|
+
*/
|
|
38
|
+
export function isEventTrusted(event: TrustableEvent): boolean {
|
|
39
|
+
if (event.isTrusted) {
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
return (
|
|
43
|
+
typeof __PROSOPO_ALLOW_UNTRUSTED_EVENTS__ !== "undefined" &&
|
|
44
|
+
__PROSOPO_ALLOW_UNTRUSTED_EVENTS__
|
|
45
|
+
);
|
|
46
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -17,6 +17,7 @@ import styled from "@emotion/styled";
|
|
|
17
17
|
import {
|
|
18
18
|
type Theme,
|
|
19
19
|
WIDGET_CHECKBOX_SPINNER_CSS_CLASS,
|
|
20
|
+
withAlpha,
|
|
20
21
|
} from "@prosopo/widget-skeleton";
|
|
21
22
|
import {
|
|
22
23
|
type ButtonHTMLAttributes,
|
|
@@ -25,6 +26,7 @@ import {
|
|
|
25
26
|
useMemo,
|
|
26
27
|
useState,
|
|
27
28
|
} from "react";
|
|
29
|
+
import { isEventTrusted } from "../events/trust.js";
|
|
28
30
|
|
|
29
31
|
interface CheckboxProps extends ButtonHTMLAttributes<HTMLButtonElement> {
|
|
30
32
|
theme: Theme;
|
|
@@ -45,20 +47,34 @@ const checkboxBefore = css`{
|
|
|
45
47
|
}
|
|
46
48
|
}`;
|
|
47
49
|
|
|
50
|
+
// In forced-colors mode (Windows High Contrast) backgrounds are overridden, so
|
|
51
|
+
// the custom-painted tick can disappear — fall back to the native control,
|
|
52
|
+
// which the OS draws in system colors. !important beats the inline styles.
|
|
53
|
+
const checkboxForcedColors = css`
|
|
54
|
+
@media (forced-colors: active) {
|
|
55
|
+
appearance: auto !important;
|
|
56
|
+
background-image: none !important;
|
|
57
|
+
}
|
|
58
|
+
`;
|
|
59
|
+
|
|
60
|
+
// 28px container with a 2dp stroke, centred in a 58px touch target. Larger
|
|
61
|
+
// than the 18dp M3 checkbox spec — kept at the original size deliberately, as
|
|
62
|
+
// the widget needs a more prominent target than a form checkbox.
|
|
63
|
+
const CHECKBOX_SIZE = "28px";
|
|
64
|
+
|
|
48
65
|
const baseStyle: CSSProperties = {
|
|
49
|
-
width:
|
|
50
|
-
height:
|
|
51
|
-
minWidth:
|
|
52
|
-
minHeight:
|
|
66
|
+
width: CHECKBOX_SIZE,
|
|
67
|
+
height: CHECKBOX_SIZE,
|
|
68
|
+
minWidth: CHECKBOX_SIZE,
|
|
69
|
+
minHeight: CHECKBOX_SIZE,
|
|
53
70
|
top: "auto",
|
|
54
71
|
left: "auto",
|
|
55
72
|
opacity: "1",
|
|
56
|
-
borderRadius: "12.5%",
|
|
57
73
|
appearance: "none",
|
|
58
74
|
cursor: "pointer",
|
|
59
75
|
margin: "0",
|
|
60
76
|
borderStyle: "solid",
|
|
61
|
-
borderWidth: "
|
|
77
|
+
borderWidth: "2px",
|
|
62
78
|
};
|
|
63
79
|
|
|
64
80
|
const ID_LETTERS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
|
@@ -86,14 +102,24 @@ export const Checkbox: FC<CheckboxProps> = ({
|
|
|
86
102
|
error,
|
|
87
103
|
loading,
|
|
88
104
|
}: CheckboxProps) => {
|
|
89
|
-
const checkboxStyleBase: CSSProperties = {
|
|
90
|
-
...baseStyle,
|
|
91
|
-
border: `1px solid ${theme.palette.background.contrastText}`,
|
|
92
|
-
};
|
|
93
105
|
const [hover, setHover] = useState(false);
|
|
94
106
|
|
|
107
|
+
// M3 focus indicator: a 3dp outline offset by 2dp, drawn only for keyboard
|
|
108
|
+
// focus. The control previously had no focus affordance at all.
|
|
109
|
+
const checkboxFocus = useMemo(
|
|
110
|
+
() => css`
|
|
111
|
+
&:focus-visible {
|
|
112
|
+
outline: 3px solid ${theme.palette.primary.main};
|
|
113
|
+
outline-offset: 2px;
|
|
114
|
+
}
|
|
115
|
+
`,
|
|
116
|
+
[theme],
|
|
117
|
+
);
|
|
118
|
+
|
|
95
119
|
const ResponsiveLabel = styled.label<ResponsiveLabelProps>`
|
|
96
|
-
|
|
120
|
+
/* The label sits on the widget surface, so it takes onSurface — not the
|
|
121
|
+
dialog container's on-colour. */
|
|
122
|
+
color: ${theme.palette.onSurface};
|
|
97
123
|
position: relative;
|
|
98
124
|
display: flex !important;
|
|
99
125
|
cursor: pointer;
|
|
@@ -117,18 +143,37 @@ export const Checkbox: FC<CheckboxProps> = ({
|
|
|
117
143
|
}
|
|
118
144
|
`;
|
|
119
145
|
|
|
120
|
-
// biome-ignore lint/correctness/useExhaustiveDependencies: TODO fix
|
|
121
146
|
const checkboxStyle: CSSProperties = useMemo(() => {
|
|
147
|
+
// White (token) tick painted directly onto the box so the checked state is
|
|
148
|
+
// identical in light and dark mode — the native control can't be themed.
|
|
149
|
+
const tickColor = encodeURIComponent(theme.palette.checkbox.tick);
|
|
150
|
+
const checkImage = `url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='${tickColor}' d='M9 16.17 4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z'/%3E%3C/svg%3E")`;
|
|
151
|
+
// M3 hover feedback is a state layer — the on-colour at 8% expressed as a
|
|
152
|
+
// spread ring around the container — not a change of stroke colour.
|
|
153
|
+
const stateLayerColor = checked
|
|
154
|
+
? theme.palette.checkbox.fill
|
|
155
|
+
: theme.palette.onSurface;
|
|
122
156
|
return {
|
|
123
|
-
...
|
|
124
|
-
|
|
125
|
-
? theme.palette.background.contrastText
|
|
126
|
-
: theme.palette.border,
|
|
127
|
-
appearance: checked ? "auto" : "none",
|
|
128
|
-
flex: 1,
|
|
157
|
+
...baseStyle,
|
|
158
|
+
// 15px each side around a 28px box gives a 58px touch target.
|
|
129
159
|
margin: "15px",
|
|
130
|
-
|
|
131
|
-
|
|
160
|
+
borderRadius: theme.shape.checkbox,
|
|
161
|
+
borderColor: checked
|
|
162
|
+
? theme.palette.checkbox.fill
|
|
163
|
+
: theme.palette.checkbox.border,
|
|
164
|
+
backgroundColor: checked
|
|
165
|
+
? theme.palette.checkbox.fill
|
|
166
|
+
: theme.palette.surface,
|
|
167
|
+
backgroundImage: checked ? checkImage : "none",
|
|
168
|
+
backgroundRepeat: "no-repeat",
|
|
169
|
+
backgroundPosition: "center",
|
|
170
|
+
// Tick inset within the 28px container.
|
|
171
|
+
backgroundSize: "20px 20px",
|
|
172
|
+
boxShadow: hover
|
|
173
|
+
? `0 0 0 10px ${withAlpha(stateLayerColor, theme.stateLayer.hover)}`
|
|
174
|
+
: "none",
|
|
175
|
+
transition:
|
|
176
|
+
"background-color 0.2s ease, border-color 0.2s ease, box-shadow 0.2s ease",
|
|
132
177
|
};
|
|
133
178
|
}, [hover, theme, checked]);
|
|
134
179
|
const id = generateRandomId();
|
|
@@ -152,12 +197,12 @@ export const Checkbox: FC<CheckboxProps> = ({
|
|
|
152
197
|
id={id}
|
|
153
198
|
onMouseEnter={() => setHover(true)}
|
|
154
199
|
onMouseLeave={() => setHover(false)}
|
|
155
|
-
css={checkboxBefore}
|
|
200
|
+
css={[checkboxBefore, checkboxForcedColors, checkboxFocus]}
|
|
156
201
|
type={"checkbox"}
|
|
157
202
|
aria-live={"assertive"}
|
|
158
203
|
aria-label={labelText}
|
|
159
204
|
onKeyDown={(e) => {
|
|
160
|
-
if (!e
|
|
205
|
+
if (!isEventTrusted(e)) {
|
|
161
206
|
return;
|
|
162
207
|
}
|
|
163
208
|
if (e.key === "Enter") {
|
|
@@ -168,7 +213,7 @@ export const Checkbox: FC<CheckboxProps> = ({
|
|
|
168
213
|
}
|
|
169
214
|
}}
|
|
170
215
|
onChange={(e) => {
|
|
171
|
-
if (!e
|
|
216
|
+
if (!isEventTrusted(e)) {
|
|
172
217
|
return;
|
|
173
218
|
}
|
|
174
219
|
e.preventDefault();
|
|
@@ -20,13 +20,13 @@ interface ReloadButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
|
|
|
20
20
|
onReload: () => void;
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
+
// M3 icon button: a 40dp circular container holding a 24dp icon.
|
|
23
24
|
const buttonStyleBase = {
|
|
24
25
|
border: "none",
|
|
25
|
-
|
|
26
|
-
paddingBottom: "6px",
|
|
26
|
+
padding: "8px",
|
|
27
27
|
cursor: "pointer",
|
|
28
|
-
height: "
|
|
29
|
-
width: "
|
|
28
|
+
height: "40px",
|
|
29
|
+
width: "40px",
|
|
30
30
|
borderRadius: "50%",
|
|
31
31
|
display: "flex",
|
|
32
32
|
};
|
|
@@ -40,28 +40,31 @@ export const ReloadButton: FC<ReloadButtonProps> = ({
|
|
|
40
40
|
[themeColor],
|
|
41
41
|
);
|
|
42
42
|
const [hover, setHover] = useState(false);
|
|
43
|
+
// M3 requires a visible focus indicator; matched imperatively so the ring is
|
|
44
|
+
// keyboard-only.
|
|
45
|
+
const [focusVisible, setFocusVisible] = useState(false);
|
|
43
46
|
const buttonStyle = useMemo(() => {
|
|
44
|
-
|
|
47
|
+
return {
|
|
45
48
|
...buttonStyleBase,
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
49
|
+
...(focusVisible
|
|
50
|
+
? {
|
|
51
|
+
// M3 focus indicator: 3dp outline, 2dp offset.
|
|
52
|
+
outline: `3px solid ${theme.palette.primary.main}`,
|
|
53
|
+
outlineOffset: "2px",
|
|
54
|
+
}
|
|
55
|
+
: {}),
|
|
56
|
+
// Material 3 tonal icon button; hover swaps to the state-layer fill so
|
|
57
|
+
// the feedback is visible in dark mode too (a brightness filter is not).
|
|
58
|
+
backgroundColor: hover
|
|
59
|
+
? theme.palette.primaryContainer.hover
|
|
60
|
+
: theme.palette.primaryContainer.main,
|
|
61
|
+
color: theme.palette.primaryContainer.contrastText,
|
|
62
|
+
transition: "background-color 0.25s",
|
|
54
63
|
justifyContent: "center",
|
|
55
64
|
alignItems: "center",
|
|
56
65
|
margin: "0 auto",
|
|
57
66
|
};
|
|
58
|
-
|
|
59
|
-
...baseStyle,
|
|
60
|
-
backgroundColor: hover
|
|
61
|
-
? theme.palette.grey[700]
|
|
62
|
-
: theme.palette.background.default,
|
|
63
|
-
};
|
|
64
|
-
}, [hover, theme]);
|
|
67
|
+
}, [hover, theme, focusVisible]);
|
|
65
68
|
return (
|
|
66
69
|
<button
|
|
67
70
|
className="reload-button"
|
|
@@ -70,14 +73,16 @@ export const ReloadButton: FC<ReloadButtonProps> = ({
|
|
|
70
73
|
style={buttonStyle}
|
|
71
74
|
onMouseEnter={() => setHover(true)}
|
|
72
75
|
onMouseLeave={() => setHover(false)}
|
|
76
|
+
onFocus={(e) => setFocusVisible(e.target.matches(":focus-visible"))}
|
|
77
|
+
onBlur={() => setFocusVisible(false)}
|
|
73
78
|
onClick={(e) => {
|
|
74
79
|
e.preventDefault();
|
|
75
80
|
onReload();
|
|
76
81
|
}}
|
|
77
82
|
>
|
|
78
83
|
<svg
|
|
79
|
-
width="
|
|
80
|
-
height="
|
|
84
|
+
width="24px"
|
|
85
|
+
height="24px"
|
|
81
86
|
viewBox="0 0 16 16"
|
|
82
87
|
version="1.1"
|
|
83
88
|
xmlns="http://www.w3.org/2000/svg"
|
|
@@ -87,9 +92,7 @@ export const ReloadButton: FC<ReloadButtonProps> = ({
|
|
|
87
92
|
<title>reload</title>
|
|
88
93
|
<path
|
|
89
94
|
shapeRendering="optimizeQuality"
|
|
90
|
-
fill={
|
|
91
|
-
hover ? theme.palette.primary.contrastText : theme.palette.grey[700]
|
|
92
|
-
}
|
|
95
|
+
fill={theme.palette.primaryContainer.contrastText}
|
|
93
96
|
transform={"scale(0.0416)"}
|
|
94
97
|
d="M234.666667,149.333333 L234.666667,106.666667 L314.564847,106.664112 C287.579138,67.9778918 242.745446,42.6666667 192,42.6666667 C109.525477,42.6666667 42.6666667,109.525477 42.6666667,192 C42.6666667,274.474523 109.525477,341.333333 192,341.333333 C268.201293,341.333333 331.072074,284.258623 340.195444,210.526102 L382.537159,215.817985 C370.807686,310.617565 289.973536,384 192,384 C85.961328,384 1.42108547e-14,298.038672 1.42108547e-14,192 C1.42108547e-14,85.961328 85.961328,1.42108547e-14 192,1.42108547e-14 C252.316171,1.42108547e-14 306.136355,27.8126321 341.335366,71.3127128 L341.333333,1.42108547e-14 L384,1.42108547e-14 L384,149.333333 L234.666667,149.333333 Z"
|
|
95
98
|
/>
|