diff --git a/src/components/Base.tsx b/src/components/Base.tsx
index e515835..5e3181e 100644
--- a/src/components/Base.tsx
+++ b/src/components/Base.tsx
@@ -19,14 +19,13 @@ import {
useElementSelectionContext,
} from '../contexts/ElementSelectionContext';
import ElementSelector from './utils/ElementSelector';
-import { setFormatsAndPushToAry } from '../features/formatter';
-import { getAbsoluteCSSSelector } from '../utils/CSSUtils';
import TemplateCustomizer from './tabitems/template/TemplateCustomizer';
import Scrollable from './tabitems/common/Scrollable';
import LayerCustomizer from './tabitems/layer/LayerCustomizer';
import { UIUpdaterContext, useUIUpdater } from '../contexts/UIUpdater';
import t from '../features/translator';
import usePropsContext, { PropsContext } from '../contexts/PropsContext';
+import Controller from './Controller';
const Wrapper = styled.div`
width: 100%;
@@ -69,27 +68,13 @@ const Base = ({}: /* categoryMap */ BaseProps) => {
const elementSelection = useElementSelectionContext();
const updater = useUIUpdater();
const props = usePropsContext();
- const [fontPermissionGranted, setFontPermissionGranted] = useState(true);
-
- const onChange = (key: string, value: string, id: number | string) => {
- if (elementSelection.selectedElement) {
- setFormatsAndPushToAry([
- {
- id,
- cssSelector:
- getAbsoluteCSSSelector(elementSelection.selectedElement) +
- elementSelection.selectedPseudoClass,
- values: [{ key, value }],
- },
- ]);
- updater.formatChanged();
- }
- };
+ const [fontPermissionGranted, setFontPermissionGranted] =
+ useState(true);
const tabs = {
//templates.json: ,
- fonts: ,
- blocks: ,
+ fonts: ,
+ blocks: ,
templates: ,
layers: ,
settings: ,
@@ -121,7 +106,7 @@ const Base = ({}: /* categoryMap */ BaseProps) => {
} catch {
setFontPermissionGranted(false);
}
- }
+ };
useEffect(() => {
(async () => {
@@ -134,20 +119,25 @@ const Base = ({}: /* categoryMap */ BaseProps) => {
+
- { fontPermissionGranted &&
+ {fontPermissionGranted && (
- }
- { !fontPermissionGranted &&
+ )}
+ {!fontPermissionGranted && (
<>
- {t('need_to_grant_for_access_local_font')}
-
+
+ {t('need_to_grant_for_access_local_font')}
+
+
>
- }
+ )}
diff --git a/src/components/ChangeStyleCategory.tsx b/src/components/ChangeStyleCategory.tsx
index 354cc61..b65b0d2 100644
--- a/src/components/ChangeStyleCategory.tsx
+++ b/src/components/ChangeStyleCategory.tsx
@@ -1,137 +1,140 @@
-import styled from 'styled-components';
-import Title from 'antd/lib/typography/Title';
-import React, { useContext } from 'react';
-import { ChangeStyleElement, LayoutPart } from '../types/ChangeStyleElement';
-import { Collapse, Input, InputNumber } from 'antd';
-import { CSSParseResultElementType } from '../types/RestaSetting';
-import t from '../features/translator';
-import { setFormatAndPushToAry } from '../features/formatter';
-import InputNumberWithUnit from './controls/InputNumberWithUnit';
-import Select from './controls/Select';
-import { getAbsoluteCSSSelector } from '../utils/CSSUtils';
-import { ElementSelectionContext } from '../contexts/ElementSelectionContext';
-
-const Wrapper = styled.div``;
-
-const CollapseWrapper = styled.div`
- background-color: white;
- border-radius: 8px;
-`;
-
-const Description = styled.p`
- font-size: 10pt;
- padding-bottom: 12px;
-`;
-
-const { Panel } = Collapse;
-
-interface CategoryProps {
- searchText: string;
- title: string;
- elements: ChangeStyleElement[];
-}
-
-const ChangeStyleCategory = ({
- searchText,
- title,
- elements,
-}: CategoryProps) => {
- const elementSelection = useContext(ElementSelectionContext);
-
- const onChange = (key: string, value: string, id: number | string) => {
- if (elementSelection.selectedElement) {
- setFormatAndPushToAry(
- getAbsoluteCSSSelector(elementSelection.selectedElement),
- key,
- value,
- id,
- );
- }
- };
-
- const filter = (element: ChangeStyleElement) => {
- if (searchText.length == 0) {
- return true;
- }
- return (
- t(element.name).includes(searchText) || element.key.includes(searchText)
- );
- };
-
- const isNumberWithUnit = (parts: LayoutPart[]) => {
- return (
- parts.length == 2 &&
- parts[0].type === CSSParseResultElementType.NUMBER &&
- parts[1].type === CSSParseResultElementType.SELECT
- );
- };
-
- return (
-
-
- {t(title)}
-
-
-
- {elements.filter(filter).map((element, index) => (
-
- {t(element.description)}
- {isNumberWithUnit(element.parts) && (
-
- )}
- {!isNumberWithUnit(element.parts) &&
- element.parts.map((part, index) => (
- <>
- {part.type === CSSParseResultElementType.SELECT && (
-
- )}
- {part.type === CSSParseResultElementType.STRING && (
-
- onChange(
- element.key,
- value.currentTarget.value,
- element.id,
- )
- }
- />
- )}
- {part.type === CSSParseResultElementType.NUMBER && (
-
- onChange(
- element.key,
- value?.toString() ?? '',
- element.id,
- )
- }
- />
- )}
- {part.type === CSSParseResultElementType.RAWTEXT && (
- {part.text}
- )}
- >
- ))}
-
- ))}
-
-
-
- );
-};
-
-export default ChangeStyleCategory;
+import styled from 'styled-components';
+import Title from 'antd/lib/typography/Title';
+import React, { useContext } from 'react';
+import { ChangeStyleElement, LayoutPart } from '../types/ChangeStyleElement';
+import { Collapse, Input, InputNumber } from 'antd';
+import { CSSParseResultElementType } from '../types/RestaSetting';
+import t from '../features/translator';
+import { setFormatAndPushToAry } from '../features/formatter';
+import InputNumberWithUnit from './controls/InputNumberWithUnit';
+import Select from './controls/Select';
+import { getAbsoluteCSSSelector } from '../utils/CSSUtils';
+import { ElementSelectionContext } from '../contexts/ElementSelectionContext';
+import { PropsContext } from '../contexts/PropsContext';
+
+const Wrapper = styled.div``;
+
+const CollapseWrapper = styled.div`
+ background-color: white;
+ border-radius: 8px;
+`;
+
+const Description = styled.p`
+ font-size: 10pt;
+ padding-bottom: 12px;
+`;
+
+const { Panel } = Collapse;
+
+interface CategoryProps {
+ searchText: string;
+ title: string;
+ elements: ChangeStyleElement[];
+}
+
+const ChangeStyleCategory = ({
+ searchText,
+ title,
+ elements,
+}: CategoryProps) => {
+ const elementSelection = useContext(ElementSelectionContext);
+ const prop = useContext(PropsContext);
+
+ const onChange = (key: string, value: string, id: number | string) => {
+ if (elementSelection.selectedElement) {
+ setFormatAndPushToAry(
+ getAbsoluteCSSSelector(elementSelection.selectedElement),
+ key,
+ value,
+ id,
+ prop,
+ );
+ }
+ };
+
+ const filter = (element: ChangeStyleElement) => {
+ if (searchText.length == 0) {
+ return true;
+ }
+ return (
+ t(element.name).includes(searchText) || element.key.includes(searchText)
+ );
+ };
+
+ const isNumberWithUnit = (parts: LayoutPart[]) => {
+ return (
+ parts.length == 2 &&
+ parts[0].type === CSSParseResultElementType.NUMBER &&
+ parts[1].type === CSSParseResultElementType.SELECT
+ );
+ };
+
+ return (
+
+
+ {t(title)}
+
+
+
+ {elements.filter(filter).map((element, index) => (
+
+ {t(element.description)}
+ {isNumberWithUnit(element.parts) && (
+
+ )}
+ {!isNumberWithUnit(element.parts) &&
+ element.parts.map((part, index) => (
+ <>
+ {part.type === CSSParseResultElementType.SELECT && (
+
+ )}
+ {part.type === CSSParseResultElementType.STRING && (
+
+ onChange(
+ element.key,
+ value.currentTarget.value,
+ element.id,
+ )
+ }
+ />
+ )}
+ {part.type === CSSParseResultElementType.NUMBER && (
+
+ onChange(
+ element.key,
+ value?.toString() ?? '',
+ element.id,
+ )
+ }
+ />
+ )}
+ {part.type === CSSParseResultElementType.RAWTEXT && (
+ {part.text}
+ )}
+ >
+ ))}
+
+ ))}
+
+
+
+ );
+};
+
+export default ChangeStyleCategory;
diff --git a/src/components/Controller.tsx b/src/components/Controller.tsx
new file mode 100644
index 0000000..68ec12e
--- /dev/null
+++ b/src/components/Controller.tsx
@@ -0,0 +1,18 @@
+import { useContext, useEffect } from 'react';
+import { PropsContext } from '../contexts/PropsContext';
+import { loadFormat, loadImportedStyle } from '../features/format_manager';
+
+export default function Controller() {
+ const prop = useContext(PropsContext);
+
+ prop.setCurrentUrl(window.location.href);
+ prop.setEditedUrl(window.location.href);
+
+ useEffect(() => {
+ // do something
+ loadFormat(prop);
+ loadImportedStyle(prop);
+ }, []);
+
+ return null;
+}
diff --git a/src/components/ToolBar.tsx b/src/components/ToolBar.tsx
index e96bd5e..c2a1ffb 100644
--- a/src/components/ToolBar.tsx
+++ b/src/components/ToolBar.tsx
@@ -73,18 +73,30 @@ const ToolBar = () => {
type="ghost"
size={'small'}
disabled={!context.executor.isUndoable}
- icon={}
+ icon={
+
+ }
onClick={context.executor.undo}
/>
}
+ icon={
+
+ }
onClick={context.executor.redo}
/>
);
};
-export default ToolBar;
\ No newline at end of file
+export default ToolBar;
diff --git a/src/components/tabitems/block/BlockCustomizer.tsx b/src/components/tabitems/block/BlockCustomizer.tsx
index 7c61012..a66271c 100644
--- a/src/components/tabitems/block/BlockCustomizer.tsx
+++ b/src/components/tabitems/block/BlockCustomizer.tsx
@@ -1,67 +1,87 @@
-import styled from 'styled-components';
-import React, { useContext, useState } from 'react';
-import { Input } from 'antd';
-import t from '../../../features/translator';
-import { SearchOutlined } from '@ant-design/icons';
-import SubTitle from '../common/SubTitle';
-import Section from '../common/Section';
-import BackgroundColorCustomizer from './BackgroundColorCustomizer';
-import BorderColorCustomizer from './BorderColorCustomizer';
-import BorderRadiusCustomizer from './BorderRadiusCustomizer';
-import PaddingCustomizer from './PaddingCustomizer';
-import SizeCustomizer from './SizeCustomizer';
-import { ElementSelectionContext } from '../../../contexts/ElementSelectionContext';
-import BoxShadowCustomizer from './BoxShadowCustomizer';
-
-const Wrapper = styled.div``;
-
-const InputWrapper = styled.div`
- padding-bottom: 12px;
-`;
-
-interface BlockCustomizerProps {
- onChange: (key: string, value: string, id: number | string) => void;
-}
-
-const BlockCustomizer = ({ onChange }: BlockCustomizerProps) => {
- const elementSelection = useContext(ElementSelectionContext);
- const [searchText, setSearchText] = useState('');
-
- const customizers: { [key: string]: React.JSX.Element } = {
- 背景色: ,
- 幅と高さ: ,
- 余白: ,
- 角丸: ,
- 影: ,
- 枠線: ,
- };
-
- return (
-
- {elementSelection.selectedElement && (
- <>
-
- }
- onChange={(e) => setSearchText(e.currentTarget!.value)}
- />
-
- {Object.entries(customizers)
- .filter((entry) =>
- searchText ? t(entry[0]).includes(searchText) : true,
- )
- .map(([title, element]) => (
-
- ))}
- >
- )}
- {!elementSelection.selectedElement && 要素を選択してください
}
-
- );
-};
-
-export default BlockCustomizer;
+import styled from 'styled-components';
+import React, { useContext, useState } from 'react';
+import { Input } from 'antd';
+import t from '../../../features/translator';
+import { SearchOutlined } from '@ant-design/icons';
+import SubTitle from '../common/SubTitle';
+import Section from '../common/Section';
+import BackgroundColorCustomizer from './BackgroundColorCustomizer';
+import BorderColorCustomizer from './BorderColorCustomizer';
+import BorderRadiusCustomizer from './BorderRadiusCustomizer';
+import PaddingCustomizer from './PaddingCustomizer';
+import SizeCustomizer from './SizeCustomizer';
+import { ElementSelectionContext } from '../../../contexts/ElementSelectionContext';
+import BoxShadowCustomizer from './BoxShadowCustomizer';
+import { PropsContext } from '../../../contexts/PropsContext';
+import { UIUpdaterContext } from '../../../contexts/UIUpdater';
+import { setFormatsAndPushToAry } from '../../../features/formatter';
+import { getAbsoluteCSSSelector } from '../../../utils/CSSUtils';
+
+const Wrapper = styled.div``;
+
+const InputWrapper = styled.div`
+ padding-bottom: 12px;
+`;
+
+const BlockCustomizer = () => {
+ const elementSelection = useContext(ElementSelectionContext);
+ const [searchText, setSearchText] = useState('');
+ const prop = useContext(PropsContext);
+ const updater = useContext(UIUpdaterContext);
+
+ const onChange = (key: string, value: string, id: number | string) => {
+ if (elementSelection.selectedElement) {
+ setFormatsAndPushToAry(
+ [
+ {
+ id,
+ cssSelector:
+ getAbsoluteCSSSelector(elementSelection.selectedElement) +
+ elementSelection.selectedPseudoClass,
+ values: [{ key, value }],
+ },
+ ],
+ prop,
+ );
+ updater.formatChanged();
+ }
+ };
+
+ const customizers: { [key: string]: React.JSX.Element } = {
+ 背景色: ,
+ 幅と高さ: ,
+ 余白: ,
+ 角丸: ,
+ 影: ,
+ 枠線: ,
+ };
+
+ return (
+
+ {elementSelection.selectedElement && (
+ <>
+
+ }
+ onChange={(e) => setSearchText(e.currentTarget!.value)}
+ />
+
+ {Object.entries(customizers)
+ .filter((entry) =>
+ searchText ? t(entry[0]).includes(searchText) : true,
+ )
+ .map(([title, element]) => (
+
+ ))}
+ >
+ )}
+ {!elementSelection.selectedElement && 要素を選択してください
}
+
+ );
+};
+
+export default BlockCustomizer;
diff --git a/src/components/tabitems/font/FontCustomizer.tsx b/src/components/tabitems/font/FontCustomizer.tsx
index f088893..e0cde0c 100644
--- a/src/components/tabitems/font/FontCustomizer.tsx
+++ b/src/components/tabitems/font/FontCustomizer.tsx
@@ -10,11 +10,12 @@ import {
FontColorsOutlined,
FontSizeOutlined,
ItalicOutlined,
- LineHeightOutlined, PlusOutlined,
+ LineHeightOutlined,
+ PlusOutlined,
UnderlineOutlined,
VerticalAlignBottomOutlined,
VerticalAlignMiddleOutlined,
- VerticalAlignTopOutlined
+ VerticalAlignTopOutlined,
} from '@ant-design/icons';
import IconButton from '../../controls/IconButton';
import InputNumberWithUnit from '../../controls/InputNumberWithUnit';
@@ -34,7 +35,10 @@ import { Button, Divider, Input, InputRef, Select, Space, Tooltip } from 'antd';
import type { SelectProps } from 'antd';
import opentype from 'opentype.js';
import { defaultFontFamilies } from '../../../consts/cssValues';
-import { kebabToCamel } from '../../../utils/CSSUtils';
+import { getAbsoluteCSSSelector, kebabToCamel } from '../../../utils/CSSUtils';
+import { setFormatsAndPushToAry } from '../../../features/formatter';
+import { PropsContext } from '../../../contexts/PropsContext';
+import { UIUpdaterContext } from '../../../contexts/UIUpdater';
const Wrapper = styled.div``;
@@ -45,22 +49,22 @@ const IW = styled.span`
padding: calc(100% - 6px);
`;
-const FontItem = styled.span<{family: string}>`
+const FontItem = styled.span<{ family: string }>`
font-family: ${(props) => props.family};
`;
const { Option } = Select;
-interface FontCustomizerProps {
- onChange: (key: string, value: string, id: number | string) => void;
-}
-
-const FontCustomizer = ({ onChange }: FontCustomizerProps) => {
+const FontCustomizer = () => {
const elementSelection = useContext(ElementSelectionContext);
//const [defaultFonts, setDefaultFonts] = useState([]);
const [selectedFonts, setSelectedFonts] = useState([]);
- const [installedFonts, setInstalledFonts] = useState([]);
+ const [installedFonts, setInstalledFonts] = useState(
+ [],
+ );
const [fontName, setFontName] = useState('');
+ const prop = useContext(PropsContext);
+ const updater = useContext(UIUpdaterContext);
const inputRef = useRef(null);
@@ -68,10 +72,15 @@ const FontCustomizer = ({ onChange }: FontCustomizerProps) => {
setFontName(event.target.value);
};
- const addFont = (e: React.MouseEvent) => {
+ const addFont = (
+ e: React.MouseEvent,
+ ) => {
if (fontName) {
e.preventDefault();
- setInstalledFonts([...(installedFonts ?? []), {label: fontName, value: fontName}]);
+ setInstalledFonts([
+ ...(installedFonts ?? []),
+ { label: fontName, value: fontName },
+ ]);
setFontName('');
setTimeout(() => {
inputRef.current?.focus();
@@ -81,11 +90,29 @@ const FontCustomizer = ({ onChange }: FontCustomizerProps) => {
const onChangeFonts = (values: string[]) => {
setSelectedFonts(values);
- }
+ };
+
+ const onChange = (key: string, value: string, id: number | string) => {
+ if (elementSelection.selectedElement) {
+ setFormatsAndPushToAry(
+ [
+ {
+ id,
+ cssSelector:
+ getAbsoluteCSSSelector(elementSelection.selectedElement) +
+ elementSelection.selectedPseudoClass,
+ values: [{ key, value }],
+ },
+ ],
+ prop,
+ );
+ updater.formatChanged();
+ }
+ };
useEffect(() => {
const value = selectedFonts
- .map((font) => defaultFontFamilies.includes(font) ? font : `"${font}"`)
+ .map((font) => (defaultFontFamilies.includes(font) ? font : `"${font}"`))
.join(', ');
onChange('font-family', value, 'font-family');
}, [selectedFonts]);
@@ -109,27 +136,30 @@ const FontCustomizer = ({ onChange }: FontCustomizerProps) => {
const queryLocalFonts = async (): Promise => {
// @ts-ignore
return window.queryLocalFonts();
- }
+ };
const push = (name: string) => {
result.push({
label: name,
value: name,
});
- }
+ };
const NULL_FONT = '#@~null~@#';
const fonts: FontData[] = await queryLocalFonts();
- const localCache: {[key: string]: string} = (await chrome.storage.local.get('fonts')).fonts ?? {};
+ const localCache: { [key: string]: string } =
+ (await chrome.storage.local.get('fonts')).fonts ?? {};
for (const font of fonts) {
const blob = await font.blob();
const sfntVersion = await blob.slice(0, 4).text();
- if (sfntVersion !== '\x00\x01\x00\x00'
- && sfntVersion !== 'true'
- && sfntVersion !== 'typ1'
- && sfntVersion !== 'OTTO') {
+ if (
+ sfntVersion !== '\x00\x01\x00\x00' &&
+ sfntVersion !== 'true' &&
+ sfntVersion !== 'typ1' &&
+ sfntVersion !== 'OTTO'
+ ) {
continue;
}
@@ -155,7 +185,7 @@ const FontCustomizer = ({ onChange }: FontCustomizerProps) => {
}
}
- await chrome.storage.local.set({'fonts': localCache});
+ await chrome.storage.local.set({ fonts: localCache });
defaultFontFamilies.forEach((font) => {
push(font);
@@ -169,10 +199,9 @@ const FontCustomizer = ({ onChange }: FontCustomizerProps) => {
if (elementSelection.selectedElement) {
const style = getComputedStyle(elementSelection.selectedElement);
const value = (style as any)[kebabToCamel('font-family')] as string;
- const fonts = value.split(/,\s*/).map((text) => text
- .replace(/^(["'])/, '')
- .replace(/(["'])$/, '')
- );
+ const fonts = value
+ .split(/,\s*/)
+ .map((text) => text.replace(/^(["'])/, '').replace(/(["'])$/, ''));
setSelectedFonts(fonts);
}
}, [elementSelection.selectedElement]);
@@ -223,8 +252,8 @@ const FontCustomizer = ({ onChange }: FontCustomizerProps) => {
diff --git a/src/components/tabitems/layer/LayerCustomizer.tsx b/src/components/tabitems/layer/LayerCustomizer.tsx
index d81c28c..1ffe04e 100644
--- a/src/components/tabitems/layer/LayerCustomizer.tsx
+++ b/src/components/tabitems/layer/LayerCustomizer.tsx
@@ -1,127 +1,130 @@
-import styled from 'styled-components';
-import React, { Key, useContext, useEffect, useState } from 'react';
-import Section from '../common/Section';
-import SubTitle from '../common/SubTitle';
-import { DataNode } from 'antd/es/tree';
-import { DeleteOutlined, DownOutlined } from '@ant-design/icons';
-import { Tree, Button } from 'antd';
-import { UIUpdaterContext } from '../../../contexts/UIUpdater';
-import { getStyleLayer } from '../../../features/style_layer';
-import { ElementSelectionContext } from '../../../contexts/ElementSelectionContext';
-import { getAbsoluteCSSSelector } from '../../../utils/CSSUtils';
-import { deleteFromAry } from '../../../features/formatter';
-import t from '../../../features/translator';
-import { updateFormat } from '../../../features/prop';
-import { saveFormat } from '../../../features/format_manager';
-import { pseudoClassOptions } from '../../../consts/menu';
-
-const Wrapper = styled.div``;
-
-const TreeWrapper = styled.div`
- & > * {
- background-color: transparent;
- }
-
- & .ant-tree-switcher {
- display: flex;
- align-items: center;
- }
-`;
-
-const LayerCustomizer = () => {
- const [tree, setTree] = useState([]);
- const updater = useContext(UIUpdaterContext);
- const elementSelection = useContext(ElementSelectionContext);
-
- const createTree = (): DataNode[] => {
- if (elementSelection.selectedElement) {
- return (
- (pseudoClassOptions.map(({ label, value }, i) => {
- return {
- title: label,
- key: i,
- selectable: false,
- children: getStyleLayer(
- getAbsoluteCSSSelector(elementSelection.selectedElement!) + value,
- ).children.map((child, index) => ({
- title: (
-
- {t(child.cssKey)}
-
- ),
- key: `${i}-${index}`,
- selectable: false,
- children: [
- {
- title: child.value,
- key: `${i}-${index}-0`,
- selectable: false,
- },
- ],
- })),
- };
- }) as DataNode[]) || []
- );
- }
- return []; // TODO
- };
-
- const onDeleteStyle = (
- selector: string,
- cssKey: string,
- id: string | number,
- ) => {
- (async () => {
- deleteFromAry(selector, cssKey, id);
- updateFormat(selector, cssKey);
- await saveFormat();
- updater.formatChanged();
- })();
- };
-
- const onSelect = (_keys: Key[]) => {
- const keys = _keys as string[];
- console.log(keys);
-
- // TODO
- };
-
- useEffect(() => {
- setTree(createTree());
- console.log(createTree());
- }, [updater.changeFormatObserver, elementSelection.selectedElement]);
-
- return (
-
-
-
-
- }
- defaultExpandParent={true}
- defaultExpandAll={true}
- onSelect={onSelect}
- treeData={tree}
- style={{ backgroundColor: 'transparent' }}
- />
-
-
-
- );
-};
-
-export default LayerCustomizer;
+import styled from 'styled-components';
+import React, { Key, useContext, useEffect, useState } from 'react';
+import Section from '../common/Section';
+import SubTitle from '../common/SubTitle';
+import { DataNode } from 'antd/es/tree';
+import { DeleteOutlined, DownOutlined } from '@ant-design/icons';
+import { Tree, Button } from 'antd';
+import { UIUpdaterContext } from '../../../contexts/UIUpdater';
+import { getStyleLayer } from '../../../features/style_layer';
+import { ElementSelectionContext } from '../../../contexts/ElementSelectionContext';
+import { getAbsoluteCSSSelector } from '../../../utils/CSSUtils';
+import { deleteFromAry } from '../../../features/formatter';
+import t from '../../../features/translator';
+import { updateFormat } from '../../../features/prop';
+import { saveFormat } from '../../../features/format_manager';
+import { pseudoClassOptions } from '../../../consts/menu';
+import { PropsContext } from '../../../contexts/PropsContext';
+
+const Wrapper = styled.div``;
+
+const TreeWrapper = styled.div`
+ & > * {
+ background-color: transparent;
+ }
+
+ & .ant-tree-switcher {
+ display: flex;
+ align-items: center;
+ }
+`;
+
+const LayerCustomizer = () => {
+ const [tree, setTree] = useState([]);
+ const updater = useContext(UIUpdaterContext);
+ const elementSelection = useContext(ElementSelectionContext);
+ const prop = useContext(PropsContext);
+
+ const createTree = (): DataNode[] => {
+ if (elementSelection.selectedElement) {
+ return (
+ (pseudoClassOptions.map(({ label, value }, i) => {
+ return {
+ title: label,
+ key: i,
+ selectable: false,
+ children: getStyleLayer(
+ getAbsoluteCSSSelector(elementSelection.selectedElement!) + value,
+ prop,
+ ).children.map((child, index) => ({
+ title: (
+
+ {t(child.cssKey)}
+
+ ),
+ key: `${i}-${index}`,
+ selectable: false,
+ children: [
+ {
+ title: child.value,
+ key: `${i}-${index}-0`,
+ selectable: false,
+ },
+ ],
+ })),
+ };
+ }) as DataNode[]) || []
+ );
+ }
+ return []; // TODO
+ };
+
+ const onDeleteStyle = (
+ selector: string,
+ cssKey: string,
+ id: string | number,
+ ) => {
+ (async () => {
+ deleteFromAry(selector, cssKey, id, prop);
+ updateFormat(selector, cssKey, prop);
+ await saveFormat(prop);
+ updater.formatChanged();
+ })();
+ };
+
+ const onSelect = (_keys: Key[]) => {
+ const keys = _keys as string[];
+ console.log(keys);
+
+ // TODO
+ };
+
+ useEffect(() => {
+ setTree(createTree());
+ console.log(createTree());
+ }, [updater.changeFormatObserver, elementSelection.selectedElement]);
+
+ return (
+
+
+
+
+ }
+ defaultExpandParent={true}
+ defaultExpandAll={true}
+ onSelect={onSelect}
+ treeData={tree}
+ style={{ backgroundColor: 'transparent' }}
+ />
+
+
+
+ );
+};
+
+export default LayerCustomizer;
diff --git a/src/components/tabitems/settings/PageSettingTabItem.tsx b/src/components/tabitems/settings/PageSettingTabItem.tsx
index e0c3b1f..63feef4 100644
--- a/src/components/tabitems/settings/PageSettingTabItem.tsx
+++ b/src/components/tabitems/settings/PageSettingTabItem.tsx
@@ -2,13 +2,13 @@ import styled from 'styled-components';
import t from '../../../features/translator';
import React, { ChangeEvent, useContext, useState } from 'react';
import { Button, Checkbox, Input, Modal, Popconfirm, Progress } from 'antd';
-import * as prop from '../../../features/prop';
import { saveFormatImmediately } from '../../../features/format_manager';
import { ElementSelectionContext } from '../../../contexts/ElementSelectionContext';
import { getAbsoluteCSSSelector } from '../../../utils/CSSUtils';
import Section from '../common/Section';
import SubTitle from '../common/SubTitle';
import { DEBUG_MODE } from '../../../consts/debug';
+import { PropsContext } from '../../../contexts/PropsContext';
const Wrapper = styled.div``;
@@ -24,15 +24,22 @@ const DeveloperTools = styled.div`
const { TextArea } = Input;
const PageSettingTabItem = () => {
+ const prop = useContext(PropsContext);
const onClickInitPageFormat = () => {
- prop.removeCurrentFormat();
- saveFormatImmediately();
+ // editedUrlと一致するものを削除
+ prop.setFormatsArray(
+ prop.formatsArray.filter((f) => f.url !== prop.editedUrl),
+ );
+ saveFormatImmediately(prop);
window.location.reload();
};
const onClickInitAllPageFormat = () => {
- prop.removeAllFormats();
- saveFormatImmediately();
+ // FormatsArrayを空にする
+ prop.setFormatsArray([]);
+ // localStorageのformatsを上書き
+ saveFormatImmediately(prop);
+ // ページをリロード
window.location.reload();
};
@@ -66,7 +73,7 @@ const PageSettingTabItem = () => {
{t('change_eddited_url_description')}
- { DEBUG_MODE &&
+ {DEBUG_MODE && (
{'拡張機能のストレージ内容を表示'}
@@ -117,7 +124,10 @@ const PageSettingTabItem = () => {
onClick={async () => {
setStorage(
JSON.stringify(
- (await chrome.storage.local.get(['formats', 'imported_style'])),
+ await chrome.storage.local.get([
+ 'formats',
+ 'imported_style',
+ ]),
null,
' ',
),
@@ -149,7 +159,7 @@ const PageSettingTabItem = () => {
/>
- }
+ )}
)}
diff --git a/src/components/tabitems/template/ImportedStylesList.tsx b/src/components/tabitems/template/ImportedStylesList.tsx
index d7d5ca0..c088201 100644
--- a/src/components/tabitems/template/ImportedStylesList.tsx
+++ b/src/components/tabitems/template/ImportedStylesList.tsx
@@ -1,130 +1,130 @@
-import React, { useEffect, useState } from 'react';
-import styled from 'styled-components';
-import {
- applyPageFormat, deleteImportedFormat,
- getImportedFormats,
- ImportedFormatAbstract
-} from '../../../features/importStyle';
-import t from '../../../features/translator';
-import { Button, Card, Popconfirm } from 'antd';
-
-const Wrapper = styled.div``;
-
-const DescriptionWrapper = styled.div`
- padding-bottom: 12px;
-`;
-
-const Author = styled.p`
- padding-bottom: 8px;
-`;
-
-const ThumbnailWrapper = styled.div`
- width: 100%;
- height: 150px;
-`;
-
-const Thumbnail = styled.div<{src: string}>`
- width: 100%;
- height: 100%;
- background-image: ${(prop) => `url("${prop.src}")`};
- background-size: cover;
- background-position: center center;
- border-radius: 8px 8px 0 0;
-`;
-
-interface CardsProps {
- styles: ImportedFormatAbstract[];
- updateFunc: () => Promise;
-}
-
-const Cards = ({ styles, updateFunc }: CardsProps) => {
- const { Meta } = Card;
-
- const onApplyClick = (style: ImportedFormatAbstract) => {
- applyPageFormat(style.id);
- };
-
- const onDeleteClick = (style: ImportedFormatAbstract) => {
- deleteImportedFormat(style.id);
- void updateFunc();
- };
-
- return (
- <>
- {styles.map((style) => (
-
-
-
- }
- actions={[
- onDeleteClick(style)}
- okText={t('yes')}
- cancelText={t('no')}
- >
-
- ,
- ,
- ]}
- >
-
- { style.author && {style.author}}
- {t('open_style_link')}
- >
- }
- />
-
- ))}
- >
- );
-};
-
-const ImportedStylesList = () => {
- const [styles, setStyles] = useState([]);
-
- const onOpenStoreClick = () => {
- window.open('https://resta-frontend.pages.dev/style/', '_blank');
- };
-
- const updateTree = async () => {
- const styles = getImportedFormats();
- setStyles(styles);
- }
-
- useEffect(() => {
- void updateTree();
- }, []);
-
- return (
-
- {styles.length !== 0 && }
- {styles.length === 0 && (
- <>
-
- {t('no_imported_styles')}
-
-
- >
- )}
-
- );
-};
-
-export default ImportedStylesList;
+import React, { useEffect, useState } from 'react';
+import styled from 'styled-components';
+import {
+ applyPageFormat, deleteImportedFormat,
+ getImportedFormats,
+ ImportedFormatAbstract
+} from '../../../features/importStyle';
+import t from '../../../features/translator';
+import { Button, Card, Popconfirm } from 'antd';
+
+const Wrapper = styled.div``;
+
+const DescriptionWrapper = styled.div`
+ padding-bottom: 12px;
+`;
+
+const Author = styled.p`
+ padding-bottom: 8px;
+`;
+
+const ThumbnailWrapper = styled.div`
+ width: 100%;
+ height: 150px;
+`;
+
+const Thumbnail = styled.div<{src: string}>`
+ width: 100%;
+ height: 100%;
+ background-image: ${(prop) => `url("${prop.src}")`};
+ background-size: cover;
+ background-position: center center;
+ border-radius: 8px 8px 0 0;
+`;
+
+interface CardsProps {
+ styles: ImportedFormatAbstract[];
+ updateFunc: () => Promise;
+}
+
+const Cards = ({ styles, updateFunc }: CardsProps) => {
+ const { Meta } = Card;
+
+ const onApplyClick = (style: ImportedFormatAbstract) => {
+ applyPageFormat(style.id);
+ };
+
+ const onDeleteClick = (style: ImportedFormatAbstract) => {
+ deleteImportedFormat(style.id);
+ void updateFunc();
+ };
+
+ return (
+ <>
+ {styles.map((style) => (
+
+
+
+ }
+ actions={[
+ onDeleteClick(style)}
+ okText={t('yes')}
+ cancelText={t('no')}
+ >
+
+ ,
+ ,
+ ]}
+ >
+
+ { style.author && {style.author}}
+ {t('open_style_link')}
+ >
+ }
+ />
+
+ ))}
+ >
+ );
+};
+
+const ImportedStylesList = () => {
+ const [styles, setStyles] = useState([]);
+
+ const onOpenStoreClick = () => {
+ window.open('https://resta-frontend.pages.dev/style/', '_blank');
+ };
+
+ const updateTree = async () => {
+ const styles = getImportedFormats();
+ setStyles(styles);
+ }
+
+ useEffect(() => {
+ void updateTree();
+ }, []);
+
+ return (
+
+ {styles.length !== 0 && }
+ {styles.length === 0 && (
+ <>
+
+ {t('no_imported_styles')}
+
+
+ >
+ )}
+
+ );
+};
+
+export default ImportedStylesList;
\ No newline at end of file
diff --git a/src/components/tabitems/template/TemplateCard.tsx b/src/components/tabitems/template/TemplateCard.tsx
index a0d627a..fbdafa1 100644
--- a/src/components/tabitems/template/TemplateCard.tsx
+++ b/src/components/tabitems/template/TemplateCard.tsx
@@ -1,96 +1,96 @@
-import { Template } from '../../../types/Template';
-import { Button, Card } from 'antd';
-import t from '../../../features/translator';
-import React, { useContext, useEffect, useRef } from 'react';
-import { getAbsoluteCSSSelector } from '../../../utils/CSSUtils';
-import styled from 'styled-components';
-import { ElementSelectionContext } from '../../../contexts/ElementSelectionContext';
-import { setFormatsAndPushToAry } from '../../../features/formatter';
-import { getStyleSheet } from '../../../features/style_sheet';
-import { UIUpdaterContext } from '../../../contexts/UIUpdater';
-import { createId } from '../../../utils/IDUtils';
-
-const InnerWrapper = styled.div`
- width: 100%;
- height: 100%;
- box-sizing: border-box;
-
- & > * {
- box-sizing: border-box;
- width: 100% !important;
- }
-`;
-
-interface TemplateCardProps {
- template: Template;
-}
-
-const TemplateCard = ({ template }: TemplateCardProps) => {
- const ref = useRef(null);
- const elementSelection = useContext(ElementSelectionContext);
- const updater = useContext(UIUpdaterContext);
-
- const onUseClick = () => {
- if (elementSelection.selectedElement) {
- setFormatsAndPushToAry(
- template.styles.map((style) => ({
- id: createId(template.name),
- cssSelector:
- getAbsoluteCSSSelector(elementSelection.selectedElement!) +
- (style.pseudoClass ? `:${style.pseudoClass}` : ''),
- values: Object.entries(style.css).map(([key, value]) => ({
- key,
- value,
- })),
- })),
- );
-
- updater.formatChanged();
- }
- };
-
- const insertCSS = () => {
- template.styles.forEach((style) => {
- getStyleSheet()?.insertRule(
- `${getAbsoluteCSSSelector(ref.current!)}[id='${template.name}']${
- style.pseudoClass ? `:${style.pseudoClass}` : ''
- } {\n` +
- `${Object.entries(style.css)
- .map(([key, value]) => `${key}: ${value}`)
- .join(';\n')};\n` +
- '}',
- );
- });
- };
-
- useEffect(() => insertCSS(), []);
-
- return (
-
- 適用
-
- }
- >
-
- {template.tags[0] === 'a' && (
-
- ボタン
-
- )}
- {template.tags[0] === 'button' && (
-
- )}
-
-
- );
-};
-
-export default TemplateCard;
+import { Template } from '../../../types/Template';
+import { Button, Card } from 'antd';
+import t from '../../../features/translator';
+import React, { useContext, useEffect, useRef } from 'react';
+import { getAbsoluteCSSSelector } from '../../../utils/CSSUtils';
+import styled from 'styled-components';
+import { ElementSelectionContext } from '../../../contexts/ElementSelectionContext';
+import { setFormatsAndPushToAry } from '../../../features/formatter';
+import { getStyleSheet } from '../../../features/style_sheet';
+import { UIUpdaterContext } from '../../../contexts/UIUpdater';
+import { createId } from '../../../utils/IDUtils';
+
+const InnerWrapper = styled.div`
+ width: 100%;
+ height: 100%;
+ box-sizing: border-box;
+
+ & > * {
+ box-sizing: border-box;
+ width: 100% !important;
+ }
+`;
+
+interface TemplateCardProps {
+ template: Template;
+}
+
+const TemplateCard = ({ template }: TemplateCardProps) => {
+ const ref = useRef(null);
+ const elementSelection = useContext(ElementSelectionContext);
+ const updater = useContext(UIUpdaterContext);
+
+ const onUseClick = () => {
+ if (elementSelection.selectedElement) {
+ setFormatsAndPushToAry(
+ template.styles.map((style) => ({
+ id: createId(template.name),
+ cssSelector:
+ getAbsoluteCSSSelector(elementSelection.selectedElement!) +
+ (style.pseudoClass ? `:${style.pseudoClass}` : ''),
+ values: Object.entries(style.css).map(([key, value]) => ({
+ key,
+ value,
+ })),
+ })),
+ );
+
+ updater.formatChanged();
+ }
+ };
+
+ const insertCSS = () => {
+ template.styles.forEach((style) => {
+ getStyleSheet()?.insertRule(
+ `${getAbsoluteCSSSelector(ref.current!)}[id='${template.name}']${
+ style.pseudoClass ? `:${style.pseudoClass}` : ''
+ } {\n` +
+ `${Object.entries(style.css)
+ .map(([key, value]) => `${key}: ${value}`)
+ .join(';\n')};\n` +
+ '}',
+ );
+ });
+ };
+
+ useEffect(() => insertCSS(), []);
+
+ return (
+
+ 適用
+
+ }
+ >
+
+ {template.tags[0] === 'a' && (
+
+ ボタン
+
+ )}
+ {template.tags[0] === 'button' && (
+
+ )}
+
+
+ );
+};
+
+export default TemplateCard;
\ No newline at end of file
diff --git a/src/consts/debug.ts b/src/consts/debug.ts
index 7eda24b..38c744e 100644
--- a/src/consts/debug.ts
+++ b/src/consts/debug.ts
@@ -1,7 +1,7 @@
-export const DEBUG_MODE = false;
+export const DEBUG_MODE = true;
/**
* Log level (recommended: 2)
* 0:log, 1:warn, 2:error
*/
-export const LOG_LEVEL = 1;
+export const LOG_LEVEL = 0;
diff --git a/src/contexts/PropsContext.ts b/src/contexts/PropsContext.ts
index 0e1b1e3..a2f19ef 100644
--- a/src/contexts/PropsContext.ts
+++ b/src/contexts/PropsContext.ts
@@ -1,9 +1,11 @@
import React, { Dispatch, useCallback, useState } from 'react';
import { FormatBlockByURL } from '../types/Format';
import { ImportedFormat } from '../features/prop';
-import useCommandExecutor, { CommandExecutor } from 'react-command-lib/dist/esm';
+import useCommandExecutor, {
+ CommandExecutor,
+} from 'react-command-lib/dist/esm';
-type PropsContext = {
+export type IPropsContext = {
/**
* 現在のURL
*/
@@ -32,9 +34,9 @@ type PropsContext = {
* コマンドマネージャ
*/
executor: CommandExecutor;
-}
+};
-const defaultPropsContext: PropsContext = {
+const defaultPropsContext: IPropsContext = {
currentUrl: '',
setCurrentUrl: () => undefined,
editedUrl: '',
@@ -48,27 +50,46 @@ const defaultPropsContext: PropsContext = {
undo: () => undefined,
redo: () => undefined,
isRedoable: false,
- isUndoable: false
+ isUndoable: false,
},
};
-export const PropsContext = React.createContext(defaultPropsContext);
+export const PropsContext =
+ React.createContext(defaultPropsContext);
-function useMemoizedState(defaultValue: T): [value: T, setValue: Dispatch] {
+function useMemoizedState(
+ defaultValue: T,
+): [value: T, setValue: Dispatch] {
const [value, _setValue] = useState(defaultValue);
const setValue = useCallback((v: T) => _setValue(v), []);
return [value, setValue];
}
-export default function usePropsContext(): PropsContext {
- const [currentUrl, setCurrentUrl] = useMemoizedState(defaultPropsContext.currentUrl);
- const [editedUrl, setEditedUrl] = useMemoizedState(defaultPropsContext.currentUrl);
- const [formatsArray, setFormatsArray] = useMemoizedState([]);
- const [importedFormats, setImportedFormats] = useMemoizedState([]);
+export default function usePropsContext(): IPropsContext {
+ const [currentUrl, setCurrentUrl] = useMemoizedState(
+ defaultPropsContext.currentUrl,
+ );
+ const [editedUrl, setEditedUrl] = useMemoizedState(
+ defaultPropsContext.currentUrl,
+ );
+ const [formatsArray, setFormatsArray] = useMemoizedState(
+ [],
+ );
+ const [importedFormats, setImportedFormats] = useMemoizedState<
+ ImportedFormat[]
+ >([]);
const executor = useCommandExecutor();
return {
- currentUrl, setCurrentUrl, editedUrl, setEditedUrl, formatsArray, setFormatsArray, importedFormats, setImportedFormats, executor
+ currentUrl,
+ setCurrentUrl,
+ editedUrl,
+ setEditedUrl,
+ formatsArray,
+ setFormatsArray,
+ importedFormats,
+ setImportedFormats,
+ executor,
};
-}
\ No newline at end of file
+}
diff --git a/src/features/commands/CssCommand.ts b/src/features/commands/CssCommand.ts
new file mode 100644
index 0000000..c35d44e
--- /dev/null
+++ b/src/features/commands/CssCommand.ts
@@ -0,0 +1,8 @@
+import { AbstractCommand } from 'react-command-lib/dist/esm';
+
+export default class CssCommand implements AbstractCommand {
+ constructor(
+ public execute: () => void,
+ public undo: () => void,
+ ) {}
+}
diff --git a/src/features/commands/TemplateCommand.ts b/src/features/commands/TemplateCommand.ts
new file mode 100644
index 0000000..a4f7c16
--- /dev/null
+++ b/src/features/commands/TemplateCommand.ts
@@ -0,0 +1,19 @@
+import { AbstractCommand } from 'react-command-lib/dist/esm';
+import CssCommand from './CssCommand';
+
+export default class TemplateCommand implements AbstractCommand {
+ constructor(...templates: CssCommand[]) {
+ this.execute = () => {
+ templates.forEach((template) => {
+ template.execute();
+ });
+ };
+ this.undo = () => {
+ templates.forEach((template) => {
+ template.undo();
+ });
+ };
+ }
+ execute: () => void;
+ undo: () => void;
+}
diff --git a/src/features/commands/TestCommand.ts b/src/features/commands/TestCommand.ts
new file mode 100644
index 0000000..321d264
--- /dev/null
+++ b/src/features/commands/TestCommand.ts
@@ -0,0 +1,8 @@
+import { AbstractCommand } from 'react-command-lib/dist/esm';
+
+export default class TestCommand implements AbstractCommand {
+ constructor() {}
+ execute(): void {}
+ undo(): void {}
+}
+// context.executor.execute(new TestCommand());
diff --git a/src/features/format_manager.ts b/src/features/format_manager.ts
index f3aac64..adcbed6 100644
--- a/src/features/format_manager.ts
+++ b/src/features/format_manager.ts
@@ -2,23 +2,39 @@ import * as prop from './prop';
import { FormatBlockByURL } from '../types/Format';
import * as resta_console from './resta_console';
import { debounce } from './debounce';
+import { IPropsContext } from '../contexts/PropsContext';
+import useFormatUtils from '../hooks/useFormatUtils';
/**
* localにフォーマットを保存する
*/
-export const saveFormat = async () => {
+export const saveFormat = async (prop: IPropsContext) => {
const debounceSave = debounce(save, 1000);
- debounceSave(new Date());
+ debounceSave(new Date(), prop);
};
let lastSaveTime = new Date();
-const save = (date: Date): void => {
+/**
+ * Saves the formats to local storage.
+ *
+ * @param date - The current date.
+ */
+const save = (date: Date, prop: IPropsContext): void => {
if (lastSaveTime.getTime() + 1000 > date.getTime()) {
return;
}
lastSaveTime = date;
- prop.sortFormats();
+ // フォーマットを詳細度の低い順にソートする
+ prop.setFormatsArray(
+ prop.formatsArray
+ .filter((e) => e.formats.length !== 0)
+ .sort(
+ (e) =>
+ (e.url.match(/\//g) || []).length &&
+ (e.url[e.url.length - 1] === '*' ? -1 : 1),
+ ),
+ );
chrome.storage.local
.set({ formats: JSON.stringify(prop.formatsArray) })
.then(() => {
@@ -26,8 +42,8 @@ const save = (date: Date): void => {
});
};
-export const saveFormatImmediately = async () => {
- prop.sortFormats();
+export const saveFormatImmediately = async (prop: IPropsContext) => {
+ useFormatUtils().sortFormats();
chrome.storage.local
.set({ formats: JSON.stringify(prop.formatsArray) })
.then(() => {
@@ -38,7 +54,7 @@ export const saveFormatImmediately = async () => {
/**
* localからフォーマットを読み込む
*/
-export const loadFormat = async () => {
+export const loadFormat = async (prop: IPropsContext) => {
await chrome.storage.local.get(['formats']).then((result) => {
if (!result.formats) {
resta_console.log('load:no format', prop.currentUrl);
@@ -46,7 +62,7 @@ export const loadFormat = async () => {
} else {
resta_console.log('load', prop.currentUrl, JSON.parse(result.formats));
if (JSON.parse(result.formats))
- prop.setFormatsAry(
+ prop.setFormatsArray(
(JSON.parse(result.formats) as Array).filter(
(e) => e.formats.length !== 0,
),
@@ -55,10 +71,27 @@ export const loadFormat = async () => {
}
});
};
+
+export const getFormatAryFromLocal = async (): Promise => {
+ await chrome.storage.local.get(['formats']).then((result) => {
+ if (!result.formats) {
+ resta_console.log('load:no format', prop.currentUrl);
+ return [];
+ } else {
+ const ary = (
+ JSON.parse(result.formats) as Array
+ ).filter((e) => e.formats.length !== 0);
+ resta_console.log('load', prop.currentUrl, ary);
+ return ary;
+ }
+ });
+};
+
/**
* localからフォーマットを読み込む
* もしURLが指定されていなかったらすべてのフォーマットを読み込む
*/
+/*
export const loadFormatForOutput = async (url: string = '') => {
await chrome.storage.local.get(['formats']).then((result) => {
if (!result.formats) {
@@ -76,11 +109,12 @@ export const loadFormatForOutput = async (url: string = '') => {
}
});
};
+*/
/**
* localからimportしたフォーマットを読み込む
*/
-export const loadImportedStyle = async () => {
+export const loadImportedStyle = async (prop: IPropsContext) => {
await chrome.storage.local.get(['imported_style']).then((result) => {
if (!result.imported_style) {
resta_console.log('loadImportedStyle:no format');
@@ -88,12 +122,12 @@ export const loadImportedStyle = async () => {
} else {
try {
resta_console.log('loadImportedStyle', result.imported_style);
- prop.setImportedFormat(
+ prop.setImportedFormats(
(result.imported_style as Array).filter(
(e) => e.style.length !== 0,
),
);
- resta_console.log('loadImportedStyle', prop.importedFormat);
+ resta_console.log('loadImportedStyle', prop.importedFormats);
} catch (e) {
resta_console.error('loadImportedStyle', e);
}
diff --git a/src/features/formatter.ts b/src/features/formatter.ts
index 6530bed..5fed1be 100644
--- a/src/features/formatter.ts
+++ b/src/features/formatter.ts
@@ -1,37 +1,58 @@
-import { loadFormat, loadImportedStyle } from './format_manager';
-import * as prop from './prop';
-import { StyleRule, setStyleRule } from './style_sheet';
-import { pushLog } from './unredo';
-import { UnRedoCommand, UnRedoCommands } from '../types/UnRedoCommands';
+import {
+ StyleRule,
+ getStyleSheet,
+ removeStyleRule,
+ setStyleRule,
+} from './style_sheet';
import * as resta_console from './resta_console';
-import { Format, FormatBlockByURL, FormatChange, FormatStyleValue } from '../types/Format';
+import {
+ Format,
+ FormatBlockByURL,
+ FormatChange,
+ FormatStyleValue,
+} from '../types/Format';
+import { IPropsContext } from '../contexts/PropsContext';
+import { matchUrl } from '../utils/urlUtil';
+import CssCommand from './commands/CssCommand';
+import TemplateCommand from './commands/TemplateCommand';
+import { currentUrl, getDisplayedFormat } from './prop';
+import { saveFormat } from './format_manager';
export const initStyle = async () => {
- // localからjson形式のデータを取得しparseしたものをformatsAryへ代入
- await loadFormat();
- // localからjson形式のデータを取得しparseしたものをimportedFormatへ代入
- loadImportedStyle();
// このページに対応するフォーマットがあれば適用
applyFormats();
};
-export const setFormatsAndPushToAry = (rules: Array) => {
- const commands: UnRedoCommands = { commands: [] };
+export const setFormatsAndPushToAry = (
+ rules: Array,
+ prop: IPropsContext,
+) => {
+ const commands: CssCommand[] = [];
for (const rule of rules) {
for (const value of rule.values) {
- const c = pushToAry(rule.cssSelector, value.key, value.value, rule.id);
+ const c: CssCommand | null = pushToAry(
+ rule.cssSelector,
+ value.key,
+ value.value,
+ rule.id,
+ prop,
+ );
if (c) {
- commands.commands.push(c);
+ commands.push(c);
}
- setStyleRule({
- cssSelector: rule.cssSelector,
- keys: [value.key]
- });
+ setStyleRule(
+ {
+ cssSelector: rule.cssSelector,
+ keys: [value.key],
+ },
+ prop,
+ );
}
}
- if (commands.commands.length > 0) {
- pushLog(commands);
+ if (commands.length > 0) {
+ prop.executor.execute(new TemplateCommand(...commands));
}
+ saveFormat(prop);
};
export type RemoveRule = {
@@ -49,7 +70,8 @@ export const setFormatAndPushToAry = (
cssSelector: string | null,
key: string | null,
value: string | null,
- id: number | string | null
+ id: number | string | null,
+ prop: IPropsContext,
) => {
resta_console.log('setFormatAndPushToAry', cssSelector, key, value, id);
if (!id) {
@@ -57,7 +79,7 @@ export const setFormatAndPushToAry = (
}
if (!cssSelector) {
resta_console.log(
- 'setFormatAndPushToAry:invalid args, cssSelector is not found'
+ 'setFormatAndPushToAry:invalid args, cssSelector is not found',
);
return;
}
@@ -69,16 +91,18 @@ export const setFormatAndPushToAry = (
resta_console.log('setFormatAndPushToAry:invalid args, value is not found');
return;
}
- const c = pushToAry(cssSelector, key, value, id);
+ const c = pushToAry(cssSelector, key, value, id, prop);
if (c) {
- pushLog({
- commands: [c]
- });
+ prop.executor.execute(c);
}
- setStyleRule({
- cssSelector: cssSelector,
- keys: [key]
- });
+ setStyleRule(
+ {
+ cssSelector: cssSelector,
+ keys: [key],
+ },
+ prop,
+ );
+ saveFormat(prop);
};
/**
@@ -90,8 +114,9 @@ export const pushToAry = (
cssSelector: string | null,
key: string | null,
value: string | null,
- id: number | string | null
-): UnRedoCommand | null => {
+ id: number | string | null,
+ prop: IPropsContext,
+): CssCommand | null => {
if (!cssSelector) {
resta_console.warn('pushToAry:invalid args, cssSelector is not found');
return null;
@@ -108,34 +133,35 @@ export const pushToAry = (
id = 0;
}
- let currentTargetFormatBlock: FormatBlockByURL | undefined = prop.formatsArray
- .find((e) => e.url === prop.edittedUrl);
+ let currentTargetFormatBlock: FormatBlockByURL | undefined =
+ prop.formatsArray.find((e) => e.url === prop.editedUrl);
// 以下のif文は、各配列が存在しない場合に配列を作成する処理
// すでに該当箇所への変更がある場合は書き換えている
if (currentTargetFormatBlock == null) {
- currentTargetFormatBlock = { url: prop.edittedUrl, formats: [] };
+ currentTargetFormatBlock = { url: prop.editedUrl, formats: [] };
prop.formatsArray.push(currentTargetFormatBlock);
}
- let currentFormat: Format | undefined = currentTargetFormatBlock
- .formats.find((e) => e.cssSelector === cssSelector);
+ let currentFormat: Format | undefined = currentTargetFormatBlock.formats.find(
+ (e) => e.cssSelector === cssSelector,
+ );
if (currentFormat == null) {
currentFormat = { cssSelector: cssSelector, changes: [] };
currentTargetFormatBlock.formats.push(currentFormat);
}
- let currentFormatChange: FormatChange | undefined = currentFormat
- .changes.find((e) => e.cssKey === key);
+ let currentFormatChange: FormatChange | undefined =
+ currentFormat.changes.find((e) => e.cssKey === key);
if (currentFormatChange == null) {
currentFormatChange = { cssKey: key, cssValues: [] };
currentFormat.changes.push({ cssKey: key, cssValues: [] });
}
- const currentFormatStyleValue: FormatStyleValue | undefined = currentFormatChange
- .cssValues.find((e) => e.id === id);
+ const currentFormatStyleValue: FormatStyleValue | undefined =
+ currentFormatChange.cssValues.find((e) => e.id === id);
if (currentFormatStyleValue == null) {
// idに対応する要素を追加する
@@ -143,25 +169,17 @@ export const pushToAry = (
// resta_console.log('pushToAry:push', cssSelector, key, value);
return {
- cssSelector: cssSelector,
- cssKey: key,
- id: id,
- undo: {
- type: 'delete',
- cssValue: value,
- index: 0
+ execute: () => {
+ pushToAry(cssSelector, key, value, id, prop);
+ },
+ undo: () => {
+ deleteFromAry(cssSelector, key, 0, prop);
},
- redo: {
- type: 'create',
- cssValue: value,
- index: undefined
- }
};
-
} else {
// すでにidに対応する要素がある場合
// その要素を削除して末尾に追加する
- const index = getIndex(cssSelector, key, id);
+ const index = getIndex(cssSelector, key, id, prop);
if (index == undefined || index === -1) {
resta_console.warn('pushToAry: bug detected, index is undefined');
}
@@ -169,7 +187,7 @@ export const pushToAry = (
// idに対応する要素を取り除く
const log = currentFormatChange.cssValues.splice(
currentFormatChange.cssValues.findIndex((e) => e.id === id) || 0,
- 1
+ 1,
);
// idに対応する要素を追加する
@@ -178,19 +196,25 @@ export const pushToAry = (
// resta_console.log('pushToAry:already exists, overwrite', cssSelector, key, value);
return {
- cssSelector: cssSelector,
- cssKey: key,
- id: id,
- undo: {
- type: 'rewrite',
- cssValue: log ? log[0].cssValue : '',
- index: index || 0
+ execute: () => {
+ pushToAry(cssSelector, key, value, id, prop);
},
- redo: {
- type: 'rewrite',
- cssValue: value,
- index: undefined
- }
+ undo: () => {
+ pushToAry(cssSelector, key, log ? log[0].cssValue : '', id, prop);
+ },
+ // cssSelector: cssSelector,
+ // cssKey: key,
+ // id: id,
+ // undo: {
+ // type: 'rewrite',
+ // cssValue: log ? log[0].cssValue : '',
+ // index: index || 0,
+ // },
+ // redo: {
+ // type: 'rewrite',
+ // cssValue: value,
+ // index: undefined,
+ // },
};
}
};
@@ -201,45 +225,42 @@ export const pushToAry = (
export const deleteFromAry = (
cssSelector: string,
key: string,
- id: number | string
-): UnRedoCommand | null => {
- const index = getIndex(cssSelector, key, id);
+ id: number | string,
+ prop: IPropsContext,
+): void => {
+ const index = getIndex(cssSelector, key, id, prop);
if (index == undefined || index === -1) {
resta_console.warn('deleteFromAry: bug detected, index is undefined');
}
const currentFormatChange = prop.formatsArray
- .find((e) => e.url === prop.edittedUrl)
+ .find((e) => e.url === prop.editedUrl)
?.formats.find((e) => e.cssSelector === cssSelector)
?.changes.find((e) => e.cssKey === key);
const deletedElem = currentFormatChange?.cssValues.splice(
currentFormatChange?.cssValues.findIndex((e) => e.id === id) || 0,
- 1
+ 1,
);
if (!deletedElem) {
resta_console.warn('deleteFromAry: bug detected, deletedElem is undefined');
- return null;
+ return;
}
resta_console.log('deleteFromAry', prop.formatsArray);
- return {
- cssSelector: cssSelector,
- cssKey: key,
- id: id,
- undo: {
- type: 'create',
- cssValue: deletedElem ? deletedElem[0].cssValue : '',
- index: index || 0
+ // TODO: index処理どこいった?
+ prop.executor.execute({
+ execute: () => {
+ deleteFromAry(cssSelector, key, id, prop);
},
- redo: {
- type: 'delete',
- cssValue: '',
- index: undefined
- }
- };
+ undo: () => {
+ pushToAry(cssSelector, key, deletedElem[0].cssValue, id, prop);
+ },
+ });
+
+ saveFormat(prop);
};
/**
@@ -249,10 +270,11 @@ export const deleteFromAry = (
const getIndex = (
cssSelector: string,
key: string,
- id: number | string
+ id: number | string,
+ prop: IPropsContext,
): number | undefined => {
return prop.formatsArray
- .find((e) => e.url === prop.edittedUrl)
+ .find((e) => e.url === prop.editedUrl)
?.formats.find((e) => e.cssSelector === cssSelector)
?.changes.find((e) => e.cssKey === key)
?.cssValues.findIndex((e) => e.id === id);
@@ -263,22 +285,105 @@ const getIndex = (
* 比較的重い処理なので、ページ遷移時などに呼び出す
*/
export const applyFormats = () => {
- resta_console.log('start:applyFormats', prop.formatsArray);
+ chrome.storage.local.get(['formats']).then((result) => {
+ if (!result.formats) {
+ resta_console.log('load:no format', currentUrl);
+ } else {
+ const formatsArray = (
+ JSON.parse(result.formats) as Array
+ ).filter((e) => e.formats.length !== 0);
+ resta_console.log('load', currentUrl, formatsArray);
+
+ for (const f of formatsArray) {
+ if (!matchUrl(currentUrl, f.url)) {
+ continue;
+ }
- for (const f of prop.formatsArray) {
- if (!prop.matchUrl(prop.edittedUrl, f.url)) {
- continue;
+ // resta_console.log(f);
+ for (const format of f.formats) {
+ const cssSelector = format.cssSelector;
+ setStyleRuleOnInit(
+ {
+ cssSelector: cssSelector,
+ keys: format.changes
+ .filter((e) => e.cssKey !== '' && e.cssValues.length !== 0)
+ .map((e) => e.cssKey),
+ },
+ formatsArray,
+ );
+ }
+ }
}
+ });
+};
- // resta_console.log(f);
- for (const format of f.formats) {
- const cssSelector = format.cssSelector;
- setStyleRule({
- cssSelector: cssSelector,
- keys: format.changes
- .filter((e) => e.cssKey !== '' && e.cssValues.length !== 0)
- .map((e) => e.cssKey)
- });
+/**
+ * CssSelectorとcssKeyの配列を渡すと、最新のスタイルを適用する
+ * valueはいらない
+ */
+const setStyleRuleOnInit = (
+ styles: {
+ cssSelector: string;
+ keys: Array;
+ },
+ formatsArray: Array,
+) => {
+ if (!styles.keys || !styles.cssSelector) {
+ resta_console.log('setStyleRule: invalid value');
+ return;
+ }
+ const styleSheet = getStyleSheet();
+ // insertRuleが使えるかどうか
+ // 使えない場合、つまり古いバージョンのChromeの場合はaddRuleを使う
+ const canInsert = styleSheet.insertRule as
+ | ((rule: string, index?: number) => number)
+ | undefined;
+ const formats = formatsArray
+ .map((e) => e.formats)
+ .filter((e) => e !== undefined)
+ .map((e) => e.find((e) => e.cssSelector === styles.cssSelector))
+ .filter((e) => e !== undefined);
+
+ const rule = Array.from(styleSheet?.cssRules).find(
+ (e) => e instanceof CSSStyleRule && e.selectorText === styles.cssSelector,
+ ) as CSSStyleRule | undefined;
+
+ if (rule) {
+ for (const key of styles.keys) {
+ const value = getDisplayedFormat(formats, key);
+ if (!value) {
+ resta_console.error(
+ 'formatter.setStyleRuleOnInit0: getDisplayFormat is false',
+ );
+ removeStyleRule(styles.cssSelector, key);
+ continue;
+ }
+ if (rule.style.getPropertyValue(key) === value) continue;
+ resta_console.log('setProperty');
+ rule.style.setProperty(key, value);
+ }
+ } else {
+ for (const key of styles.keys) {
+ const value = getDisplayedFormat(formats, key);
+ if (!value) {
+ resta_console.error(
+ 'formatter.setStyleRuleOnInit1: getDisplayFormat is false',
+ );
+ removeStyleRule(styles.cssSelector, key);
+ }
+ resta_console.log('insertRule');
+ if (canInsert) {
+ styleSheet?.insertRule(
+ `${styles.cssSelector}{${key}:${value}}`,
+ styleSheet.cssRules.length,
+ );
+ } else {
+ styleSheet?.addRule(
+ styles.cssSelector,
+ `${key}:${value}`,
+ styleSheet.rules.length,
+ );
+ }
}
}
};
diff --git a/src/features/importStyle.ts b/src/features/importStyle.ts
index 3a75b8f..98aaac4 100644
--- a/src/features/importStyle.ts
+++ b/src/features/importStyle.ts
@@ -1,8 +1,9 @@
import { CompressedStyle } from './style_compresser';
-import * as prop from './prop';
import { error, log } from './resta_console';
import { StyleRule, StyleValue } from './style_sheet';
import { setFormatsAndPushToAry } from './formatter';
+import { IPropsContext } from '../contexts/PropsContext';
+import { matchUrl } from '../utils/urlUtil';
export const importFormat = async (
downloadUrl: string,
@@ -10,21 +11,26 @@ export const importFormat = async (
style: string,
id: string,
imageUrl: string | undefined,
- author: string| undefined
+ author: string | undefined,
+ prop: IPropsContext,
) => {
- if (prop.importedFormat.find((e) => e.id === id)) {
+ if (prop.importedFormats.find((e) => e.id === id)) {
// すでに登録されている場合は取り出す
- prop.setImportedFormat(prop.importedFormat.filter((e) => e.id !== id));
+ prop.setImportedFormats(prop.importedFormats.filter((e) => e.id !== id));
}
- prop.importedFormat.push({
- id, title, downloadUrl, imageUrl, author,
+ prop.importedFormats.push({
+ id,
+ title,
+ downloadUrl,
+ imageUrl,
+ author,
style: JSON.parse(style) as CompressedStyle[],
});
- await chrome.storage.local.set({ imported_style: prop.importedFormat });
+ await chrome.storage.local.set({ imported_style: prop.importedFormats });
};
-export const applyPageFormat = (id: string) => {
- const format = prop.importedFormat.find((e) => e.id === id);
+export const applyPageFormat = (id: string, prop: IPropsContext) => {
+ const format = prop.importedFormats.find((e) => e.id === id);
if (!format) {
error('applyPageFormat', 'format not found');
return;
@@ -44,32 +50,33 @@ export const applyPageFormat = (id: string) => {
values: styleValues,
});
}
- setFormatsAndPushToAry(styleRule);
+ setFormatsAndPushToAry(styleRule, prop);
};
-export const deleteImportedFormat = (id: string) => {
- prop.setImportedFormat(prop.importedFormat.filter((e) => e.id !== id));
- chrome.storage.local.set({ imported_style: prop.importedFormat });
+export const deleteImportedFormat = (id: string, prop: IPropsContext) => {
+ prop.setImportedFormats(prop.importedFormats.filter((e) => e.id !== id));
+ chrome.storage.local.set({ imported_style: prop.importedFormats });
};
-export const deleteAllImportedFormat = () => {
- prop.setImportedFormat([]);
- chrome.storage.local.set({ imported_style: prop.importedFormat });
+export const deleteAllImportedFormat = (prop: IPropsContext) => {
+ prop.setImportedFormats([]);
+ chrome.storage.local.set({ imported_style: prop.importedFormats });
};
export const getImportedFormats = (
+ prop: IPropsContext,
all: boolean = false,
): ImportedFormatAbstract[] => {
- log('getImportedFormats', prop.importedFormat);
+ log('getImportedFormats', prop.importedFormats);
if (all) {
- return prop.importedFormat.map((e) => {
- return {...e};
+ return prop.importedFormats.map((e) => {
+ return { ...e };
});
} else {
- return prop.importedFormat
- .filter((e) => prop.matchUrl(prop.currentUrl, e.style[0].url))
+ return prop.importedFormats
+ .filter((e) => matchUrl(prop.currentUrl, e.style[0].url))
.map((e) => {
- return {...e};
+ return { ...e };
});
}
};
diff --git a/src/features/output_style.ts b/src/features/output_style.ts
index 4699a3d..b2b44f7 100644
--- a/src/features/output_style.ts
+++ b/src/features/output_style.ts
@@ -1,12 +1,10 @@
-import { loadFormatForOutput } from './format_manager';
-import { compressStyle } from './style_compresser';
-
-export const getChangedUrls = async (): Promise => {
- const result = await chrome.storage.local.get(['formats']);
- return JSON.parse(result.formats).map((obj: { url: string }) => obj.url);
-};
-
-export const getFormatByURL = async (url: string) => {
- await loadFormatForOutput();
- return compressStyle(url);
-};
+import { compressStyle } from './style_compresser';
+
+export const getChangedUrls = async (): Promise => {
+ const result = await chrome.storage.local.get(['formats']);
+ return JSON.parse(result.formats).map((obj: { url: string }) => obj.url);
+};
+
+export const getFormatByURL = async (url: string) => {
+ return compressStyle(url);
+};
diff --git a/src/features/prop.ts b/src/features/prop.ts
index fbb751b..49f9b70 100644
--- a/src/features/prop.ts
+++ b/src/features/prop.ts
@@ -1,65 +1,29 @@
-import { Format, FormatBlockByURL } from '../types/Format';
+import { Format } from '../types/Format';
import { removeStyleRule, setStyleRule } from './style_sheet';
import * as resta_console from './resta_console';
import { CompressedStyle } from './style_compresser';
import { ImportedFormatAbstract } from './importStyle';
+import { IPropsContext } from '../contexts/PropsContext';
// 現在のURLを格納する変数
export let currentUrl: string;
export const setUrl = (url: string) => {
const urlObj = new URL(url);
currentUrl = urlObj.origin + urlObj.pathname;
- edittedUrl = currentUrl;
-};
-
-// スタイル変更編集中のURLを格納する変数
-// tsukuba.mast.ac.jp/* などのワイルドカードを含むURLを格納することもできる
-export let edittedUrl: string;
-export const setEdittedUrl = (url: string) => {
- resta_console.log('setEdittedUrl', url);
- edittedUrl = url;
-};
-
-export let formatsArray: Array = [];
-export const setFormatsAry = (ary: Array) => {
- formatsArray = ary;
-};
-export const removeAllFormats = () => {
- formatsArray.splice(0, formatsArray.length);
- resta_console.log('resetFormatsAry', formatsArray);
-};
-
-export const removeCurrentFormat = () => {
- const index = formatsArray.findIndex((x) => x.url === currentUrl);
- if (index !== -1) {
- formatsArray.splice(index, 1);
- }
-};
-
-export const sortFormats = () => {
- setFormatsAry(
- formatsArray
- .filter((e) => e.formats.length !== 0)
- .sort(
- (e) =>
- (e.url.match(/\//g) || []).length &&
- (e.url[e.url.length - 1] === '*' ? -1 : 1),
- ),
- );
};
/**
- * ワイルドカード→完全一致の順にソートし、cssSelectorとcssKeyに対応するスタイルを返す
+ * Formatの配列から、cssKeyに対応するスタイルを返す
* 見つからない場合はfalseを返す
*/
-export const getDisplayFormat = (
- formatsArray: (Format | undefined)[],
+export const getDisplayedFormat = (
+ formats: (Format | undefined)[],
cssKey: string,
): string | false => {
- if (!formatsArray || formatsArray.length === 0) {
+ if (!formats || formats.length === 0) {
return false;
}
- const format: (Format | undefined)[] = formatsArray.filter(
+ const format: (Format | undefined)[] = formats.filter(
(e) => e?.changes.find((l) => l.cssKey === cssKey),
);
const value = format[format.length - 1]?.changes.find(
@@ -75,9 +39,13 @@ export const getDisplayFormat = (
return value.cssValues[value.cssValues.length - 1].cssValue;
};
-export const updateFormat = (cssSelector: string, cssKey: string) => {
- const value = getDisplayFormat(
- formatsArray
+export const updateFormat = (
+ cssSelector: string,
+ cssKey: string,
+ prop: IPropsContext,
+) => {
+ const value = getDisplayedFormat(
+ prop.formatsArray
.map((e) => e.formats)
.filter((e) => e !== undefined)
.map((e) => e.find((e) => e.cssSelector === cssSelector))
@@ -88,10 +56,13 @@ export const updateFormat = (cssSelector: string, cssKey: string) => {
removeStyleRule(cssSelector, cssKey);
return;
}
- setStyleRule({
- cssSelector: cssSelector,
- keys: [cssKey],
- });
+ setStyleRule(
+ {
+ cssSelector: cssSelector,
+ keys: [cssKey],
+ },
+ prop,
+ );
};
export const matchUrl = (currentUrl: string, matchUrl: string) => {
@@ -111,10 +82,6 @@ export const matchUrl = (currentUrl: string, matchUrl: string) => {
return currentUrl === matchUrl;
}
};
-export let importedFormat: Array = [];
-export const setImportedFormat = (ary: Array) => {
- importedFormat = ary;
-};
export type ImportedFormat = {
style: CompressedStyle[];
diff --git a/src/features/root_manager.tsx b/src/features/root_manager.tsx
index be93504..ffe4d8f 100644
--- a/src/features/root_manager.tsx
+++ b/src/features/root_manager.tsx
@@ -1,93 +1,93 @@
-import ReactDOM from 'react-dom';
-import React from 'react';
-import Base from '../components/Base';
-import { ChangeStyleCategoryMap } from '../types/ChangeStyleElement';
-import { setContainerActive } from '..';
-import StyledComponentRegistry from '../components/utils/StyledComponentRegistry';
-
-export const CONTAINER_ID = 'resta-root';
-
-export const initContainer = (categoryMap: ChangeStyleCategoryMap) => {
- const div = document.createElement('div');
- div.setAttribute('id', CONTAINER_ID);
- document.body.insertAdjacentElement('beforeend', div);
-
- ReactDOM.render(
-
-
- ,
- div,
- );
-
- let mouseDowning = false;
- let baseX = 0;
-
- div.addEventListener('mousedown', (e: MouseEvent) => {
- const computedStyle = window.getComputedStyle(div);
- const rect = div.getBoundingClientRect();
- if (computedStyle.right === '0px') {
- if (rect.x <= e.clientX && e.clientX <= rect.x + 3) {
- mouseDowning = true;
- baseX = e.clientX;
- }
- } else {
- if (
- rect.x + rect.width <= e.clientX + 3 &&
- e.clientX + 3 <= rect.x + rect.width + 3
- ) {
- mouseDowning = true;
- baseX = e.clientX;
- }
- }
- });
-
- document.addEventListener('mousemove', (e: MouseEvent) => {
- if (mouseDowning) {
- const computedStyle = window.getComputedStyle(div);
- const rect = div.getBoundingClientRect();
- div.style.width =
- computedStyle.right === '0px'
- ? `${rect.width + (baseX - e.clientX)}px`
- : `${rect.width + (e.clientX - baseX)}px`;
- baseX = e.clientX;
- }
- });
-
- document.addEventListener('mouseup', () => {
- if (mouseDowning) {
- mouseDowning = false;
- }
- });
-};
-
-export const closeContainer = () => {
- const root = document.getElementById(CONTAINER_ID);
- if (!root) throw new Error(`#${CONTAINER_ID} is null!`);
-
- root.style.animationName = 'closeAnimation';
- root.style.animationDuration = '0.25s';
- root.addEventListener(
- 'animationend',
- () => {
- root.remove();
- },
- { once: true },
- );
- setContainerActive(false);
-};
-
-export const toggleContainerPosition = (): boolean => {
- const root = document.getElementById(CONTAINER_ID);
- if (!root) throw new Error(`#${CONTAINER_ID} is null!`);
-
- const computedStyle = getComputedStyle(root);
- if (computedStyle.right === '0px') {
- root.style.left = '0';
- root.style.right = 'unset';
- return false;
- } else {
- root.style.right = '0';
- root.style.left = 'unset';
- return true;
- }
-};
+import ReactDOM from 'react-dom';
+import React from 'react';
+import Base from '../components/Base';
+import { ChangeStyleCategoryMap } from '../types/ChangeStyleElement';
+import { setContainerActive } from '..';
+import StyledComponentRegistry from '../components/utils/StyledComponentRegistry';
+
+export const CONTAINER_ID = 'resta-root';
+
+export const initContainer = (categoryMap: ChangeStyleCategoryMap) => {
+ const div = document.createElement('div');
+ div.setAttribute('id', CONTAINER_ID);
+ document.body.insertAdjacentElement('beforeend', div);
+
+ ReactDOM.render(
+
+
+ ,
+ div,
+ );
+
+ let mouseDowning = false;
+ let baseX = 0;
+
+ div.addEventListener('mousedown', (e: MouseEvent) => {
+ const computedStyle = window.getComputedStyle(div);
+ const rect = div.getBoundingClientRect();
+ if (computedStyle.right === '0px') {
+ if (rect.x <= e.clientX && e.clientX <= rect.x + 3) {
+ mouseDowning = true;
+ baseX = e.clientX;
+ }
+ } else {
+ if (
+ rect.x + rect.width <= e.clientX + 3 &&
+ e.clientX + 3 <= rect.x + rect.width + 3
+ ) {
+ mouseDowning = true;
+ baseX = e.clientX;
+ }
+ }
+ });
+
+ document.addEventListener('mousemove', (e: MouseEvent) => {
+ if (mouseDowning) {
+ const computedStyle = window.getComputedStyle(div);
+ const rect = div.getBoundingClientRect();
+ div.style.width =
+ computedStyle.right === '0px'
+ ? `${rect.width + (baseX - e.clientX)}px`
+ : `${rect.width + (e.clientX - baseX)}px`;
+ baseX = e.clientX;
+ }
+ });
+
+ document.addEventListener('mouseup', () => {
+ if (mouseDowning) {
+ mouseDowning = false;
+ }
+ });
+};
+
+export const closeContainer = () => {
+ const root = document.getElementById(CONTAINER_ID);
+ if (!root) throw new Error(`#${CONTAINER_ID} is null!`);
+
+ root.style.animationName = 'closeAnimation';
+ root.style.animationDuration = '0.25s';
+ root.addEventListener(
+ 'animationend',
+ () => {
+ root.remove();
+ },
+ { once: true },
+ );
+ setContainerActive(false);
+};
+
+export const toggleContainerPosition = (): boolean => {
+ const root = document.getElementById(CONTAINER_ID);
+ if (!root) throw new Error(`#${CONTAINER_ID} is null!`);
+
+ const computedStyle = getComputedStyle(root);
+ if (computedStyle.right === '0px') {
+ root.style.left = '0';
+ root.style.right = 'unset';
+ return false;
+ } else {
+ root.style.right = '0';
+ root.style.left = 'unset';
+ return true;
+ }
+};
diff --git a/src/features/style_compresser.ts b/src/features/style_compresser.ts
index 1aaf85b..cd6aaec 100644
--- a/src/features/style_compresser.ts
+++ b/src/features/style_compresser.ts
@@ -1,18 +1,24 @@
import * as prop from './prop';
-import { FormatChange } from '../types/Format';
+import { FormatBlockByURL, FormatChange } from '../types/Format';
+import { getFormatAryFromLocal } from './format_manager';
export const compressStyle = (url: string): CompressedStyle | false => {
const compressedFormats: CompressedFormat[] = [];
// 優先度の高い順にルールを追加する
// すでに登録されている場合はスキップする
- for (const format of prop.formatsArray
- .filter((e) => {
- return prop.matchUrl(url, e.url);
- })
- .reverse()) {
- for (const f of format.formats) {
- insertStyleRule(f.cssSelector, f.changes, compressedFormats);
+ getFormatAryFromLocal().then((result) => {
+ if (!result) {
+ return;
}
- }
+ for (const format of result
+ .filter((e: FormatBlockByURL) => {
+ return prop.matchUrl(url, e.url);
+ })
+ .reverse()) {
+ for (const f of format.formats) {
+ insertStyleRule(f.cssSelector, f.changes, compressedFormats);
+ }
+ }
+ });
if (compressedFormats.length === 0) {
return false;
}
diff --git a/src/features/style_layer.ts b/src/features/style_layer.ts
index 7ab6f2c..250ecd4 100644
--- a/src/features/style_layer.ts
+++ b/src/features/style_layer.ts
@@ -1,7 +1,10 @@
+import { IPropsContext } from '../contexts/PropsContext';
import { StyleLayer, StyleLayerValue } from '../types/StyleLayer';
-import * as prop from './prop';
import * as resta_console from './resta_console';
-export const getStyleLayer = (cssSelector: string): StyleLayer => {
+export const getStyleLayer = (
+ cssSelector: string,
+ prop: IPropsContext,
+): StyleLayer => {
if (!cssSelector) {
return { key: '', children: [] };
}
diff --git a/src/features/style_sheet.ts b/src/features/style_sheet.ts
index 6866a96..59ed8a9 100644
--- a/src/features/style_sheet.ts
+++ b/src/features/style_sheet.ts
@@ -1,13 +1,12 @@
-import { getDisplayFormat } from './prop';
+import { getDisplayedFormat } from './prop';
import * as resta_console from './resta_console';
-import * as prop from './prop';
+import { IPropsContext } from '../contexts/PropsContext';
let styleSheet: CSSStyleSheet | null = null;
/**
* CSSStyleSheetを取得する
* シングルトンのような構造になっている
*/
-
export const getStyleSheet = () => {
if (styleSheet == null) {
const styleSheetElement = document.createElement('style');
@@ -21,15 +20,20 @@ export const getStyleSheet = () => {
* CssSelectorとcssKeyの配列を渡すと、最新のスタイルを適用する
* valueはいらない
*/
-export const setStyleRule = (styles: {
- cssSelector: string;
- keys: Array;
-}) => {
+export const setStyleRule = (
+ styles: {
+ cssSelector: string;
+ keys: Array;
+ },
+ prop: IPropsContext,
+) => {
if (!styles.keys || !styles.cssSelector) {
resta_console.log('setStyleRule: invalid value');
return;
}
const styleSheet = getStyleSheet();
+ // insertRuleが使えるかどうか
+ // 使えない場合、つまり古いバージョンのChromeの場合はaddRuleを使う
const canInsert = styleSheet.insertRule as
| ((rule: string, index?: number) => number)
| undefined;
@@ -39,16 +43,23 @@ export const setStyleRule = (styles: {
.map((e) => e.find((e) => e.cssSelector === styles.cssSelector))
.filter((e) => e !== undefined);
+ if (!formats || formats.length === 0) {
+ resta_console.error('style_sheet.setStyleRule: formats is empty');
+ return;
+ }
+
const rule = Array.from(styleSheet?.cssRules).find(
(e) => e instanceof CSSStyleRule && e.selectorText === styles.cssSelector,
) as CSSStyleRule | undefined;
if (rule) {
for (const key of styles.keys) {
- const value = getDisplayFormat(formats, key);
+ const value = getDisplayedFormat(formats, key);
if (!value) {
resta_console.error(
'style_sheet.setStyleRule0: getDisplayFormat is false',
+ formats,
+ key,
);
removeStyleRule(styles.cssSelector, key);
continue;
@@ -59,12 +70,15 @@ export const setStyleRule = (styles: {
}
} else {
for (const key of styles.keys) {
- const value = getDisplayFormat(formats, key);
+ const value = getDisplayedFormat(formats, key);
if (!value) {
resta_console.error(
'style_sheet.setStyleRule1: getDisplayFormat is false',
+ formats,
+ key,
);
removeStyleRule(styles.cssSelector, key);
+ continue;
}
resta_console.log('insertRule');
if (canInsert) {
diff --git a/src/features/unredo.ts b/src/features/unredo.ts
deleted file mode 100644
index b5136ed..0000000
--- a/src/features/unredo.ts
+++ /dev/null
@@ -1,204 +0,0 @@
-import {
- UnRedoCommands,
- UnRedoCommand,
- Command,
-} from '../types/UnRedoCommands';
-import { saveFormat } from './format_manager';
-import { deleteFromAry, pushToAry } from './formatter';
-import { updateFormat } from './prop';
-import * as resta_console from './resta_console';
-
-const UNREDO_MAX_LENGTH = 32;
-const unRedoStack: Array = [];
-
-let index: number = 0;
-
-export const reDo = (): void => {
- if (!canRedo()) {
- return;
- }
- applyRedoCommands(unRedoStack[index++]);
-};
-
-export const unDo = (): void => {
- if (!canUndo()) {
- return;
- }
- applyUndoCommands(unRedoStack[--index]);
-};
-
-export const canRedo = (): boolean => {
- return index < unRedoStack.length;
-};
-
-export const canUndo = (): boolean => {
- return index > 0;
-};
-
-/**
- * undoStackをリセットする
- * サイトの移動時などに呼び出す
- */
-export const resetUndoStack = () => {
- unRedoStack.splice(0, unRedoStack.length);
- index = 0;
-};
-
-export const pushLog = (changes: UnRedoCommands) => {
- if (changes.commands.length === 0) {
- return;
- }
- // すでに変更がある場合は、そこから先のRedo用の変更を削除する
- if (index < unRedoStack.length) {
- unRedoStack.splice(index, unRedoStack.length - index);
- }
- const lastChanges = unRedoStack[unRedoStack.length - 1];
- // 直前の変更と同じidの場合は、直前の変更とマージする
- if (
- lastChanges &&
- lastChanges.commands[0] &&
- lastChanges.commands[0].id !== 0 &&
- lastChanges.commands[0].id === changes.commands[0].id &&
- lastChanges.commands[0].redo.type !== 'delete' &&
- changes.commands[0].redo.type !== 'delete' &&
- lastChanges.commands
- .map((x) => x.cssSelector)
- .sort()
- .toString() ===
- changes.commands
- .map((x) => x.cssSelector)
- .sort()
- .toString()
- ) {
- unRedoStack.pop();
- unRedoStack.push(margeCommands(lastChanges, changes));
- } else {
- // マージの必要がない場合は、そのままundoStackに追加する
- unRedoStack.push(changes);
- }
-
- // undoLengthを超えた場合は先頭を削除する
- if (unRedoStack.length > UNREDO_MAX_LENGTH) {
- unRedoStack.shift();
- }
- index = unRedoStack.length;
- resta_console.log('pushLog', unRedoStack);
- saveFormat();
-};
-
-/**
- * 作業を取り消すような変更の種類
- */
-export type ChangeType = 'create' | 'delete' | 'rewrite';
-
-const margeCommands = (
- prev: UnRedoCommands,
- next: UnRedoCommands,
-): UnRedoCommands => {
- const prevCommands = prev.commands;
- const nextCommands = next.commands;
- const margedCommands: Array = [];
- for (const prevCommand of prevCommands) {
- const nextCommandIndex = nextCommands
- .filter((e) => e.id === prevCommand.id)
- .findIndex(
- (e) =>
- e.cssSelector === prevCommand.cssSelector &&
- e.cssKey === prevCommand.cssKey,
- );
- if (nextCommandIndex === -1) {
- // nextCommandsにprevCommandと同じidの変更がない場合は、prevCommandをそのまま追加する
- margedCommands.push(prevCommand);
- continue;
- }
- const nextCommand = nextCommands.splice(nextCommandIndex, 1)[0];
- switch (prevCommand.redo.type) {
- case 'create':
- if (nextCommand.redo.type === 'rewrite') {
- margedCommands.push({
- cssSelector: prevCommand.cssSelector,
- cssKey: prevCommand.cssKey,
- id: prevCommand.id,
- undo: {
- type: 'delete',
- cssValue: '',
- index: undefined,
- },
- redo: {
- type: 'create',
- cssValue: nextCommand.redo.cssValue,
- index: nextCommand.redo.index,
- },
- });
- } else {
- resta_console.log('margeCommands: bug detected, create -> create');
- return { commands: [] };
- }
- break;
- case 'rewrite':
- if (nextCommand.redo.type === 'rewrite') {
- margedCommands.push({
- cssSelector: prevCommand.cssSelector,
- cssKey: prevCommand.cssKey,
- id: prevCommand.id,
- undo: {
- type: 'rewrite',
- cssValue: prevCommand.undo.cssValue,
- index: prevCommand.undo.index,
- },
- redo: {
- type: 'rewrite',
- cssValue: nextCommand.redo.cssValue,
- index: nextCommand.redo.index,
- },
- });
- } else {
- resta_console.log('margeCommands: bug detected, rewrite -> create');
- return { commands: [] };
- }
- break;
- default:
- resta_console.log('margeCommands: bug detected, try to marge delete');
- return { commands: [] };
- }
- }
- nextCommands.forEach((element) => {
- margedCommands.push(element);
- });
- return { commands: margedCommands } as UnRedoCommands;
-};
-
-const applyUndoCommands = (changes: UnRedoCommands) => {
- for (const command of changes.commands) {
- applyCommand(command.cssSelector, command.cssKey, command.id, command.undo);
- }
- saveFormat();
-};
-
-const applyRedoCommands = (changes: UnRedoCommands) => {
- for (const command of changes.commands) {
- applyCommand(command.cssSelector, command.cssKey, command.id, command.redo);
- }
- saveFormat();
-};
-
-export const applyCommand = (
- cssSelector: string,
- cssKey: string,
- id: number | string,
- change: Command,
-) => {
- resta_console.log('applyCommand', cssSelector, cssKey, id, change);
- switch (change.type) {
- case 'create':
- pushToAry(cssSelector, cssKey, change.cssValue, id);
- break;
- case 'delete':
- deleteFromAry(cssSelector, cssKey, id);
- break;
- case 'rewrite':
- pushToAry(cssSelector, cssKey, change.cssValue, id);
- break;
- }
- updateFormat(cssSelector, cssKey);
-};
diff --git a/src/features/upload_import_manager.tsx b/src/features/upload_import_manager.tsx
index 6f51a82..e330f62 100644
--- a/src/features/upload_import_manager.tsx
+++ b/src/features/upload_import_manager.tsx
@@ -1,112 +1,117 @@
-import { getFormatByURL } from './output_style';
-import { ImportedFormatAbstract, importFormat } from './importStyle';
-import React from 'react';
-import ReactDOM from 'react-dom';
-import StyledComponentRegistry from '../components/utils/StyledComponentRegistry';
-import StyleSelectionDialogRoot from '../components/utils/StyleSelectionDialogRoot';
-import StyleDownloader from '../components/utils/StyleDownloader';
-import { DEBUG_MODE } from '../consts/debug';
-import { error } from './resta_console';
-
-const HOST = 'resta-frontend.pages.dev';
-export const DOWNLOAD_PAGE_URL = `https://${HOST}/style`;
-
-// アップロードページ
-export const ID_ADD_STYLE_BUTTON = 'resta-add-style';
-
-// ダウンロードページ
-export const ID_DOWNLOAD_STYLE_BUTTON = 'resta-add-style';
-export const ID_FORMAT_TITLE = 'resta-style-title';
-export const ID_FORMAT_JSON_INPUT = 'resta-style-json';
-export const ID_FORMAT_ID_INPUT = 'resta-style-id';
-export const ID_FORMAT_IMAGE = 'resta-style-image';
-export const ID_FORMAT_AUTHOR_INPUT = 'resta-style-author';
-
-const enableButton = (id: string, onClick: VoidFunction) => {
- const mutationObserver = new MutationObserver(() => {
- const addButton = document.getElementById(id);
- if (addButton) {
- let newAddButton = addButton.cloneNode(true) as HTMLButtonElement;
- addButton.parentNode!.replaceChild(newAddButton, addButton);
-
- newAddButton.style.display = 'block';
- newAddButton.addEventListener('click', onClick);
- mutationObserver.disconnect();
- }
- });
-
- mutationObserver.observe(document.getElementById('__next')!, {
- childList: true,
- subtree: true,
- });
-};
-
-const getValue = (id: string) => {
- return (document.getElementById(id) as HTMLInputElement).value;
-};
-
-export const downloadFormat = async (): Promise => {
- const title = document.getElementById(ID_FORMAT_TITLE)?.innerText;
- const json = getValue(ID_FORMAT_JSON_INPUT);
- const id = getValue(ID_FORMAT_ID_INPUT);
- const imageUrl = document.getElementById(ID_FORMAT_IMAGE)?.getAttribute('src') ?? undefined;
- const downloadUrl = `${DOWNLOAD_PAGE_URL}/${id}`;
- const author = document.getElementById(ID_FORMAT_AUTHOR_INPUT)?.innerText;
-
- if (!title || !json || !id || !downloadUrl) {
- error("error: ", title, json, id);
- return undefined;
- }
-
- await importFormat(downloadUrl, title, json, id, imageUrl, author);
-
- return {
- title,
- id,
- downloadUrl,
- imageUrl,
- author,
- };
-};
-
-export const enableRestaAddStyleButton = (onClick: VoidFunction) => {
- enableButton(ID_ADD_STYLE_BUTTON, onClick);
-};
-
-export const enableRestaDownloadStyleButton = (onClick: () => void) => {
- enableButton(ID_DOWNLOAD_STYLE_BUTTON, onClick);
-};
-
-export const injectStyleJson = async (url: string) => {
- (document.getElementById('resta-style-json') as HTMLInputElement).value =
- JSON.stringify(await getFormatByURL(url));
-};
-
-export const activateRestaSubsystems = () => {
- const targetHosts = DEBUG_MODE ? [HOST, 'localhost'] : [HOST];
- const url = new URL(window.location.href);
-
- if (targetHosts.includes(url.hostname)) {
- document.getElementById('resta-subsystem-root')?.remove();
-
- const insertComponent = (component: React.ReactNode) => {
- const div = document.createElement('div');
- div.setAttribute('id', 'resta-subsystem-root');
- document.body.insertAdjacentElement('beforeend', div);
-
- ReactDOM.render(
- {component},
- div,
- );
- };
-
- // Restaのアップロードページなら
- if (url.pathname.match(/^\/upload\/$/)) {
- insertComponent();
-
- // Restaのダウンロードページなら
- } else if (url.pathname.match(/^\/style\/.+\/$/)) {
- insertComponent();
- }
- }
-};
+import { getFormatByURL } from './output_style';
+import { ImportedFormatAbstract, importFormat } from './importStyle';
+import React, { useContext } from 'react';
+import ReactDOM from 'react-dom';
+import StyledComponentRegistry from '../components/utils/StyledComponentRegistry';
+import StyleSelectionDialogRoot from '../components/utils/StyleSelectionDialogRoot';
+import StyleDownloader from '../components/utils/StyleDownloader';
+import { DEBUG_MODE } from '../consts/debug';
+import { error } from './resta_console';
+import { PropsContext } from '../contexts/PropsContext';
+
+const HOST = 'resta-frontend.pages.dev';
+export const DOWNLOAD_PAGE_URL = `https://${HOST}/style`;
+
+// アップロードページ
+export const ID_ADD_STYLE_BUTTON = 'resta-add-style';
+
+// ダウンロードページ
+export const ID_DOWNLOAD_STYLE_BUTTON = 'resta-add-style';
+export const ID_FORMAT_TITLE = 'resta-style-title';
+export const ID_FORMAT_JSON_INPUT = 'resta-style-json';
+export const ID_FORMAT_ID_INPUT = 'resta-style-id';
+export const ID_FORMAT_IMAGE = 'resta-style-image';
+export const ID_FORMAT_AUTHOR_INPUT = 'resta-style-author';
+
+const enableButton = (id: string, onClick: VoidFunction) => {
+ const mutationObserver = new MutationObserver(() => {
+ const addButton = document.getElementById(id);
+ if (addButton) {
+ let newAddButton = addButton.cloneNode(true) as HTMLButtonElement;
+ addButton.parentNode!.replaceChild(newAddButton, addButton);
+
+ newAddButton.style.display = 'block';
+ newAddButton.addEventListener('click', onClick);
+ mutationObserver.disconnect();
+ }
+ });
+
+ mutationObserver.observe(document.getElementById('__next')!, {
+ childList: true,
+ subtree: true,
+ });
+};
+
+const getValue = (id: string) => {
+ return (document.getElementById(id) as HTMLInputElement).value;
+};
+
+export const downloadFormat = async (): Promise<
+ ImportedFormatAbstract | undefined
+> => {
+ const title = document.getElementById(ID_FORMAT_TITLE)?.innerText;
+ const json = getValue(ID_FORMAT_JSON_INPUT);
+ const id = getValue(ID_FORMAT_ID_INPUT);
+ const imageUrl =
+ document.getElementById(ID_FORMAT_IMAGE)?.getAttribute('src') ?? undefined;
+ const downloadUrl = `${DOWNLOAD_PAGE_URL}/${id}`;
+ const author = document.getElementById(ID_FORMAT_AUTHOR_INPUT)?.innerText;
+ const prop = useContext(PropsContext);
+
+ if (!title || !json || !id || !downloadUrl) {
+ error('error: ', title, json, id);
+ return undefined;
+ }
+
+ await importFormat(downloadUrl, title, json, id, imageUrl, author, prop);
+
+ return {
+ title,
+ id,
+ downloadUrl,
+ imageUrl,
+ author,
+ };
+};
+
+export const enableRestaAddStyleButton = (onClick: VoidFunction) => {
+ enableButton(ID_ADD_STYLE_BUTTON, onClick);
+};
+
+export const enableRestaDownloadStyleButton = (onClick: () => void) => {
+ enableButton(ID_DOWNLOAD_STYLE_BUTTON, onClick);
+};
+
+export const injectStyleJson = async (url: string) => {
+ (document.getElementById('resta-style-json') as HTMLInputElement).value =
+ JSON.stringify(await getFormatByURL(url));
+};
+
+export const activateRestaSubsystems = () => {
+ const targetHosts = DEBUG_MODE ? [HOST, 'localhost'] : [HOST];
+ const url = new URL(window.location.href);
+
+ if (targetHosts.includes(url.hostname)) {
+ document.getElementById('resta-subsystem-root')?.remove();
+
+ const insertComponent = (component: React.ReactNode) => {
+ const div = document.createElement('div');
+ div.setAttribute('id', 'resta-subsystem-root');
+ document.body.insertAdjacentElement('beforeend', div);
+
+ ReactDOM.render(
+ {component},
+ div,
+ );
+ };
+
+ // Restaのアップロードページなら
+ if (url.pathname.match(/^\/upload\/$/)) {
+ insertComponent();
+
+ // Restaのダウンロードページなら
+ } else if (url.pathname.match(/^\/style\/.+\/$/)) {
+ insertComponent();
+ }
+ }
+};
diff --git a/src/hooks/useFormatUtils.ts b/src/hooks/useFormatUtils.ts
index afc51d8..12b10c0 100644
--- a/src/hooks/useFormatUtils.ts
+++ b/src/hooks/useFormatUtils.ts
@@ -1,29 +1,37 @@
import { useCallback, useContext } from 'react';
-import { PropsContext } from '../contexts/PropsContext';
import { Format } from '../types/Format';
import * as resta_console from '../features/resta_console';
import { removeStyleRule, setStyleRule } from '../features/style_sheet';
+import { PropsContext } from '../contexts/PropsContext';
type ReturnType = {
removeAllFormats: VoidFunction;
removeCurrentFormat: VoidFunction;
sortFormats: VoidFunction;
- getDisplayFormat: (formatsArray: (Format | undefined)[], cssKey: string) => string | undefined;
+ getDisplayFormat: (
+ formatsArray: (Format | undefined)[],
+ cssKey: string,
+ ) => string | undefined;
updateFormat: (cssSelector: string, cssKey: string) => void;
matchUrl: (currentUrl: string, matchUrl: string) => boolean;
-}
+};
export default function useFormatUtils(): ReturnType {
const props = useContext(PropsContext);
const removeAllFormats = useCallback(() => {
- const newFormatsArray = props.formatsArray.splice(0, props.formatsArray.length);
+ const newFormatsArray = props.formatsArray.splice(
+ 0,
+ props.formatsArray.length,
+ );
props.setFormatsArray(newFormatsArray);
resta_console.log('resetFormatsAry', newFormatsArray);
}, [props.formatsArray]);
const removeCurrentFormat = useCallback(() => {
- const index = props.formatsArray.findIndex((x) => x.url === props.currentUrl);
+ const index = props.formatsArray.findIndex(
+ (x) => x.url === props.currentUrl,
+ );
if (index !== -1) {
const newFormatsArray = props.formatsArray.splice(index, 1);
props.setFormatsArray(newFormatsArray);
@@ -42,64 +50,81 @@ export default function useFormatUtils(): ReturnType {
);
}, [props.formatsArray]);
- const getDisplayFormat = useCallback((formatsArray: (Format | undefined)[], cssKey: string) => {
- if (!formatsArray || formatsArray.length === 0) {
- return undefined;
- }
- const format: (Format | undefined)[] = formatsArray.filter(
- (e) => e?.changes.find((l) => l.cssKey === cssKey),
- );
- const value = format[format.length - 1]?.changes.find(
- (e) => e.cssKey === cssKey,
- );
- if (!value || value.cssValues.length === 0) {
- return undefined;
- }
- resta_console.log(
- 'getDisplayFormat',
- value.cssValues[value.cssValues.length - 1].cssValue,
- );
- return value.cssValues[value.cssValues.length - 1].cssValue;
- }, [props.formatsArray]);
+ const getDisplayFormat = useCallback(
+ (formatsArray: (Format | undefined)[], cssKey: string) => {
+ if (!formatsArray || formatsArray.length === 0) {
+ return undefined;
+ }
+ const format: (Format | undefined)[] = formatsArray.filter(
+ (e) => e?.changes.find((l) => l.cssKey === cssKey),
+ );
+ const value = format[format.length - 1]?.changes.find(
+ (e) => e.cssKey === cssKey,
+ );
+ if (!value || value.cssValues.length === 0) {
+ return undefined;
+ }
+ resta_console.log(
+ 'getDisplayFormat',
+ value.cssValues[value.cssValues.length - 1].cssValue,
+ );
+ return value.cssValues[value.cssValues.length - 1].cssValue;
+ },
+ [props.formatsArray],
+ );
- const updateFormat = useCallback((cssSelector: string, cssKey: string) => {
- const value = getDisplayFormat(
- props.formatsArray
- .map((e) => e.formats)
- .filter((e) => e !== undefined)
- .map((e) => e.find((e) => e.cssSelector === cssSelector))
- .filter((e) => e !== undefined),
- cssKey,
- );
- if (!value) {
- removeStyleRule(cssSelector, cssKey);
- return;
- }
- setStyleRule({
- cssSelector: cssSelector,
- keys: [cssKey],
- });
- }, [props.formatsArray]);
+ const updateFormat = useCallback(
+ (cssSelector: string, cssKey: string) => {
+ const value = getDisplayFormat(
+ props.formatsArray
+ .map((e) => e.formats)
+ .filter((e) => e !== undefined)
+ .map((e) => e.find((e) => e.cssSelector === cssSelector))
+ .filter((e) => e !== undefined),
+ cssKey,
+ );
+ if (!value) {
+ removeStyleRule(cssSelector, cssKey);
+ return;
+ }
+ setStyleRule(
+ {
+ cssSelector: cssSelector,
+ keys: [cssKey],
+ },
+ props,
+ );
+ },
+ [props.formatsArray],
+ );
- const matchUrl = useCallback((currentUrl: string, matchUrl: string) => {
- if (!matchUrl || !currentUrl) {
- return false;
- }
- let hasWildcard = false;
- let compareUrl = '';
- // 最後の文字が*ならワイルドカードとして扱う
- if (matchUrl[matchUrl.length - 1] === '*') {
- hasWildcard = true;
- compareUrl = matchUrl.slice(0, -1);
- }
- if (hasWildcard) {
- return currentUrl === compareUrl || currentUrl.startsWith(compareUrl);
- } else {
- return currentUrl === matchUrl;
- }
- }, [props.formatsArray]);
+ const matchUrl = useCallback(
+ (currentUrl: string, matchUrl: string) => {
+ if (!matchUrl || !currentUrl) {
+ return false;
+ }
+ let hasWildcard = false;
+ let compareUrl = '';
+ // 最後の文字が*ならワイルドカードとして扱う
+ if (matchUrl[matchUrl.length - 1] === '*') {
+ hasWildcard = true;
+ compareUrl = matchUrl.slice(0, -1);
+ }
+ if (hasWildcard) {
+ return currentUrl === compareUrl || currentUrl.startsWith(compareUrl);
+ } else {
+ return currentUrl === matchUrl;
+ }
+ },
+ [props.formatsArray],
+ );
return {
- removeAllFormats, removeCurrentFormat, sortFormats, getDisplayFormat, updateFormat, matchUrl
- }
-}
\ No newline at end of file
+ removeAllFormats,
+ removeCurrentFormat,
+ sortFormats,
+ getDisplayFormat,
+ updateFormat,
+ matchUrl,
+ };
+}
diff --git a/src/index.ts b/src/index.ts
index a91526a..7c89af7 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -1,54 +1,51 @@
-import * as prop from './features/prop';
-import { initStyle } from './features/formatter';
-import loadRestaSetting from './features/setting_loader';
-import { initContainer } from './features/root_manager';
-import * as resta_console from './features/resta_console';
-import { loadFormat, loadImportedStyle } from './features/format_manager';
-import { activateRestaSubsystems } from './features/upload_import_manager';
-
-export let isContainerActive: boolean = false;
-export const setContainerActive = (value: boolean) => {
- isContainerActive = value;
-};
-
-window.addEventListener('load', () => {
- resta_console.log('OnUpdated');
-
- prop.setUrl(window.location.href);
-
- (async () => {
- resta_console.log('Init Style');
- await initStyle();
- })();
-});
-
-const activateContainer = () => {
- if (isContainerActive) return;
- resta_console.log('Load Resta Setting');
- loadRestaSetting().then((categoryMap) => {
- initContainer(categoryMap);
- });
-};
-
-chrome.runtime.onMessage.addListener((req) => {
- console.log('req', req);
- if (req.type === 'url') {
- activateContainer();
- isContainerActive = true;
- } else if (req.type === 'activate') {
- loadFormat();
- loadImportedStyle();
- resta_console.log('Activate');
- }
-});
-
-const target = document.querySelector('body');
-const observer = new MutationObserver(() => {
- resta_console.log('OnUpdated');
- initStyle();
-});
-observer.observe(target!, {
- childList: true,
-});
-
-(window as any).activateRestaSubsystems = activateRestaSubsystems;
+import * as prop from './features/prop';
+import { initStyle } from './features/formatter';
+import loadRestaSetting from './features/setting_loader';
+import { initContainer } from './features/root_manager';
+import * as resta_console from './features/resta_console';
+import { activateRestaSubsystems } from './features/upload_import_manager';
+
+export let isContainerActive: boolean = false;
+export const setContainerActive = (value: boolean) => {
+ isContainerActive = value;
+};
+
+window.addEventListener('load', () => {
+ resta_console.log('OnUpdated');
+
+ prop.setUrl(window.location.href);
+
+ (async () => {
+ resta_console.log('Init Style');
+ await initStyle();
+ })();
+});
+
+const activateContainer = () => {
+ if (isContainerActive) return;
+ resta_console.log('Load Resta Setting');
+ loadRestaSetting().then((categoryMap) => {
+ initContainer(categoryMap);
+ });
+};
+
+chrome.runtime.onMessage.addListener((req) => {
+ console.log('req', req);
+ if (req.type === 'url') {
+ activateContainer();
+ isContainerActive = true;
+ } else if (req.type === 'activate') {
+ resta_console.log('Activate');
+ }
+});
+
+const target = document.querySelector('body');
+const observer = new MutationObserver(() => {
+ resta_console.log('OnUpdated');
+ initStyle();
+});
+observer.observe(target!, {
+ childList: true,
+});
+
+(window as any).activateRestaSubsystems = activateRestaSubsystems;
diff --git a/src/types/UnRedoCommands.ts b/src/types/UnRedoCommands.ts
deleted file mode 100644
index a623e40..0000000
--- a/src/types/UnRedoCommands.ts
+++ /dev/null
@@ -1,22 +0,0 @@
-import { ChangeType } from '../features/unredo';
-
-export type UnRedoCommands = {
- commands: Array;
-};
-
-export type UnRedoCommand = {
- cssSelector: string;
- cssKey: string;
- id: number | string;
- undo: Command;
- redo: Command;
-};
-
-export type Command = {
- type: ChangeType;
- cssValue: string;
- /**
- * create, rewriteのときのみ。undefinedのときは末尾に追加
- */
- index: number | undefined;
-};
diff --git a/src/utils/urlUtil.ts b/src/utils/urlUtil.ts
new file mode 100644
index 0000000..e4db329
--- /dev/null
+++ b/src/utils/urlUtil.ts
@@ -0,0 +1,17 @@
+export const matchUrl = (currentUrl: string, matchUrl: string) => {
+ if (!matchUrl || !currentUrl) {
+ return false;
+ }
+ let hasWildcard = false;
+ let compareUrl = '';
+ // 最後の文字が*ならワイルドカードとして扱う
+ if (matchUrl[matchUrl.length - 1] === '*') {
+ hasWildcard = true;
+ compareUrl = matchUrl.slice(0, -1);
+ }
+ if (hasWildcard) {
+ return currentUrl === compareUrl || currentUrl.startsWith(compareUrl);
+ } else {
+ return currentUrl === matchUrl;
+ }
+};