mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-04-22 16:58:28 +02:00
Add React hooks eslint
This commit is contained in:
@@ -2,6 +2,7 @@ module.exports = {
|
|||||||
extends: [
|
extends: [
|
||||||
"eslint:recommended",
|
"eslint:recommended",
|
||||||
"plugin:react/recommended",
|
"plugin:react/recommended",
|
||||||
|
"plugin:react-hooks/recommended",
|
||||||
"plugin:import/recommended",
|
"plugin:import/recommended",
|
||||||
"plugin:jsx-a11y/recommended",
|
"plugin:jsx-a11y/recommended",
|
||||||
"plugin:@typescript-eslint/recommended",
|
"plugin:@typescript-eslint/recommended",
|
||||||
|
|||||||
20
package-lock.json
generated
20
package-lock.json
generated
@@ -59,6 +59,7 @@
|
|||||||
"eslint-plugin-import": "^2.27.5",
|
"eslint-plugin-import": "^2.27.5",
|
||||||
"eslint-plugin-jsx-a11y": "^6.7.1",
|
"eslint-plugin-jsx-a11y": "^6.7.1",
|
||||||
"eslint-plugin-react": "^7.32.2",
|
"eslint-plugin-react": "^7.32.2",
|
||||||
|
"eslint-plugin-react-hooks": "^4.6.0",
|
||||||
"postcss": "^8.4.21",
|
"postcss": "^8.4.21",
|
||||||
"postcss-nesting": "^11.2.1",
|
"postcss-nesting": "^11.2.1",
|
||||||
"prettier": "^2.8.4",
|
"prettier": "^2.8.4",
|
||||||
@@ -3823,6 +3824,18 @@
|
|||||||
"eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8"
|
"eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/eslint-plugin-react-hooks": {
|
||||||
|
"version": "4.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz",
|
||||||
|
"integrity": "sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/eslint-plugin-react/node_modules/doctrine": {
|
"node_modules/eslint-plugin-react/node_modules/doctrine": {
|
||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
|
||||||
@@ -10191,6 +10204,13 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"eslint-plugin-react-hooks": {
|
||||||
|
"version": "4.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz",
|
||||||
|
"integrity": "sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {}
|
||||||
|
},
|
||||||
"eslint-scope": {
|
"eslint-scope": {
|
||||||
"version": "5.1.1",
|
"version": "5.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
|
||||||
|
|||||||
@@ -66,6 +66,7 @@
|
|||||||
"eslint-plugin-import": "^2.27.5",
|
"eslint-plugin-import": "^2.27.5",
|
||||||
"eslint-plugin-jsx-a11y": "^6.7.1",
|
"eslint-plugin-jsx-a11y": "^6.7.1",
|
||||||
"eslint-plugin-react": "^7.32.2",
|
"eslint-plugin-react": "^7.32.2",
|
||||||
|
"eslint-plugin-react-hooks": "^4.6.0",
|
||||||
"postcss": "^8.4.21",
|
"postcss": "^8.4.21",
|
||||||
"postcss-nesting": "^11.2.1",
|
"postcss-nesting": "^11.2.1",
|
||||||
"prettier": "^2.8.4",
|
"prettier": "^2.8.4",
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ export function GraphQLEditor({ defaultValue, onChange, baseRequest, ...extraEdi
|
|||||||
|
|
||||||
const handleChangeQuery = useCallback(
|
const handleChangeQuery = useCallback(
|
||||||
(query: string) => handleChange({ query, variables }),
|
(query: string) => handleChange({ query, variables }),
|
||||||
[handleChange],
|
[handleChange, variables],
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleChangeVariables = useCallback(
|
const handleChangeVariables = useCallback(
|
||||||
@@ -54,11 +54,12 @@ export function GraphQLEditor({ defaultValue, onChange, baseRequest, ...extraEdi
|
|||||||
// Meh, not much we can do here
|
// Meh, not much we can do here
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[handleChange],
|
[handleChange, query],
|
||||||
);
|
);
|
||||||
|
|
||||||
const editorViewRef = useRef<EditorView>(null);
|
const editorViewRef = useRef<EditorView>(null);
|
||||||
|
|
||||||
|
// Refetch the schema when the URL changes
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let unmounted = false;
|
let unmounted = false;
|
||||||
const body = JSON.stringify({
|
const body = JSON.stringify({
|
||||||
@@ -67,43 +68,44 @@ export function GraphQLEditor({ defaultValue, onChange, baseRequest, ...extraEdi
|
|||||||
});
|
});
|
||||||
sendEphemeralRequest({ ...baseRequest, body }).then((response) => {
|
sendEphemeralRequest({ ...baseRequest, body }).then((response) => {
|
||||||
if (unmounted) return;
|
if (unmounted) return;
|
||||||
|
if (!editorViewRef.current) return;
|
||||||
try {
|
try {
|
||||||
if (editorViewRef.current) {
|
const { data } = JSON.parse(response.body);
|
||||||
const { data } = JSON.parse(response.body);
|
const schema = buildClientSchema(data);
|
||||||
const schema = buildClientSchema(data);
|
console.log('SET SCHEMA', schema, baseRequest.url);
|
||||||
updateSchema(editorViewRef.current, schema);
|
updateSchema(editorViewRef.current, schema);
|
||||||
}
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log('Failed to parse introspection query', err);
|
console.log('Failed to parse introspection query', err);
|
||||||
|
updateSchema(editorViewRef.current, undefined);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return () => {
|
return () => {
|
||||||
unmounted = true;
|
unmounted = true;
|
||||||
};
|
};
|
||||||
}, [baseRequest.url]);
|
}, [baseRequest, baseRequest.url]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="pb-2 h-full grid grid-rows-[minmax(0,100%)_auto_auto_minmax(0,auto)]">
|
<div className="pb-2 h-full grid grid-rows-[minmax(0,100%)_auto_auto_minmax(0,auto)]">
|
||||||
<Editor
|
<Editor
|
||||||
ref={editorViewRef}
|
|
||||||
heightMode="auto"
|
|
||||||
defaultValue={query ?? ''}
|
|
||||||
onChange={handleChangeQuery}
|
|
||||||
contentType="application/graphql"
|
contentType="application/graphql"
|
||||||
placeholder="..."
|
defaultValue={query ?? ''}
|
||||||
format={formatGraphQL}
|
format={formatGraphQL}
|
||||||
|
heightMode="auto"
|
||||||
|
onChange={handleChangeQuery}
|
||||||
|
placeholder="..."
|
||||||
|
ref={editorViewRef}
|
||||||
{...extraEditorProps}
|
{...extraEditorProps}
|
||||||
/>
|
/>
|
||||||
<Separator variant="primary" />
|
<Separator variant="primary" />
|
||||||
<p className="pt-1 text-gray-500 text-sm">Variables</p>
|
<p className="pt-1 text-gray-500 text-sm">Variables</p>
|
||||||
<Editor
|
<Editor
|
||||||
useTemplating
|
|
||||||
heightMode="auto"
|
|
||||||
placeholder="{}"
|
|
||||||
defaultValue={JSON.stringify(variables, null, 2)}
|
|
||||||
onChange={handleChangeVariables}
|
|
||||||
contentType="application/json"
|
contentType="application/json"
|
||||||
|
defaultValue={JSON.stringify(variables, null, 2)}
|
||||||
|
heightMode="auto"
|
||||||
|
onChange={handleChangeVariables}
|
||||||
|
placeholder="{}"
|
||||||
|
useTemplating
|
||||||
{...extraEditorProps}
|
{...extraEditorProps}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -116,18 +116,16 @@ export const RequestPane = memo(function RequestPane({ style, fullHeight, classN
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
[
|
[activeRequest, updateRequest],
|
||||||
activeRequest?.bodyType,
|
|
||||||
activeRequest?.headers,
|
|
||||||
activeRequest?.authenticationType,
|
|
||||||
activeRequest?.authentication,
|
|
||||||
],
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleBodyChange = useCallback((body: string) => updateRequest.mutate({ body }), []);
|
const handleBodyChange = useCallback(
|
||||||
|
(body: string) => updateRequest.mutate({ body }),
|
||||||
|
[updateRequest],
|
||||||
|
);
|
||||||
const handleHeadersChange = useCallback(
|
const handleHeadersChange = useCallback(
|
||||||
(headers: HttpHeader[]) => updateRequest.mutate({ headers }),
|
(headers: HttpHeader[]) => updateRequest.mutate({ headers }),
|
||||||
[],
|
[updateRequest],
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ export const RequestResponse = memo(function RequestResponse({ style }: Props) {
|
|||||||
|
|
||||||
const handleReset = useCallback(
|
const handleReset = useCallback(
|
||||||
() => (vertical ? heightKv.set(DEFAULT) : widthKv.set(DEFAULT)),
|
() => (vertical ? heightKv.set(DEFAULT) : widthKv.set(DEFAULT)),
|
||||||
[vertical],
|
[heightKv, vertical, widthKv],
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleResizeStart = useCallback(
|
const handleResizeStart = useCallback(
|
||||||
@@ -110,7 +110,7 @@ export const RequestResponse = memo(function RequestResponse({ style }: Props) {
|
|||||||
document.documentElement.addEventListener('mouseup', moveState.current.up);
|
document.documentElement.addEventListener('mouseup', moveState.current.up);
|
||||||
setIsResizing(true);
|
setIsResizing(true);
|
||||||
},
|
},
|
||||||
[widthKv.value, heightKv.value, vertical],
|
[width, height, vertical, heightKv, widthKv],
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ function SidebarItems({
|
|||||||
updateRequest.mutate({ id: requestId, update });
|
updateRequest.mutate({ id: requestId, update });
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[hoveredIndex, requests],
|
[hoveredIndex, requests, updateRequest],
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -139,10 +139,13 @@ const _SidebarItem = forwardRef(function SidebarItem(
|
|||||||
const updateRequest = useUpdateRequest(requestId);
|
const updateRequest = useUpdateRequest(requestId);
|
||||||
const [editing, setEditing] = useState<boolean>(false);
|
const [editing, setEditing] = useState<boolean>(false);
|
||||||
|
|
||||||
const handleSubmitNameEdit = useCallback(async (el: HTMLInputElement) => {
|
const handleSubmitNameEdit = useCallback(
|
||||||
await updateRequest.mutate((r) => ({ ...r, name: el.value }));
|
async (el: HTMLInputElement) => {
|
||||||
setEditing(false);
|
await updateRequest.mutate((r) => ({ ...r, name: el.value }));
|
||||||
}, []);
|
setEditing(false);
|
||||||
|
},
|
||||||
|
[updateRequest],
|
||||||
|
);
|
||||||
|
|
||||||
const handleFocus = useCallback((el: HTMLInputElement | null) => {
|
const handleFocus = useCallback((el: HTMLInputElement | null) => {
|
||||||
el?.focus();
|
el?.focus();
|
||||||
@@ -171,7 +174,7 @@ const _SidebarItem = forwardRef(function SidebarItem(
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[active],
|
[handleSubmitNameEdit],
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ export const SidebarActions = memo(function SidebarDisplayToggle() {
|
|||||||
const createRequest = useCreateRequest({ navigateAfter: true });
|
const createRequest = useCreateRequest({ navigateAfter: true });
|
||||||
const handleCreateRequest = useCallback(() => {
|
const handleCreateRequest = useCallback(() => {
|
||||||
createRequest.mutate({ name: 'New Request' });
|
createRequest.mutate({ name: 'New Request' });
|
||||||
}, []);
|
}, [createRequest]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@@ -17,8 +17,14 @@ type Props = Pick<HttpRequest, 'id' | 'url' | 'method'> & {
|
|||||||
export const UrlBar = memo(function UrlBar({ id: requestId, url, method, className }: Props) {
|
export const UrlBar = memo(function UrlBar({ id: requestId, url, method, className }: Props) {
|
||||||
const sendRequest = useSendRequest(requestId);
|
const sendRequest = useSendRequest(requestId);
|
||||||
const updateRequest = useUpdateRequest(requestId);
|
const updateRequest = useUpdateRequest(requestId);
|
||||||
const handleMethodChange = useCallback((method: string) => updateRequest.mutate({ method }), []);
|
const handleMethodChange = useCallback(
|
||||||
const handleUrlChange = useCallback((url: string) => updateRequest.mutate({ url }), []);
|
(method: string) => updateRequest.mutate({ method }),
|
||||||
|
[updateRequest],
|
||||||
|
);
|
||||||
|
const handleUrlChange = useCallback(
|
||||||
|
(url: string) => updateRequest.mutate({ url }),
|
||||||
|
[updateRequest],
|
||||||
|
);
|
||||||
const loading = useIsResponseLoading(requestId);
|
const loading = useIsResponseLoading(requestId);
|
||||||
const { updateKey } = useRequestUpdateKey(requestId);
|
const { updateKey } = useRequestUpdateKey(requestId);
|
||||||
|
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ export default function Workspace() {
|
|||||||
setFloating(false);
|
setFloating(false);
|
||||||
sidebar.show();
|
sidebar.show();
|
||||||
}
|
}
|
||||||
}, [windowSize.width]);
|
}, [sidebar, windowSize.width]);
|
||||||
|
|
||||||
const unsub = () => {
|
const unsub = () => {
|
||||||
if (moveState.current !== null) {
|
if (moveState.current !== null) {
|
||||||
@@ -73,7 +73,7 @@ export default function Workspace() {
|
|||||||
document.documentElement.addEventListener('mouseup', moveState.current.up);
|
document.documentElement.addEventListener('mouseup', moveState.current.up);
|
||||||
setIsResizing(true);
|
setIsResizing(true);
|
||||||
},
|
},
|
||||||
[sidebar.width, sidebar.hidden],
|
[sidebar],
|
||||||
);
|
);
|
||||||
|
|
||||||
const sideWidth = sidebar.hidden ? 0 : sidebar.width;
|
const sideWidth = sidebar.hidden ? 0 : sidebar.width;
|
||||||
|
|||||||
@@ -65,7 +65,15 @@ export const WorkspaceActionsDropdown = memo(function WorkspaceDropdown({ classN
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
}, [workspaces, activeWorkspaceId]);
|
}, [
|
||||||
|
workspaces,
|
||||||
|
activeWorkspaceId,
|
||||||
|
routes,
|
||||||
|
createWorkspace,
|
||||||
|
confirm,
|
||||||
|
activeWorkspace?.name,
|
||||||
|
deleteWorkspace,
|
||||||
|
]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dropdown items={items}>
|
<Dropdown items={items}>
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ export function Dropdown({ children, items }: DropdownProps) {
|
|||||||
const handleClose = useCallback(() => {
|
const handleClose = useCallback(() => {
|
||||||
setOpen(false);
|
setOpen(false);
|
||||||
ref.current?.focus();
|
ref.current?.focus();
|
||||||
}, [ref.current]);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
ref.current?.setAttribute('aria-expanded', open.toString());
|
ref.current?.setAttribute('aria-expanded', open.toString());
|
||||||
@@ -63,7 +63,7 @@ export function Dropdown({ children, items }: DropdownProps) {
|
|||||||
const triggerRect = useMemo(() => {
|
const triggerRect = useMemo(() => {
|
||||||
if (!open) return null;
|
if (!open) return null;
|
||||||
return ref.current?.getBoundingClientRect();
|
return ref.current?.getBoundingClientRect();
|
||||||
}, [ref.current, open]);
|
}, [open]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -83,8 +83,6 @@ interface MenuProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function Menu({ className, items, onClose, triggerRect }: MenuProps) {
|
function Menu({ className, items, onClose, triggerRect }: MenuProps) {
|
||||||
if (triggerRect === undefined) return null;
|
|
||||||
|
|
||||||
const containerRef = useRef<HTMLDivElement | null>(null);
|
const containerRef = useRef<HTMLDivElement | null>(null);
|
||||||
const [menuStyles, setMenuStyles] = useState<CSSProperties>({});
|
const [menuStyles, setMenuStyles] = useState<CSSProperties>({});
|
||||||
|
|
||||||
|
|||||||
@@ -88,12 +88,14 @@ const _Editor = forwardRef<EditorView | undefined, EditorProps>(function Editor(
|
|||||||
const { view, languageCompartment } = cm.current;
|
const { view, languageCompartment } = cm.current;
|
||||||
const ext = getLanguageExtension({ contentType, useTemplating, autocomplete });
|
const ext = getLanguageExtension({ contentType, useTemplating, autocomplete });
|
||||||
view.dispatch({ effects: languageCompartment.reconfigure(ext) });
|
view.dispatch({ effects: languageCompartment.reconfigure(ext) });
|
||||||
}, [contentType, autocomplete]);
|
}, [contentType, autocomplete, useTemplating]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (cm.current === null) return;
|
if (cm.current === null) return;
|
||||||
const { view } = cm.current;
|
const { view } = cm.current;
|
||||||
view.dispatch({ changes: { from: 0, to: view.state.doc.length, insert: defaultValue ?? '' } });
|
view.dispatch({ changes: { from: 0, to: view.state.doc.length, insert: defaultValue ?? '' } });
|
||||||
|
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [forceUpdateKey]);
|
}, [forceUpdateKey]);
|
||||||
|
|
||||||
// Initialize the editor when ref mounts
|
// Initialize the editor when ref mounts
|
||||||
@@ -133,6 +135,8 @@ const _Editor = forwardRef<EditorView | undefined, EditorProps>(function Editor(
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log('Failed to initialize Codemirror', e);
|
console.log('Failed to initialize Codemirror', e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const cmContainer = (
|
const cmContainer = (
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ const _IconButton = forwardRef<HTMLButtonElement, Props>(function IconButton(
|
|||||||
if (showConfirm) setConfirmed();
|
if (showConfirm) setConfirmed();
|
||||||
onClick?.(e);
|
onClick?.(e);
|
||||||
},
|
},
|
||||||
[onClick],
|
[onClick, setConfirmed, showConfirm],
|
||||||
);
|
);
|
||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
@@ -63,10 +63,13 @@ export function Input({
|
|||||||
return true;
|
return true;
|
||||||
}, [currentValue, validate, require]);
|
}, [currentValue, validate, require]);
|
||||||
|
|
||||||
const handleChange = useCallback((value: string) => {
|
const handleChange = useCallback(
|
||||||
setCurrentValue(value);
|
(value: string) => {
|
||||||
onChange?.(value);
|
setCurrentValue(value);
|
||||||
}, []);
|
onChange?.(value);
|
||||||
|
},
|
||||||
|
[onChange],
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<VStack className="w-full">
|
<VStack className="w-full">
|
||||||
|
|||||||
@@ -62,6 +62,8 @@ export const PairEditor = memo(function PairEditor({
|
|||||||
const nonEmpty = originalPairs.filter((h) => !(h.name === '' && h.value === ''));
|
const nonEmpty = originalPairs.filter((h) => !(h.name === '' && h.value === ''));
|
||||||
const pairs = nonEmpty.map((pair) => newPairContainer(pair));
|
const pairs = nonEmpty.map((pair) => newPairContainer(pair));
|
||||||
setPairs([...pairs, newPairContainer()]);
|
setPairs([...pairs, newPairContainer()]);
|
||||||
|
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [forceUpdateKey]);
|
}, [forceUpdateKey]);
|
||||||
|
|
||||||
const setPairsAndSave = useCallback(
|
const setPairsAndSave = useCallback(
|
||||||
@@ -99,19 +101,19 @@ export const PairEditor = memo(function PairEditor({
|
|||||||
return newPairs;
|
return newPairs;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[hoveredIndex],
|
[hoveredIndex, setPairsAndSave],
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleChange = useCallback(
|
const handleChange = useCallback(
|
||||||
(pair: PairContainer) =>
|
(pair: PairContainer) =>
|
||||||
setPairsAndSave((pairs) => pairs.map((p) => (pair.id !== p.id ? p : pair))),
|
setPairsAndSave((pairs) => pairs.map((p) => (pair.id !== p.id ? p : pair))),
|
||||||
[],
|
[setPairsAndSave],
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleDelete = useCallback(
|
const handleDelete = useCallback(
|
||||||
(pair: PairContainer) =>
|
(pair: PairContainer) =>
|
||||||
setPairsAndSave((oldPairs) => oldPairs.filter((p) => p.id !== pair.id)),
|
setPairsAndSave((oldPairs) => oldPairs.filter((p) => p.id !== pair.id)),
|
||||||
[],
|
[setPairsAndSave],
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleFocus = useCallback(
|
const handleFocus = useCallback(
|
||||||
@@ -216,17 +218,17 @@ const FormRow = memo(function FormRow({
|
|||||||
|
|
||||||
const handleChangeEnabled = useMemo(
|
const handleChangeEnabled = useMemo(
|
||||||
() => (enabled: boolean) => onChange({ id, pair: { ...pairContainer.pair, enabled } }),
|
() => (enabled: boolean) => onChange({ id, pair: { ...pairContainer.pair, enabled } }),
|
||||||
[onChange, pairContainer.pair.name, pairContainer.pair.value],
|
[id, onChange, pairContainer.pair],
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleChangeName = useMemo(
|
const handleChangeName = useMemo(
|
||||||
() => (name: string) => onChange({ id, pair: { ...pairContainer.pair, name } }),
|
() => (name: string) => onChange({ id, pair: { ...pairContainer.pair, name } }),
|
||||||
[onChange, pairContainer.pair.value, pairContainer.pair.enabled],
|
[onChange, id, pairContainer.pair],
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleChangeValue = useMemo(
|
const handleChangeValue = useMemo(
|
||||||
() => (value: string) => onChange({ id, pair: { ...pairContainer.pair, value } }),
|
() => (value: string) => onChange({ id, pair: { ...pairContainer.pair, value } }),
|
||||||
[onChange, pairContainer.pair.name, pairContainer.pair.enabled],
|
[onChange, id, pairContainer.pair],
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleFocus = useCallback(() => onFocus?.(pairContainer), [onFocus, pairContainer]);
|
const handleFocus = useCallback(() => onFocus?.(pairContainer), [onFocus, pairContainer]);
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ export function RadioDropdown<T = string | null>({
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
[value, items],
|
[items, value, onChange],
|
||||||
);
|
);
|
||||||
|
|
||||||
return <Dropdown items={dropdownItems}>{children}</Dropdown>;
|
return <Dropdown items={dropdownItems}>{children}</Dropdown>;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import type { ReactNode } from 'react';
|
import type { ReactNode } from 'react';
|
||||||
import { memo, useEffect, useRef } from 'react';
|
import { memo, useCallback, useEffect, useRef } from 'react';
|
||||||
import { Button } from '../Button';
|
import { Button } from '../Button';
|
||||||
import { Icon } from '../Icon';
|
import { Icon } from '../Icon';
|
||||||
import type { RadioDropdownProps } from '../RadioDropdown';
|
import type { RadioDropdownProps } from '../RadioDropdown';
|
||||||
@@ -38,28 +38,31 @@ export function Tabs({
|
|||||||
}: Props) {
|
}: Props) {
|
||||||
const ref = useRef<HTMLDivElement | null>(null);
|
const ref = useRef<HTMLDivElement | null>(null);
|
||||||
|
|
||||||
const handleTabChange = (value: string) => {
|
const handleTabChange = useCallback(
|
||||||
const tabs = ref.current?.querySelectorAll<HTMLDivElement>(`[data-tab]`);
|
(value: string) => {
|
||||||
for (const tab of tabs ?? []) {
|
const tabs = ref.current?.querySelectorAll<HTMLDivElement>(`[data-tab]`);
|
||||||
const v = tab.getAttribute('data-tab');
|
for (const tab of tabs ?? []) {
|
||||||
if (v === value) {
|
const v = tab.getAttribute('data-tab');
|
||||||
tab.setAttribute('tabindex', '-1');
|
if (v === value) {
|
||||||
tab.setAttribute('data-state', 'active');
|
tab.setAttribute('tabindex', '-1');
|
||||||
tab.setAttribute('aria-hidden', 'false');
|
tab.setAttribute('data-state', 'active');
|
||||||
tab.style.display = 'block';
|
tab.setAttribute('aria-hidden', 'false');
|
||||||
} else {
|
tab.style.display = 'block';
|
||||||
tab.setAttribute('data-state', 'inactive');
|
} else {
|
||||||
tab.setAttribute('aria-hidden', 'true');
|
tab.setAttribute('data-state', 'inactive');
|
||||||
tab.style.display = 'none';
|
tab.setAttribute('aria-hidden', 'true');
|
||||||
|
tab.style.display = 'none';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
onChangeValue(value);
|
||||||
onChangeValue(value);
|
},
|
||||||
};
|
[onChangeValue],
|
||||||
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (value === undefined) return;
|
if (value === undefined) return;
|
||||||
handleTabChange(value);
|
handleTabChange(value);
|
||||||
}, [value]);
|
}, [handleTabChange, value]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ export function Webview({ body, url, contentType }: Props) {
|
|||||||
return body.replace(/<head>/gi, `<head><base href="${url}"/>`);
|
return body.replace(/<head>/gi, `<head><base href="${url}"/>`);
|
||||||
}
|
}
|
||||||
return body;
|
return body;
|
||||||
}, [body, contentType]);
|
}, [url, body, contentType]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="px-2 pb-2">
|
<div className="px-2 pb-2">
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
||||||
import { useCallback } from 'react';
|
import { useCallback, useMemo } from 'react';
|
||||||
import { buildKeyValueKey, getKeyValue, setKeyValue } from '../lib/keyValueStore';
|
import { buildKeyValueKey, getKeyValue, setKeyValue } from '../lib/keyValueStore';
|
||||||
|
|
||||||
const DEFAULT_NAMESPACE = 'app';
|
const DEFAULT_NAMESPACE = 'app';
|
||||||
@@ -46,15 +46,18 @@ export function useKeyValue<T extends Object | null>({
|
|||||||
mutate.mutate(value);
|
mutate.mutate(value);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[defaultValue],
|
[defaultValue, key, mutate, namespace],
|
||||||
);
|
);
|
||||||
|
|
||||||
const reset = useCallback(() => mutate.mutate(defaultValue), [defaultValue]);
|
const reset = useCallback(() => mutate.mutate(defaultValue), [mutate, defaultValue]);
|
||||||
|
|
||||||
return {
|
return useMemo(
|
||||||
value: query.data,
|
() => ({
|
||||||
isLoading: query.isLoading,
|
value: query.data,
|
||||||
set,
|
isLoading: query.isLoading,
|
||||||
reset,
|
set,
|
||||||
};
|
reset,
|
||||||
|
}),
|
||||||
|
[query.data, query.isLoading, reset, set],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { useCallback } from 'react';
|
import { useCallback, useMemo } from 'react';
|
||||||
import { NAMESPACE_NO_SYNC } from '../lib/keyValueStore';
|
import { NAMESPACE_NO_SYNC } from '../lib/keyValueStore';
|
||||||
import { useKeyValue } from './useKeyValue';
|
import { useKeyValue } from './useKeyValue';
|
||||||
|
|
||||||
@@ -28,12 +28,15 @@ export function useSidebarDisplay() {
|
|||||||
const hidden = width < COLLAPSE_WIDTH;
|
const hidden = width < COLLAPSE_WIDTH;
|
||||||
display.set({ hidden, width: Math.max(MIN_WIDTH, width) });
|
display.set({ hidden, width: Math.max(MIN_WIDTH, width) });
|
||||||
},
|
},
|
||||||
[display.set],
|
[display],
|
||||||
);
|
);
|
||||||
const hide = useCallback(() => display.set((v) => ({ ...v, hidden: true })), []);
|
const hide = useCallback(() => display.set((v) => ({ ...v, hidden: true })), [display]);
|
||||||
const show = useCallback(() => display.set((v) => ({ ...v, hidden: false })), []);
|
const show = useCallback(() => display.set((v) => ({ ...v, hidden: false })), [display]);
|
||||||
const toggle = useCallback(() => display.set((v) => ({ ...v, hidden: !v.hidden })), []);
|
const toggle = useCallback(() => display.set((v) => ({ ...v, hidden: !v.hidden })), [display]);
|
||||||
const reset = display.reset;
|
const reset = display.reset;
|
||||||
|
|
||||||
return { width, hidden, set, reset, hide, show, toggle };
|
return useMemo(
|
||||||
|
() => ({ width, hidden, set, reset, hide, show, toggle }),
|
||||||
|
[hidden, hide, reset, set, show, toggle, width],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -87,7 +87,9 @@ export function useTauriListeners() {
|
|||||||
: null;
|
: null;
|
||||||
|
|
||||||
if (queryKey === null) {
|
if (queryKey === null) {
|
||||||
console.log('Unrecognized updated model:', payload);
|
if (payload.model) {
|
||||||
|
console.log('Unrecognized updated model:', payload);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -154,5 +156,5 @@ export function useTauriListeners() {
|
|||||||
unsub();
|
unsub();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}, []);
|
}, [queryClient, sidebarDisplay.toggle, wasUpdatedExternally]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,16 +13,12 @@ export function useTheme() {
|
|||||||
defaultValue: getAppearance(),
|
defaultValue: getAppearance(),
|
||||||
});
|
});
|
||||||
|
|
||||||
const themeChange = (appearance: Appearance) => {
|
|
||||||
appearanceKv.set(appearance);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleToggleAppearance = async () => {
|
const handleToggleAppearance = async () => {
|
||||||
appearanceKv.set(appearanceKv.value === 'dark' ? 'light' : 'dark');
|
appearanceKv.set(appearanceKv.value === 'dark' ? 'light' : 'dark');
|
||||||
};
|
};
|
||||||
|
|
||||||
// Set appearance when preferred theme changes
|
// Set appearance when preferred theme changes
|
||||||
useEffect(() => subscribeToPreferredAppearanceChange(themeChange), []);
|
useEffect(() => subscribeToPreferredAppearanceChange(appearanceKv.set), [appearanceKv.set]);
|
||||||
|
|
||||||
// Sync appearance when k/v changes
|
// Sync appearance when k/v changes
|
||||||
useEffect(() => setAppearance(appearanceKv.value), [appearanceKv.value]);
|
useEffect(() => setAppearance(appearanceKv.value), [appearanceKv.value]);
|
||||||
|
|||||||
Reference in New Issue
Block a user