Skip to content

Commit

Permalink
Feature/update UI (#10)
Browse files Browse the repository at this point in the history
* v1.0.3

* 色の調整

* refactor

* jest-chrome uninstall

* css分割

* icon導入

* refactor

* fix

* wasm import

* fix test

* npm i --force削除

* update gif

---------

Co-authored-by: nusuke <[email protected]>
  • Loading branch information
nusuke and nusuke authored Jul 6, 2024
1 parent 0f6c1f1 commit d028967
Show file tree
Hide file tree
Showing 19 changed files with 2,670 additions and 361 deletions.
3 changes: 1 addition & 2 deletions .github/workflows/development.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,9 @@ jobs:
with:
node-version: ${{ matrix.node-version }}

# jest-chromeの強制installのため、force
- name: Build
run: |
npm ci --force
npm ci
npm run build
- name: Test
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ Chrome Web Store: https://chromewebstore.google.com/detail/json-viewer-+jq/jiepg
# 開発環境用意

```
npm ci --force
npm ci
npm run build:debug
```

Expand Down
Binary file modified document/introduction.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 5 additions & 1 deletion jest.setup.js
Original file line number Diff line number Diff line change
@@ -1 +1,5 @@
Object.assign(global, require("jest-chrome"));
Object.assign(global, {
chrome: {
runtime: { sendMessage: () => {}, onMessage: { addListener: () => {} } },
},
});
2 changes: 1 addition & 1 deletion manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"manifest_version": 3,
"name": "JSON viewer +jq",
"description": "You can also share your jq queries by URL. Recommended for large JSON viewers. Narrow it down for easier viewing.",
"version": "1.0.3",
"version": "1.0.4",
"icons": {
"16": "logo16.png",
"48": "logo48.png",
Expand Down
2,602 changes: 2,363 additions & 239 deletions package-lock.json

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@
"build": "webpack --mode production",
"test": "jest"
},
"author": "",
"author": "nusuke",
"license": "MIT",
"devDependencies": {
"@svgr/webpack": "^8.1.0",
"@types/chrome": "^0.0.261",
"@types/jest": "^29.5.12",
"@types/react": "^18.2.58",
Expand All @@ -20,7 +21,6 @@
"crypto-browserify": "^3.12.0",
"css-loader": "^6.10.0",
"jest": "^29.7.0",
"jest-chrome": "^0.8.0",
"mini-css-extract-plugin": "^2.8.0",
"react-test-renderer": "^18.2.0",
"sass": "^1.71.1",
Expand Down
58 changes: 58 additions & 0 deletions src/app/feature/jq/historyHooks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { useCallback, useEffect, useState } from "react";
import {
getHistory,
remoevHistoryAll,
removeHistory,
} from "../../../lib/queryHistoryFromLocalStrage";

export const useQueryHistory = (setJqQuery: any) => {
const [jqQueryHistories, setJqQueryHistory] = useState<string[]>([]);
const [historyKeyIndex, setHistoryKeyIndex] = useState(-1);
const [suggestMode, setSuggestMode] = useState(false);
const [selectedHistoryQuery, setSelectedHisotoryQuery] = useState<
string | null
>(null);

// ↑↓で履歴をinput要素にセットする
useEffect(() => {
if (historyKeyIndex >= 0 && jqQueryHistories.length > historyKeyIndex) {
setSelectedHisotoryQuery(jqQueryHistories[historyKeyIndex]);
}
}, [historyKeyIndex]);

const updateHistoryFromLocalStrage = useCallback(async () => {
const res = await getHistory();
setJqQueryHistory(res ?? []);
}, []);

// 履歴を全て消す
const allRemoveHisotryHandler = useCallback(async () => {
if (confirm(`Delete all`)) {
await remoevHistoryAll();
window.location.reload();
}
}, []);

const removeHisotryHandler = (queryHistory: string) => {
if (confirm(`Can I delete the query "${queryHistory}"?`)) {
removeHistory(queryHistory, jqQueryHistories);
window.location.reload();
}
};

const execQueryHistory = (query?: string) => {
setJqQuery(query ?? selectedHistoryQuery);
};

return {
updateHistoryFromLocalStrage,
setSuggestMode,
setHistoryKeyIndex,
suggestMode,
jqQueryHistories,
allRemoveHisotryHandler,
removeHisotryHandler,
execQueryHistory,
selectedHistoryQuery,
};
};
116 changes: 66 additions & 50 deletions src/app/feature/jq/queryInput.tsx
Original file line number Diff line number Diff line change
@@ -1,28 +1,32 @@
import { useEffect, useRef, useState } from "react";
import {
getHistory,
remoevHistoryAll,
removeHistory,
} from "../../../lib/queryHistoryFromLocalStrage";
import { useCallback, useEffect, useRef, useState } from "react";
import { useQueryHistory } from "./historyHooks";
import DeleteIcon from "../../../icons/delete.svg";
import HistoryIcon from "../../../icons/history.svg";
import { messageType } from "../../../background";

type P = {
initialJqQuery: string;
};
export const QueryInput: React.FC<P> = (props) => {
const [jqQuery, setJqQuery] = useState<string>(props.initialJqQuery);
const [jqQueryHistories, setJqQueryHistory] = useState<string[]>([]);
const [historyKeyIndex, setHistoryKeyIndex] = useState(-1);
const [suggestMode, setSuggestMode] = useState(false);
const queryInputSuggestRef = useRef<HTMLButtonElement>(null);

const updateHistoryFromLocalStrage = async () => {
const res = await getHistory();
setJqQueryHistory(res ?? []);
};
const {
updateHistoryFromLocalStrage,
setSuggestMode,
setHistoryKeyIndex,
suggestMode,
jqQueryHistories,
allRemoveHisotryHandler,
removeHisotryHandler,
execQueryHistory,
selectedHistoryQuery,
} = useQueryHistory(setJqQuery);

const queryInputSuggestRef = useRef<HTMLButtonElement>(null);

useEffect(() => {
chrome.runtime.sendMessage({
type: "query",
type: messageType.query,
text: props.initialJqQuery,
});

Expand All @@ -33,29 +37,37 @@ export const QueryInput: React.FC<P> = (props) => {
setSuggestMode(false);
}, [props.initialJqQuery]);

// ↑↓で履歴をinput要素にセットする
useEffect(() => {
if (historyKeyIndex >= 0 && jqQueryHistories.length > historyKeyIndex) {
setJqQuery(jqQueryHistories[historyKeyIndex]);
}
}, [historyKeyIndex]);

// Enterや送信ボタンでjq発火
// jq発火
const executeJq = async (jqQuery: string) => {
setSuggestMode(false);
await chrome.runtime.sendMessage({
type: "query",
type: messageType.query,
text: jqQuery,
});

if (jqQuery) {
updateHistoryFromLocalStrage();
}
setJqQuery(jqQuery);
};

// 入力されるたびに実行
useEffect(() => {
executeJq(jqQuery);
}, [jqQuery]);

const shareHandler = useCallback(() => {
executeJq(jqQuery);

const url = new URL(document.URL);
url.searchParams.set("chromeExtentionJqQuery", jqQuery);
history.pushState(null, "", url.toString());
};

chrome.runtime.sendMessage({
type: messageType.setHistory,
text: jqQuery,
});
}, [jqQuery]);

return (
<form
Expand All @@ -77,8 +89,9 @@ export const QueryInput: React.FC<P> = (props) => {
}}
onKeyDown={(e) => {
if (e.key === "Enter") {
suggestMode ? execQueryHistory() : executeJq(jqQuery);

setSuggestMode(false);
executeJq(jqQuery);
e.preventDefault();
e.stopPropagation();
}
Expand All @@ -104,19 +117,28 @@ export const QueryInput: React.FC<P> = (props) => {
}}
value={jqQuery}
/>
<button
className="queryInputDeleteButton"
onClick={() => {
executeJq(".");
}}
aria-label="reset input"
>
<DeleteIcon />
</button>
</div>
<div className="queryInputButtonArea">
<button className="queryInputShareButton" onClick={shareHandler}>
Set URL
</button>
<button
className="queryInputHistoryButton"
onClick={() => setSuggestMode((s) => !s)}
aria-label="history"
>
{suggestMode ? "×" : "history"}
{suggestMode ? "×" : <HistoryIcon />}
</button>
</div>
<button
className="queryInputShareButton"
onClick={() => executeJq(jqQuery)}
>
Set URL
</button>
</div>

{suggestMode && jqQueryHistories.length > 0 && (
Expand All @@ -125,23 +147,22 @@ export const QueryInput: React.FC<P> = (props) => {
<li key={queryHistory} className="queryInputSuggestList">
<button
className={`${
queryHistory === jqQuery ? "queryInputSuggest--active" : ""
queryHistory === selectedHistoryQuery
? "queryInputSuggest--active"
: ""
} queryInputSuggestButton`}
onClick={() => {
executeJq(queryHistory);
}}
ref={queryHistory === jqQuery ? queryInputSuggestRef : null}
onClick={() => execQueryHistory(queryHistory)}
ref={
queryHistory === selectedHistoryQuery
? queryInputSuggestRef
: null
}
>
{queryHistory}
</button>
<button
className="queryInputSuggestRemoveButton"
onClick={() => {
if (confirm(`Can I delete the query "${queryHistory}"?`)) {
removeHistory(queryHistory, jqQueryHistories);
window.location.reload();
}
}}
onClick={() => removeHisotryHandler(queryHistory)}
>
×
</button>
Expand All @@ -150,12 +171,7 @@ export const QueryInput: React.FC<P> = (props) => {
<li>
<button
className="queryInputSuggestAllRemoveButton"
onClick={async () => {
if (confirm(`Delete all`)) {
await remoevHistoryAll();
window.location.reload();
}
}}
onClick={allRemoveHisotryHandler}
>
all remove.
</button>
Expand Down
29 changes: 28 additions & 1 deletion src/app/feature/jsonPreview/convertJSONToHTML.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,28 @@ import { getSurroundCharactor, surroundParentheses } from "./parentheses";
export function convertJSONToHTML(json: JSON) {
const nodes = [];

for (const [key, value] of Object.entries(json)) {
// booleanなどが単一で入ってきた際
const jsonArray = Object.entries(json);
if (jsonArray.length === 0) {
nodes.push(
<div className="jsonRow">
<span className="jsonValue">{convertJsonValueToHTML(json)}</span>
</div>
);

return nodes;
}
for (const [key, value] of jsonArray) {
// keyがfalsyの場合
if (!key) {
nodes.push(
<div className="jsonRow">
<span className="jsonValue">{convertJsonValueToHTML(value)}</span>
</div>
);
return nodes;
}

if (typeof value === "object") {
const surruondChar = getSurroundCharactor(value);
nodes.push(
Expand Down Expand Up @@ -40,6 +61,12 @@ export function convertJSONToHTML(json: JSON) {
function convertJsonValueToHTML(value: unknown) {
if (typeof value === "number") {
return <span className="jsonValue--number">{value}</span>;
} else if (typeof value === "boolean") {
return (
<span className="jsonValue--boolean">{value ? "true" : "false"}</span>
);
} else if (typeof value === "undefined") {
return <span className="jsonValue--undefined">{value}</span>;
} else if (typeof value === "string") {
if (URL.canParse(value)) {
return (
Expand Down
9 changes: 6 additions & 3 deletions src/app/feature/jsonPreview/jsonPreview.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,23 @@
import { useEffect } from "react";
import { convertJSONToHTML } from "./convertJSONToHTML";
import { getSurroundCharactor, surroundParentheses } from "./parentheses";
import { messageType } from "../../../background";

type P = { targetJson: JSON };
export const JsonPreview: React.FC<P> = (props: P) => {
useEffect(() => {
chrome.runtime.sendMessage({
type: "road",
type: messageType.load,
text: JSON.stringify(props.targetJson),
});
}, []);
const surruondChar = getSurroundCharactor(props.targetJson);

return (
<div className="jsonPreview">
{surroundParentheses(convertJSONToHTML(props.targetJson), surruondChar)}
{surroundParentheses(
convertJSONToHTML(props.targetJson),
getSurroundCharactor(props.targetJson)
)}
</div>
);
};
Loading

0 comments on commit d028967

Please sign in to comment.