Compare commits

...

3 Commits

Author SHA1 Message Date
Wells
43b99de01c chore(release): publish
- @music163/tango-context@1.0.0
 - @music163/tango-core@1.0.0
 - @music163/tango-designer@1.0.0
 - @music163/tango-sandbox@1.0.0
 - @music163/tango-setting-form@1.0.0
 - @music163/tango-ui@1.0.0
2024-04-15 14:41:25 +08:00
BoBoooooo
1c1ad9e5eb feat: support traverse native dom (#132)
* Create sync.yml

* Update sync.yml

* feat: support nativeDom

* ci: remove sync.yml

* fix: update test case

* fix: update isValidComponentName logic
2024-04-15 14:22:34 +08:00
BoBoooooo
776837079a feat: update ui & add dual screen mode (#131)
* fix: change view source icon

* fix: setting-form collapsed panel default collapsed

* fix: selectAction font size larger

* feat: add dual screen mode

* fix: trigger bug
2024-04-11 10:20:07 +08:00
20 changed files with 216 additions and 67 deletions

View File

@@ -173,6 +173,60 @@ class App extends React.Component {
<FormilyFormItem name="select1" component="Select" label="表单项" />
</FormilyForm>
</Section>
<Section title="原生 DOM" tid="section3">
<div
style={{
border: "1px solid #ccc",
borderRadius: "5px",
backgroundColor: "#f4f4f4",
}}
>
<form onSubmit={tango.stores.app.submitForm} style={{
padding: "10px",
}}>
<div>
<label>Username:</label>
<input
type="text"
id="username"
name="username"
style={{ margin: "5px" }}
/>
</div>
<div>
<label>Password:</label>
<input
type="password"
id="password"
name="password"
style={{ margin: "5px" }}
/>
</div>
<div>
<label>Role:</label>
<select id="role" name="role" style={{ margin: "5px" }}>
<option value="admin">Admin</option>
<option value="user">User</option>
</select>
</div>
<div>
<button
type="submit"
style={{
marginTop: '5px',
padding: "3px 10px",
backgroundColor: "#007bff",
color: "#fff",
border: "none",
borderRadius: "5px",
}}
>
Submit
</button>
</div>
</form>
</div>
</Section>
</Page>
);
}
@@ -227,38 +281,21 @@ const storeApp = `
import { defineStore } from '@music163/tango-boot';
export default defineStore({
title: 'Page Title',
array: [1, 2, 3],
test: function() {
console.log('test');
console.log('test');
console.log('test');
console.log('test');
console.log('test');
console.log('test');
console.log('test');
console.log('test');
console.log('test');
console.log('test');
console.log('test');
console.log('test');
console.log('test');
console.log('test');
console.log('test');
console.log('test');
console.log('test');
console.log('test');
console.log('test');
console.log('test');
console.log('test');
console.log('test');
console.log('test');
console.log('test');
console.log('test');
console.log('test');
},
submitForm: function(event) {
event.preventDefault();
const formData = new FormData(event.target);
const username = formData.get("username");
const password = formData.get("password");
const role = formData.get("role");
console.log("Submitted Data:", { username, password, role });
}
}, 'app');
`;

View File

@@ -85,8 +85,82 @@ basePrototypes['Section'].siblingNames = [
'Section',
];
export const nativeDomPrototypes = () => {
const doms = [
'div',
'span',
'h1',
'h2',
'p',
'a',
'img',
'ul',
'ol',
'li',
'input',
'button',
'form',
'table',
'tr',
'td',
'header',
'footer',
'nav',
'section',
'article',
'aside',
'main',
'video',
'audio',
'label',
'select',
'option',
'textarea',
'iframe',
];
const componentPrototypes: Dict<IComponentPrototype> = doms.reduce(
(acc: Dict<IComponentPrototype>, tag) => {
acc[tag] = {
name: tag,
title: tag,
hasChildren: true,
package: '',
type: 'element',
props: [
{
name: 'style',
title: '样式',
group: 'style',
setter: 'expressionSetter',
},
{
name: 'className',
title: '类名',
setter: 'textSetter',
},
{
name: 'id',
title: 'ID',
setter: 'textSetter',
},
{
name: 'onClick',
title: '点击事件',
setter: 'actionSetter',
group: 'event',
},
],
};
return acc;
},
{},
);
return componentPrototypes;
};
// iconfont: https://www.iconfont.cn/manage/index?manage_type=myprojects&projectId=2891794
const prototypes: Dict<IComponentPrototype> = {
...nativeDomPrototypes(),
...(basePrototypes as any),
SnippetSuccessResult,
Snippet2ColumnLayout,

View File

@@ -1,6 +1,6 @@
{
"name": "@music163/tango-context",
"version": "1.0.0-alpha.10",
"version": "1.0.0",
"description": "react context for tango-apps",
"keywords": [
"react",
@@ -31,7 +31,7 @@
"react": ">= 16.8"
},
"dependencies": {
"@music163/tango-core": "^1.0.0-alpha.10",
"@music163/tango-core": "^1.0.0",
"@music163/tango-helpers": "^1.0.0-alpha.6",
"mobx-react-lite": "4.0.5"
},

View File

@@ -1,6 +1,6 @@
{
"name": "@music163/tango-core",
"version": "1.0.0-alpha.10",
"version": "1.0.0",
"description": "tango core",
"author": "wwsun <ww.sun@outlook.com>",
"homepage": "",

View File

@@ -75,15 +75,20 @@ export function inferFileType(filename: string): FileType {
/**
* 判断组件名是否合法
* @example Button -> valid
* @example div -> invalid
* @example isStrict ? div -> invalid : div -> valid
* @param name
* @param isStrict 是否严格模式(严格模式不匹配原生标签)
* @returns
*/
export function isValidComponentName(name: string) {
export function isValidComponentName(name: string, isStrict = false) {
if (!name) {
return false;
}
if (!isStrict) {
return true;
}
const firstChar = name.charAt(0);
return firstChar === firstChar.toUpperCase();
}

View File

@@ -3,7 +3,7 @@ import { IWorkspace } from './interfaces';
export type SimulatorNameType = 'desktop' | 'phone';
export type DesignerViewType = 'design' | 'code';
export type DesignerViewType = 'design' | 'code' | 'dual';
interface ISimulatorType {
name: SimulatorNameType;

View File

@@ -161,6 +161,10 @@ describe('string helpers', () => {
it('isValidComponentName', () => {
expect(isValidComponentName('Button')).toBeTruthy();
expect(isValidComponentName('Button.Group')).toBeTruthy();
expect(isValidComponentName('div')).toBeTruthy();
expect(isValidComponentName('')).toBeFalsy();
expect(isValidComponentName(null)).toBeFalsy();
expect(isValidComponentName(undefined)).toBeFalsy();
});
it('inferFileType', () => {

View File

@@ -1,6 +1,6 @@
{
"name": "@music163/tango-designer",
"version": "1.0.0-alpha.18",
"version": "1.0.0",
"description": "lowcode designer",
"keywords": [
"react"
@@ -33,12 +33,12 @@
"dependencies": {
"@ant-design/icons": "^4.8.0",
"@music163/request": "^0.1.2",
"@music163/tango-context": "^1.0.0-alpha.10",
"@music163/tango-core": "^1.0.0-alpha.10",
"@music163/tango-context": "^1.0.0",
"@music163/tango-core": "^1.0.0",
"@music163/tango-helpers": "^1.0.0-alpha.6",
"@music163/tango-sandbox": "^1.0.0-alpha.10",
"@music163/tango-setting-form": "^1.0.0-alpha.14",
"@music163/tango-ui": "^1.0.0-alpha.12",
"@music163/tango-sandbox": "^1.0.0",
"@music163/tango-setting-form": "^1.0.0",
"@music163/tango-ui": "^1.0.0",
"antd": "^4.24.2",
"cash-dom": "^8.1.2",
"classnames": "^2.3.2",

View File

@@ -1,7 +1,6 @@
import React from 'react';
import { observer, useDesigner } from '@music163/tango-context';
import { SelectAction } from '@music163/tango-ui';
import { AimOutlined } from '@ant-design/icons';
import { SelectAction, CodeOutlined } from '@music163/tango-ui';
export const ViewSourceAction = observer(() => {
const designer = useDesigner();
@@ -12,7 +11,7 @@ export const ViewSourceAction = observer(() => {
designer.setActiveView('code');
}}
>
<AimOutlined />
<CodeOutlined />
</SelectAction>
);
});

View File

@@ -1,13 +1,25 @@
import React from 'react';
import React, { useCallback } from 'react';
import { Group } from 'coral-system';
import { Modal } from 'antd';
import { ToggleButton, CodeOutlined } from '@music163/tango-ui';
import { ToggleButton, CodeOutlined, DualOutlined } from '@music163/tango-ui';
import { observer, useDesigner, useWorkspace } from '@music163/tango-context';
import { BorderOutlined } from '@ant-design/icons';
export const ModeSwitchTool = observer(() => {
const workspace = useWorkspace();
const designer = useDesigner();
const activeFileCheck = useCallback(() => {
if (workspace.activeFile !== workspace.activeViewFile) {
Modal.confirm({
title: '当前打开的文件与视图不匹配,是否切换到当前视图对应的文件?',
onOk: () => {
workspace.setActiveFile(workspace.activeViewFile);
},
});
}
}, [workspace]);
return (
<Group attached>
<ToggleButton
@@ -26,19 +38,24 @@ export const ModeSwitchTool = observer(() => {
onClick={() => {
designer.setActiveView('code'); // 切换到源码视图
designer.setActiveSidebarPanel(''); // 关闭左侧面板
if (workspace.activeFile !== workspace.activeViewFile) {
Modal.confirm({
title: '当前打开的文件与视图不匹配,是否切换到当前视图对应的文件?',
onOk: () => {
workspace.setActiveFile(workspace.activeViewFile);
},
});
}
activeFileCheck();
}}
tooltip="源码视图"
>
<CodeOutlined />
</ToggleButton>
<ToggleButton
shape="ghost"
selected={designer.activeView === 'dual'}
onClick={() => {
designer.setActiveView('dual'); // 切换到双屏视图
designer.setActiveSidebarPanel(''); // 关闭左侧面板
activeFileCheck();
}}
tooltip="双屏视图"
>
<DualOutlined />
</ToggleButton>
</Group>
);
});

View File

@@ -13,7 +13,7 @@ export const PreviewTool = observer(() => {
onClick={() => {
const nextIsPreview = !designer.isPreview;
designer.toggleIsPreview(nextIsPreview);
if (nextIsPreview) {
if (nextIsPreview && designer.activeView === 'code') {
designer.setActiveView('design');
}
}}

View File

@@ -9,7 +9,6 @@ export function WorkspacePanel({ children }: WorkspacePanelProps) {
<Box
flex="1"
display="flex"
flexDirection="column"
overflow="hidden"
bg="colors.custom.viewportBg"
position="relative"

View File

@@ -21,7 +21,7 @@ export const WorkspaceView = observer((props: WorkspaceViewProps) => {
return (
<Box
className={`ViewPanel ${mode}`}
display={display}
display={designer.activeView === 'dual' ? 'block' : display}
flex="1"
overflow={overflow}
position="relative"

View File

@@ -1,6 +1,6 @@
{
"name": "@music163/tango-sandbox",
"version": "1.0.0-alpha.10",
"version": "1.0.0",
"description": "sandbox of tango apps",
"author": "wwsun <ww.sun@outlook.com>",
"homepage": "",
@@ -29,7 +29,7 @@
},
"dependencies": {
"@ant-design/icons": "^4.8.0",
"@music163/tango-core": "^1.0.0-alpha.10",
"@music163/tango-core": "^1.0.0",
"@music163/tango-helpers": "^1.0.0-alpha.6",
"crypto-js": "^4.1.1",
"lodash.isequal": "4.5.0",

View File

@@ -1,6 +1,6 @@
{
"name": "@music163/tango-setting-form",
"version": "1.0.0-alpha.14",
"version": "1.0.0",
"description": "setting form of tango-apps",
"author": "wwsun <ww.sun@outlook.com>",
"homepage": "",
@@ -28,9 +28,9 @@
},
"dependencies": {
"@ant-design/icons": "^4.8.0",
"@music163/tango-core": "^1.0.0-alpha.10",
"@music163/tango-core": "^1.0.0",
"@music163/tango-helpers": "^1.0.0-alpha.6",
"@music163/tango-ui": "^1.0.0-alpha.12",
"@music163/tango-ui": "^1.0.0",
"antd": "^4.24.2",
"coral-system": "^1.0.5",
"mobx": "6.12.0",

View File

@@ -62,10 +62,6 @@ export interface FormControlGroupProps extends FormLabelProps {
* 是否勾选
*/
checked?: boolean;
/**
* checkbox 默认是否勾选
*/
defaultChecked?: boolean;
}
export function FormControlGroup({
@@ -78,9 +74,9 @@ export function FormControlGroup({
children,
onCheck,
checked,
defaultChecked = false,
}: FormControlGroupProps) {
const [collapsed, setCollapsed] = useState(isNil(checked) ? !defaultChecked : !checked);
// 默认折叠
const [collapsed, setCollapsed] = useState(true);
return (
<CollapsePanel
collapsed={collapsed}

View File

@@ -1,6 +1,6 @@
{
"name": "@music163/tango-ui",
"version": "1.0.0-alpha.12",
"version": "1.0.0",
"description": "ui widgets of tango",
"keywords": [
"react",

View File

@@ -0,0 +1,17 @@
import React from 'react';
import { createIcon } from './create-icon';
const DualOutlinedSvg = () => (
<svg
viewBox="0 0 1025 1024"
version="1.1"
xmlns="http://www.w3.org/2000/svg"
width="1em"
height="1em"
fill="currentColor"
>
<path d="M912 64H112c-26.5 0-48 21.5-48 48v800c0 26.5 21.5 48 48 48h800c26.5 0 48-21.5 48-48V112c0-26.5-21.5-48-48-48zM136 193h340v695H136V193z m412 695V193h340v695H548z" />
</svg>
);
export const DualOutlined = createIcon(DualOutlinedSvg, 'DualOutlined');

View File

@@ -8,3 +8,4 @@ export * from './open-panel-left-outlined';
export * from './open-panel-right-outlined';
export * from './undo-outlined';
export * from './redo-outlined';
export * from './dual-outlined';

View File

@@ -21,7 +21,7 @@ export function SelectAction({ tooltip, children, ...rest }: SelectActionProps)
bg="brand"
color="white"
px="s"
fontSize="body"
fontSize="subtitle"
css={selectionActionStyle}
{...rest}
>