mirror of https://gitee.com/answerdev/answer.git
Merge branch 'feat/ui-0.7.0' into 'test'
Feat/ui 0.7.0 See merge request opensource/answer!332
This commit is contained in:
commit
ed242e7569
|
@ -1127,7 +1127,7 @@ ui:
|
|||
fields:
|
||||
display_name:
|
||||
label: Display Name
|
||||
msg: display_name must be at maximum 30 characters in length.
|
||||
msg: display_name must be at 4 - 30 characters in length.
|
||||
email:
|
||||
label: Email
|
||||
msg: Email is not valid.
|
||||
|
|
|
@ -321,6 +321,7 @@ export interface SiteSettings {
|
|||
general: AdminSettingsGeneral;
|
||||
interface: AdminSettingsInterface;
|
||||
login: AdminSettingsLogin;
|
||||
custom_css_html: AdminSettingsCustom;
|
||||
}
|
||||
|
||||
export interface AdminSettingBranding {
|
||||
|
|
|
@ -0,0 +1,84 @@
|
|||
import { FC, memo, useEffect } from 'react';
|
||||
|
||||
import { customizeStore } from '@/stores';
|
||||
|
||||
const getElementByAttr = (attr: string, elName: string) => {
|
||||
let el = document.querySelector(`[${attr}]`);
|
||||
if (!el) {
|
||||
el = document.createElement(elName);
|
||||
el.setAttribute(attr, '');
|
||||
}
|
||||
return el;
|
||||
};
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const textToDf = (t) => {
|
||||
const dummyDoc = document.createElement('div');
|
||||
dummyDoc.innerHTML = t;
|
||||
const frag = document.createDocumentFragment();
|
||||
while (dummyDoc.childNodes.length) {
|
||||
frag.appendChild(dummyDoc.children[0]);
|
||||
}
|
||||
return frag;
|
||||
};
|
||||
|
||||
const injectCustomCSS = (t: string) => {
|
||||
if (!t) {
|
||||
return;
|
||||
}
|
||||
const el = getElementByAttr('data-custom-css', 'style');
|
||||
el.textContent = t;
|
||||
document.head.insertBefore(el, document.head.lastChild);
|
||||
};
|
||||
|
||||
const injectCustomHead = (t: string) => {
|
||||
if (!t) {
|
||||
return;
|
||||
}
|
||||
setTimeout(() => {
|
||||
const el = getElementByAttr('data-custom-head', 'style');
|
||||
el.textContent = t;
|
||||
document.head.appendChild(el);
|
||||
}, 200);
|
||||
};
|
||||
|
||||
const injectCustomHeader = (t: string) => {
|
||||
if (!t) {
|
||||
return;
|
||||
}
|
||||
// const frag = textToDf(t);
|
||||
t = ' Customize Header ';
|
||||
document.body.insertBefore(
|
||||
document.createComment(t),
|
||||
document.body.firstChild,
|
||||
);
|
||||
};
|
||||
|
||||
const injectCustomFooter = (t: string) => {
|
||||
if (!t) {
|
||||
return;
|
||||
}
|
||||
// FIXME
|
||||
t = ' Customize Footer ';
|
||||
// const frag = textToDf(t);
|
||||
document.documentElement.appendChild(document.createComment(t));
|
||||
};
|
||||
|
||||
const Index: FC = () => {
|
||||
const { custom_css, custom_head, custom_header, custom_footer } =
|
||||
customizeStore((state) => state);
|
||||
useEffect(() => {
|
||||
injectCustomCSS(custom_css);
|
||||
injectCustomHead(custom_head);
|
||||
injectCustomHeader(custom_header);
|
||||
injectCustomFooter(custom_footer);
|
||||
}, []);
|
||||
return (
|
||||
<>
|
||||
{null}
|
||||
{/* App customize */}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(Index);
|
|
@ -12,6 +12,9 @@ import { marked } from 'marked';
|
|||
import { htmlRender } from './utils';
|
||||
|
||||
let scrollTop = 0;
|
||||
marked.setOptions({
|
||||
breaks: true,
|
||||
});
|
||||
|
||||
const Index = ({ value }, ref) => {
|
||||
const [html, setHtml] = useState('');
|
||||
|
|
|
@ -46,7 +46,7 @@ const Index: FC = () => {
|
|||
{t('save')}
|
||||
</Button>
|
||||
</Card.Header>
|
||||
<Card.Body className="my-n1">
|
||||
<Card.Body>
|
||||
<TagSelector
|
||||
value={followingTags}
|
||||
onChange={handleTagsChange}
|
||||
|
@ -67,14 +67,14 @@ const Index: FC = () => {
|
|||
{t('edit')}
|
||||
</Button>
|
||||
</Card.Header>
|
||||
<Card.Body className="m-n1">
|
||||
<Card.Body>
|
||||
{followingTags?.length ? (
|
||||
<>
|
||||
<div className="m-n1">
|
||||
{followingTags.map((item) => {
|
||||
const slugName = item?.slug_name;
|
||||
return <Tag key={slugName} className="m-1" data={item} />;
|
||||
})}
|
||||
</>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<div className="text-muted">{t('follow_tag_tip')}</div>
|
||||
|
|
|
@ -58,6 +58,7 @@ export interface UISchema {
|
|||
| 'url'
|
||||
| 'week';
|
||||
empty?: string;
|
||||
className?: string | string[];
|
||||
validator?: (
|
||||
value,
|
||||
formData?,
|
||||
|
@ -459,6 +460,7 @@ const SchemaForm: ForwardRefRenderFunction<IRef, IProps> = (
|
|||
onChange={handleInputChange}
|
||||
isInvalid={formData[key].isInvalid}
|
||||
rows={options?.rows || 3}
|
||||
className={classnames(options.className)}
|
||||
/>
|
||||
<Form.Control.Feedback type="invalid">
|
||||
{formData[key]?.errorMsg}
|
||||
|
|
|
@ -181,7 +181,7 @@ const TagSelector: FC<IProps> = ({
|
|||
onFocus={onFocus}
|
||||
onBlur={onBlur}
|
||||
onKeyDown={handleKeyDown}>
|
||||
<div className="d-flex flex-wrap mx-n1">
|
||||
<div className="d-flex flex-wrap m-n1">
|
||||
{initialValue?.map((item, index) => {
|
||||
return (
|
||||
<Button
|
||||
|
|
|
@ -27,6 +27,7 @@ import BrandUpload from './BrandUpload';
|
|||
import SchemaForm, { JSONSchema, UISchema, initFormData } from './SchemaForm';
|
||||
import Labels from './LabelsCard';
|
||||
import DiffContent from './DiffContent';
|
||||
import Customize from './Customize';
|
||||
|
||||
export {
|
||||
Avatar,
|
||||
|
@ -60,5 +61,6 @@ export {
|
|||
initFormData,
|
||||
Labels,
|
||||
DiffContent,
|
||||
Customize,
|
||||
};
|
||||
export type { EditorRef, JSONSchema, UISchema };
|
||||
|
|
|
@ -45,7 +45,10 @@ const useAddUserModal = (props: IProps = {}) => {
|
|||
display_name: {
|
||||
'ui:options': {
|
||||
validator: (value) => {
|
||||
if (value.length > 30) {
|
||||
const MIN_LENGTH = 4;
|
||||
const MAX_LENGTH = 30;
|
||||
|
||||
if (value.length < MIN_LENGTH || value.length > MAX_LENGTH) {
|
||||
return t('form.fields.display_name.msg');
|
||||
}
|
||||
return true;
|
||||
|
|
|
@ -42,24 +42,28 @@ const Index: FC = () => {
|
|||
'ui:widget': 'textarea',
|
||||
'ui:options': {
|
||||
rows: 10,
|
||||
className: ['fs-14', 'font-monospace'],
|
||||
},
|
||||
},
|
||||
custom_head: {
|
||||
'ui:widget': 'textarea',
|
||||
'ui:options': {
|
||||
rows: 10,
|
||||
className: ['fs-14', 'font-monospace'],
|
||||
},
|
||||
},
|
||||
custom_header: {
|
||||
'ui:widget': 'textarea',
|
||||
'ui:options': {
|
||||
rows: 10,
|
||||
className: ['fs-14', 'font-monospace'],
|
||||
},
|
||||
},
|
||||
custom_footer: {
|
||||
'ui:widget': 'textarea',
|
||||
'ui:options': {
|
||||
rows: 10,
|
||||
className: ['fs-14', 'font-monospace'],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
@ -82,6 +86,9 @@ const Index: FC = () => {
|
|||
msg: t('update', { keyPrefix: 'toast' }),
|
||||
variant: 'success',
|
||||
});
|
||||
setTimeout(() => {
|
||||
window.location.reload();
|
||||
}, 3000);
|
||||
})
|
||||
.catch((err) => {
|
||||
if (err.isError) {
|
||||
|
|
|
@ -5,7 +5,7 @@ import { Helmet, HelmetProvider } from 'react-helmet-async';
|
|||
import { SWRConfig } from 'swr';
|
||||
|
||||
import { toastStore, brandingStore, pageTagStore } from '@/stores';
|
||||
import { Header, Footer, Toast } from '@/components';
|
||||
import { Header, Footer, Toast, Customize } from '@/components';
|
||||
|
||||
const Layout: FC = () => {
|
||||
const { msg: toastMsg, variant, clear: toastClear } = toastStore();
|
||||
|
@ -41,6 +41,7 @@ const Layout: FC = () => {
|
|||
</div>
|
||||
<Toast msg={toastMsg} variant={variant} onClose={closeToast} />
|
||||
<Footer />
|
||||
<Customize />
|
||||
</SWRConfig>
|
||||
</HelmetProvider>
|
||||
);
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
import create from 'zustand';
|
||||
|
||||
interface IType {
|
||||
custom_css: string;
|
||||
custom_head: string;
|
||||
custom_header: string;
|
||||
custom_footer: string;
|
||||
update: (params: {
|
||||
custom_css?: string;
|
||||
custom_head?: string;
|
||||
custom_header?: string;
|
||||
custom_footer?: string;
|
||||
}) => void;
|
||||
}
|
||||
|
||||
const loginSetting = create<IType>((set) => ({
|
||||
custom_css: '',
|
||||
custom_head: '',
|
||||
custom_header: '',
|
||||
custom_footer: '',
|
||||
update: (params) =>
|
||||
set((state) => {
|
||||
return {
|
||||
...state,
|
||||
...params,
|
||||
};
|
||||
}),
|
||||
}));
|
||||
|
||||
export default loginSetting;
|
|
@ -6,6 +6,7 @@ import siteInfoStore from './siteInfo';
|
|||
import interfaceStore from './interface';
|
||||
import brandingStore from './branding';
|
||||
import pageTagStore from './pageTags';
|
||||
import customizeStore from './customize';
|
||||
|
||||
export {
|
||||
toastStore,
|
||||
|
@ -15,4 +16,5 @@ export {
|
|||
brandingStore,
|
||||
pageTagStore,
|
||||
loginSettingStore,
|
||||
customizeStore,
|
||||
};
|
||||
|
|
|
@ -5,6 +5,7 @@ import {
|
|||
interfaceStore,
|
||||
brandingStore,
|
||||
loginSettingStore,
|
||||
customizeStore,
|
||||
} from '@/stores';
|
||||
import { RouteAlias } from '@/router/alias';
|
||||
import Storage from '@/utils/storage';
|
||||
|
@ -256,6 +257,7 @@ export const initAppSettingsStore = async () => {
|
|||
interfaceStore.getState().update(appSettings.interface);
|
||||
brandingStore.getState().update(appSettings.branding);
|
||||
loginSettingStore.getState().update(appSettings.login);
|
||||
customizeStore.getState().update(appSettings.custom_css_html);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in New Issue