Skip to content

Commit e330bff

Browse files
committed
Merge branch 'master' of https://github.com/jsonwebtoken/jsonwebtoken.github.io into gh-actions-testing-stale-pr
2 parents a898c83 + 56ddd57 commit e330bff

File tree

13 files changed

+292
-15
lines changed

13 files changed

+292
-15
lines changed

src/features/common/components/card-tabs/card-tabs.component.tsx

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,31 +21,34 @@ import {
2121
TabPanel,
2222
Tabs,
2323
} from "react-aria-components";
24+
import { Spinner } from "../spinner/spinner.component";
2425

2526
type CardTabsProps = {
2627
resizeId: string;
2728
languageCode: string;
2829
title: string | null;
2930
cards: CardComponentProps[];
3031
activeTabId: string;
32+
isLoading?: boolean;
3133
handleActiveCardChange: (tabId: string) => void;
3234
};
3335

3436
const CardTabs: React.FC<CardTabsProps> = ({
3537
resizeId,
3638
title,
3739
cards,
40+
isLoading,
3841
activeTabId,
3942
handleActiveCardChange,
4043
}) => {
4144
const tabsId = useId();
4245

4346
const outputModalState$ = useDebuggerStore(
44-
(state) => state.outputModalState$,
47+
(state) => state.outputModalState$
4548
);
4649
const outputModalId$ = useDebuggerStore((state) => state.outputModalId$);
4750
const closeOutputModal$ = useDebuggerStore(
48-
(state) => state.closeOutputModal$,
51+
(state) => state.closeOutputModal$
4952
);
5053

5154
const activeCard = cards.filter((card) => card.id === activeTabId)[0];
@@ -166,9 +169,12 @@ const CardTabs: React.FC<CardTabsProps> = ({
166169
className={styles.cardTabs__container}
167170
>
168171
{title && (
169-
<h4 id={tabsId} className={styles.cardTabs__title}>
170-
{title}
171-
</h4>
172+
<div className={styles.cardTabs__title__container}>
173+
<h4 id={tabsId} className={styles.cardTabs__title}>
174+
{title}
175+
</h4>
176+
{isLoading && <Spinner />}
177+
</div>
172178
)}
173179
<Tabs
174180
selectedKey={activeCard.id}
@@ -269,6 +275,7 @@ type CardTabsWithTabPersistenceComponentProps = {
269275
languageCode: string;
270276
title: string | null;
271277
cards: CardComponentProps[];
278+
isLoading?: boolean;
272279
handleTabChange: (key: string) => void;
273280
};
274281

@@ -281,6 +288,7 @@ export const CardTabsWithTabPersistenceComponentProps: React.FC<
281288
languageCode,
282289
title,
283290
cards,
291+
isLoading,
284292
handleTabChange,
285293
}) => {
286294
const [activeTabId, setActiveTabId] = useState<string>(initialTabId);
@@ -309,6 +317,7 @@ export const CardTabsWithTabPersistenceComponentProps: React.FC<
309317
languageCode={languageCode}
310318
title={title}
311319
cards={cards}
320+
isLoading={isLoading}
312321
activeTabId={activeTabId}
313322
handleActiveCardChange={handleActiveCardChange}
314323
/>

src/features/common/components/card-tabs/card-tabs.module.scss

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
line-height: 1.375rem;
2525
font-weight: 500;
2626
letter-spacing: 0.24px;
27+
margin-right: 8px;
2728
}
2829

2930
.cardTabs {
@@ -49,6 +50,11 @@ $cardTabs__tabList__height: 2.5rem;
4950
flex-shrink: 0;
5051
}
5152

53+
.cardTabs__title__container {
54+
display: flex;
55+
56+
}
57+
5258
.cardTab__title {
5359
position: relative;
5460
display: flex;
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { Checkbox, type CheckboxProps } from "react-aria-components";
2+
import styles from './checkbox.module.scss'
3+
4+
export function CheckboxComponent({
5+
children,
6+
...props
7+
}: Omit<CheckboxProps, "children"> & {
8+
children?: React.ReactNode;
9+
}) {
10+
return (
11+
<Checkbox {...props} className={styles.checkbox__component}>
12+
{({ isIndeterminate }) => (
13+
<>
14+
<div className={styles.checkbox}>
15+
<svg viewBox="0 0 18 18" aria-hidden="true">
16+
{isIndeterminate ? (
17+
<rect x={1} y={7.5} width={15} height={3} />
18+
) : (
19+
<polyline points="1 9 7 14 15 4" />
20+
)}
21+
</svg>
22+
</div>
23+
{children}
24+
</>
25+
)}
26+
</Checkbox>
27+
);
28+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
.checkbox__component {
2+
--selected-color: var(--color_bg_state_success);
3+
--selected-color-pressed: var(--color_fg_on_state_success_subtle);
4+
--checkmark-color: var(--color_border_state_success);
5+
6+
display: flex;
7+
/* This is needed so the HiddenInput is positioned correctly */
8+
position: relative;
9+
align-items: center;
10+
gap: 0.571rem;
11+
font-size: 1.143rem;
12+
color: white;
13+
forced-color-adjust: none;
14+
15+
.checkbox {
16+
width: 1.143rem;
17+
height: 1.143rem;
18+
border: 2px solid var(--color_fg_default);
19+
border-radius: 4px;
20+
transition: all 200ms;
21+
display: flex;
22+
align-items: center;
23+
justify-content: center;
24+
flex-shrink: 0;
25+
}
26+
27+
svg {
28+
width: 1rem;
29+
height: 1rem;
30+
fill: none;
31+
stroke: var(--functional-gray-0);
32+
stroke-width: 3px;
33+
stroke-dasharray: 22px;
34+
stroke-dashoffset: 66;
35+
transition: all 200ms;
36+
}
37+
38+
&[data-focus-visible] .checkbox {
39+
outline: 2px solid var(--color_fg_selected);
40+
outline-offset: 2px;
41+
}
42+
43+
&[data-selected],
44+
&[data-indeterminate] {
45+
.checkbox {
46+
border-color: var(--selected-color);
47+
background: var(--selected-color);
48+
}
49+
50+
&[data-pressed] .checkbox {
51+
border-color: var(--selected-color-pressed);
52+
background: var(--selected-color-pressed);
53+
}
54+
55+
svg {
56+
stroke-dashoffset: 44;
57+
}
58+
}
59+
60+
&[data-indeterminate] {
61+
& svg {
62+
stroke: none;
63+
fill: var(--checkmark-color);
64+
}
65+
}
66+
}

src/features/common/components/code-editor/editor.component.tsx

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ type Props = React.HTMLAttributes<HTMLDivElement> & {
1818
textareaId?: string;
1919
textareaClassName?: string;
2020
autoFocus?: boolean;
21+
focusOnWindowFocus?: boolean;
2122
disabled?: boolean;
2223
form?: string;
2324
maxLength?: number;
@@ -87,6 +88,21 @@ export class EditorComponent extends React.Component<Props, State> {
8788

8889
componentDidMount() {
8990
this._recordCurrentState();
91+
if (this.props.focusOnWindowFocus) {
92+
window.addEventListener("focus", this._focusInput);
93+
}
94+
}
95+
96+
componentDidUpdate(prevProps: Readonly<Props>): void {
97+
if (prevProps.focusOnWindowFocus !== this.props.focusOnWindowFocus) {
98+
this.props.focusOnWindowFocus
99+
? window.addEventListener("focus", this._focusInput)
100+
: window.removeEventListener("focus", this._focusInput);
101+
}
102+
}
103+
104+
componentWillUnmount() {
105+
window.removeEventListener("focus", this._focusInput);
90106
}
91107

92108
private _recordCurrentState = () => {
@@ -161,6 +177,13 @@ export class EditorComponent extends React.Component<Props, State> {
161177
this._history.offset++;
162178
};
163179

180+
private _focusInput = () => {
181+
const input = this._input;
182+
183+
if (!input) return;
184+
input.focus();
185+
};
186+
164187
private _updateInput = (record: Record) => {
165188
const input = this._input;
166189

@@ -466,7 +489,7 @@ export class EditorComponent extends React.Component<Props, State> {
466489
selectionStart,
467490
selectionEnd,
468491
},
469-
true,
492+
true
470493
);
471494

472495
this.props.onValueChange(value);
@@ -516,6 +539,7 @@ export class EditorComponent extends React.Component<Props, State> {
516539
insertSpaces,
517540
ignoreTabKey,
518541
preClassName,
542+
focusOnWindowFocus,
519543
...rest
520544
} = this.props;
521545

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import React from "react";
2+
import { ProgressBar } from "react-aria-components";
3+
import styles from "./spinner.module.scss";
4+
5+
export const Spinner = () => {
6+
return (
7+
<ProgressBar aria-label="Loading…" isIndeterminate>
8+
{() => (
9+
<div className={styles.circular__spinner}>
10+
<svg className={styles.spinner__svg} viewBox="0 0 20 20">
11+
<circle
12+
className={styles.spinner__circle}
13+
cx="10"
14+
cy="10"
15+
r="8"
16+
fill="none"
17+
strokeWidth="4"
18+
/>
19+
</svg>
20+
</div>
21+
)}
22+
</ProgressBar>
23+
);
24+
};
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
.circular__spinner {
2+
width: 16px;
3+
height: 16px;
4+
display: inline-block;
5+
}
6+
7+
.spinner__svg {
8+
animation: rotate 1s linear infinite;
9+
width: 100%;
10+
height: 100%;
11+
}
12+
13+
.spinner__circle {
14+
stroke: var(--color_fg_default);
15+
stroke-dasharray: 90 150;
16+
stroke-dashoffset: 0;
17+
stroke-linecap: round;
18+
animation: dash 1.5s ease-in-out infinite;
19+
}
20+
21+
@keyframes rotate {
22+
100% {
23+
transform: rotate(360deg);
24+
}
25+
}
26+
27+
@keyframes dash {
28+
0% {
29+
stroke-dasharray: 1, 200;
30+
stroke-dashoffset: 0;
31+
}
32+
50% {
33+
stroke-dasharray: 100, 200;
34+
stroke-dashoffset: -15;
35+
}
36+
100% {
37+
stroke-dasharray: 1, 200;
38+
stroke-dashoffset: -126;
39+
}
40+
}

src/features/debugger/components/debugger-toolbar/debugger-toolbar.component.tsx

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React from "react";
1+
import React, { useEffect, useRef } from "react";
22
import styles from "./debugger-toolbar.module.scss";
33
import { BoxComponent } from "@/features/common/components/box/box.component";
44
import { useDebuggerStore } from "@/features/debugger/services/debugger.store";
@@ -18,9 +18,22 @@ interface DebuggerToolbarComponentProps {
1818
export const DebuggerToolbarComponent: React.FC<
1919
DebuggerToolbarComponentProps
2020
> = ({ decoderDictionary, encoderDictionary, mode }) => {
21+
const tabRefs = useRef<Array<HTMLLIElement | null>>([]);
2122
const activeWidget$ = useDebuggerStore((state) => state.activeWidget$);
22-
2323
const setActiveWidget$ = useDebuggerStore((state) => state.setActiveWidget$);
24+
const isDecoder = activeWidget$ === DebuggerWidgetValues.DECODER;
25+
26+
const handleKeyDown = (e: React.KeyboardEvent<HTMLLIElement>) => {
27+
const { key } = e;
28+
29+
if (key == "ArrowRight" || key == "ArrowLeft") {
30+
setActiveWidget$(
31+
isDecoder ? DebuggerWidgetValues.ENCODER : DebuggerWidgetValues.DECODER
32+
);
33+
e.preventDefault();
34+
}
35+
tabRefs.current[isDecoder ? 0 : 1]?.focus();
36+
};
2437

2538
if (mode === DebuggerModeValues.UNIFIED) {
2639
return (
@@ -40,8 +53,15 @@ export const DebuggerToolbarComponent: React.FC<
4053
onClick={() => {
4154
setActiveWidget$(DebuggerWidgetValues.DECODER);
4255
}}
56+
onKeyDown={handleKeyDown}
4357
data-active={activeWidget$ === DebuggerWidgetValues.DECODER}
4458
data-testid={dataTestidDictionary.debugger.decoderTab.id}
59+
aria-selected={activeWidget$ === DebuggerWidgetValues.DECODER}
60+
aria-controls={`${DebuggerWidgetValues.DECODER}-panel`}
61+
ref={(el) => {
62+
tabRefs.current[0] = el;
63+
}}
64+
tabIndex={activeWidget$ === DebuggerWidgetValues.DECODER ? 0 : -1}
4565
>
4666
<span className={styles.titleTab__compactLabel}>
4767
{decoderDictionary.compactTitle}
@@ -53,11 +73,18 @@ export const DebuggerToolbarComponent: React.FC<
5373
<li
5474
role="tab"
5575
className={styles.titleTab}
76+
onKeyDown={handleKeyDown}
5677
onClick={() => {
5778
setActiveWidget$(DebuggerWidgetValues.ENCODER);
5879
}}
5980
data-active={activeWidget$ === DebuggerWidgetValues.ENCODER}
6081
data-testid={dataTestidDictionary.debugger.encoderTab.id}
82+
aria-selected={activeWidget$ === DebuggerWidgetValues.ENCODER}
83+
aria-controls={`${DebuggerWidgetValues.ENCODER}-panel`}
84+
ref={(el) => {
85+
tabRefs.current[1] = el;
86+
}}
87+
tabIndex={activeWidget$ === DebuggerWidgetValues.ENCODER ? 0 : -1}
6188
>
6289
<span className={styles.titleTab__compactLabel}>
6390
{encoderDictionary.compactTitle}

0 commit comments

Comments
 (0)