Use rainbow bg color to mark build in label & Add behavior select counter & Optim popup focus #28

Merged
MrKBear merged 4 commits from dev-mrkbear into master 2022-03-28 23:17:34 +08:00
16 changed files with 130 additions and 53 deletions

View File

@ -1,7 +1,7 @@
import { BehaviorRecorder, IAnyBehaviorRecorder } from "@Model/Behavior"; import { BehaviorRecorder, IAnyBehaviorRecorder } from "@Model/Behavior";
import { Template } from "./Template"; import { Template } from "./Template";
const AllBehaviors: IAnyBehaviorRecorder[] = new Array(20).fill(0).map((_, i) => { const AllBehaviors: IAnyBehaviorRecorder[] = new Array(4).fill(0).map((_, i) => {
let behavior = new BehaviorRecorder(Template); let behavior = new BehaviorRecorder(Template);
behavior.behaviorId = behavior.behaviorId + i; behavior.behaviorId = behavior.behaviorId + i;
return behavior; return behavior;

View File

@ -10,6 +10,7 @@ div.behavior-list {
div.behavior-item { div.behavior-item {
margin: 5px; margin: 5px;
height: $behavior-item-height; height: $behavior-item-height;
user-select: none;
border-radius: 3px; border-radius: 3px;
cursor: pointer; cursor: pointer;
display: flex; display: flex;

View File

@ -5,6 +5,10 @@ div.behavior-popup {
height: 100%; height: 100%;
} }
span.behavior-popup-select-counter {
opacity: .75;
}
div.behavior-popup-search-box { div.behavior-popup-search-box {
padding: 10px 0 10px 10px; padding: 10px 0 10px 10px;
width: calc(100% - 10px); width: calc(100% - 10px);

View File

@ -62,19 +62,46 @@ class BehaviorPopupComponent extends Component<
</div>; </div>;
} }
private showBehaviorInfo = (behavior: IRenderBehavior) => {
if (this.props.status) {
const status = this.props.status;
status.popup.showPopup(ConfirmPopup, {
infoI18n: behavior.describe as any,
titleI18N: "Popup.Behavior.Info.Title",
titleI18NOption: {
behavior: I18N(this.props, behavior.behaviorName as any)
},
yesI18n: "Popup.Behavior.Info.Confirm",
})
}
}
private renderActionBar = () => {
return <Localization
className="behavior-popup-select-counter"
i18nKey="Popup.Add.Behavior.Select.Counter"
options={{
count: this.state.focusBehavior.size.toString()
}}
/>
}
public render(): ReactNode { public render(): ReactNode {
return <ConfirmContent return <ConfirmContent
className="behavior-popup" className="behavior-popup"
actions={[{ actions={[{
i18nKey: "Popup.Add.Behavior.Action.Add" i18nKey: "Popup.Add.Behavior.Action.Add",
disable: this.state.focusBehavior.size <= 0
}]} }]}
header={this.renderHeader} header={this.renderHeader}
customFooter={this.renderActionBar}
headerHeight={46} headerHeight={46}
> >
<Message i18nKey="ZH_CN" isTitle first/> <Message i18nKey="ZH_CN" isTitle first/>
<BehaviorList <BehaviorList
focusBehaviors={Array.from(this.state.focusBehavior)} focusBehaviors={Array.from(this.state.focusBehavior)}
behaviors={AllBehaviors} behaviors={AllBehaviors}
action={this.showBehaviorInfo}
click={(behavior) => { click={(behavior) => {
if (this.state.focusBehavior.has(behavior)) { if (this.state.focusBehavior.has(behavior)) {
this.state.focusBehavior.delete(behavior); this.state.focusBehavior.delete(behavior);
@ -83,19 +110,6 @@ class BehaviorPopupComponent extends Component<
} }
this.forceUpdate(); this.forceUpdate();
}} }}
action={(behavior)=>{
if (this.props.status) {
const status = this.props.status;
status.popup.showPopup(ConfirmPopup, {
infoI18n: behavior.describe as any,
titleI18N: "Popup.Behavior.Info.Title",
titleI18NOption: {
behavior: I18N(this.props, behavior.behaviorName as any)
},
yesI18n: "Popup.Behavior.Info.Confirm",
})
}
}}
/> />
</ConfirmContent> </ConfirmContent>
} }

View File

@ -47,7 +47,17 @@ div.confirm-root {
justify-content: flex-end; justify-content: flex-end;
align-items: center; align-items: center;
div.action-right-view {
width: 100%;
height: 100%;
flex-shrink: 1;
display: flex;
align-items: center;
padding-left: 10px;
}
div.action-button { div.action-button {
flex-shrink: 0;
height: 26px; height: 26px;
padding: 0 10px; padding: 0 10px;
border-radius: 3px; border-radius: 3px;

View File

@ -68,6 +68,7 @@ interface IConfirmContentProps {
hidePadding?: boolean; hidePadding?: boolean;
className?: string; className?: string;
actions: IActionButtonProps[]; actions: IActionButtonProps[];
customFooter?: () => ReactNode;
header?: () => ReactNode; header?: () => ReactNode;
headerHeight?: number; headerHeight?: number;
} }
@ -154,10 +155,14 @@ class ConfirmContent extends Component<IConfirmContentProps> {
</div> </div>
<div className="action-view"> <div className="action-view">
<div className="action-right-view">
{this.props.customFooter ? this.props.customFooter() : null}
</div>
{ {
this.props.actions.map((prop, index) => { this.props.actions ?
return this.renderActionButton(prop, index); this.props.actions.map((prop, index) => {
}) return this.renderActionButton(prop, index);
}) : null
} }
</div> </div>
</Theme>; </Theme>;

View File

@ -9,7 +9,7 @@ const LanguageDataBase = {
EN_US, ZH_CN EN_US, ZH_CN
} }
type AllI18nKeys = keyof typeof EN_US; type AllI18nKeys = (keyof typeof EN_US) | string;
interface ILocalizationProps { interface ILocalizationProps {
className?: string; className?: string;
@ -17,14 +17,17 @@ interface ILocalizationProps {
options?: Record<string, string>; options?: Record<string, string>;
} }
function I18N(language: Language | IMixinSettingProps, key: keyof typeof EN_US, values?: Record<string, string>) { function I18N(language: Language | IMixinSettingProps, key: AllI18nKeys, values?: Record<string, string>) {
let lang: Language; let lang: Language;
if (typeof language === "string") { if (typeof language === "string") {
lang = language; lang = language;
} else { } else {
lang = language.setting?.language ?? "EN_US"; lang = language.setting?.language ?? "EN_US";
} }
let i18nValue = LanguageDataBase[lang][key]; let languageDataBase = LanguageDataBase[lang];
if (!languageDataBase) languageDataBase = LanguageDataBase["EN_US"];
let i18nValue = languageDataBase[key as keyof typeof EN_US];
if (!i18nValue) i18nValue = key;
if (values) { if (values) {
for (let valueKey in values) { for (let valueKey in values) {
i18nValue = i18nValue.replaceAll(new RegExp(`\\{\\s*${valueKey}\\s*\\}`, "g"), values[valueKey]); i18nValue = i18nValue.replaceAll(new RegExp(`\\{\\s*${valueKey}\\s*\\}`, "g"), values[valueKey]);

View File

@ -1,4 +1,5 @@
@import "../Theme/Theme.scss"; @import "../Theme/Theme.scss";
@import "../PickerList/RainbowBg.scss";
$line-min-height: 24px; $line-min-height: 24px;

View File

@ -97,20 +97,8 @@ class ObjectPicker extends Component<IObjectPickerProps & IMixinStatusProps, IOb
public render(): ReactNode { public render(): ReactNode {
let disPlayInfo: IDisplayInfo; let disPlayInfo: IDisplayInfo = getObjectDisplayInfo(this.props.value);
let isDelete: boolean = false; let isDelete: boolean = !!this.props.value?.isDeleted();
if (this.props.value) {
disPlayInfo = getObjectDisplayInfo(this.props.value);
isDelete = this.props.value.isDeleted();
} else {
disPlayInfo = {
name: "Input.Error.Select",
icon: "Label",
color: "transparent",
needI18n: true
}
}
return <> return <>
<TextField <TextField
@ -129,9 +117,13 @@ class ObjectPicker extends Component<IObjectPickerProps & IMixinStatusProps, IOb
}} }}
> >
<div <div
className="list-color" className={
"list-color" + (
disPlayInfo.allLabel ? " rainbow-back-ground-color" : ""
)
}
style={{ style={{
backgroundColor: disPlayInfo.color backgroundColor: disPlayInfo.color
}} }}
/> />
<div className="list-button"> <div className="list-button">
@ -145,7 +137,7 @@ class ObjectPicker extends Component<IObjectPickerProps & IMixinStatusProps, IOb
}} }}
> >
{ {
disPlayInfo.needI18n ? disPlayInfo.internal ?
<Localization i18nKey={disPlayInfo.name as any}/> : <Localization i18nKey={disPlayInfo.name as any}/> :
<span>{disPlayInfo.name}</span> <span>{disPlayInfo.name}</span>
} }

View File

@ -1,3 +1,5 @@
@import "./RainbowBg.scss";
div.picker-list-root { div.picker-list-root {
min-width: 200px; min-width: 200px;
height: 100%; height: 100%;

View File

@ -12,7 +12,8 @@ interface IDisplayInfo {
color: string; color: string;
icon: string; icon: string;
name: string; name: string;
needI18n?: boolean; internal: boolean;
allLabel: boolean;
}; };
interface IDisplayItem { interface IDisplayItem {
@ -21,12 +22,23 @@ interface IDisplayItem {
mark?: boolean; mark?: boolean;
} }
function getObjectDisplayInfo(item: IPickerListItem): IDisplayInfo { function getObjectDisplayInfo(item?: IPickerListItem): IDisplayInfo {
let color: number[] = []; if (!item) {
return {
color: "transparent",
icon: "Label",
name: "Input.Error.Select",
internal: true,
allLabel: false
}
}
let color: number[] | string = [];
let icon: string = "tag"; let icon: string = "tag";
let name: string = ""; let name: string = "";
let needI18n: boolean = false; let internal: boolean = false;
let allLabel: boolean = false;
if (item instanceof Range) { if (item instanceof Range) {
icon = "CubeShape" icon = "CubeShape"
@ -35,6 +47,7 @@ function getObjectDisplayInfo(item: IPickerListItem): IDisplayInfo {
icon = "WebAppBuilderFragment" icon = "WebAppBuilderFragment"
} }
if (item instanceof CtrlObject) { if (item instanceof CtrlObject) {
color = [];
color[0] = Math.round(item.color[0] * 255); color[0] = Math.round(item.color[0] * 255);
color[1] = Math.round(item.color[1] * 255); color[1] = Math.round(item.color[1] * 255);
color[2] = Math.round(item.color[2] * 255); color[2] = Math.round(item.color[2] * 255);
@ -43,7 +56,9 @@ function getObjectDisplayInfo(item: IPickerListItem): IDisplayInfo {
if (item instanceof Label) { if (item instanceof Label) {
if (item.isBuildIn) { if (item.isBuildIn) {
needI18n = true; internal = true;
allLabel = true;
color = "transparent";
if (item.id === "AllRange") { if (item.id === "AllRange") {
icon = "ProductList"; icon = "ProductList";
name = "Build.In.Label.Name.All.Range"; name = "Build.In.Label.Name.All.Range";
@ -60,11 +75,16 @@ function getObjectDisplayInfo(item: IPickerListItem): IDisplayInfo {
} }
} }
if (Array.isArray(color)) {
color = `rgb(${color[0]},${color[1]},${color[2]})`;
}
return { return {
color: needI18n ? "transparent" : `rgb(${color[0]},${color[1]},${color[2]})`, color: color,
icon: icon, icon: icon,
name: name, name: name,
needI18n: needI18n internal: internal,
allLabel: allLabel
} }
} }
@ -92,7 +112,11 @@ class PickerList extends Component<IPickerListProps> {
} }
}} }}
> >
<div className="list-item-color" <div className={
"list-item-color" + (
displayInfo.allLabel ? " rainbow-back-ground-color" : ""
)
}
style={{ style={{
backgroundColor: displayInfo.color backgroundColor: displayInfo.color
}} }}
@ -102,8 +126,8 @@ class PickerList extends Component<IPickerListProps> {
</div> </div>
<div className="list-item-name"> <div className="list-item-name">
{ {
displayInfo.needI18n ? displayInfo.internal ?
<Localization i18nKey={displayInfo.name as any}/> : <Localization i18nKey={displayInfo.name}/> :
displayInfo.name displayInfo.name
} }
</div> </div>

View File

@ -0,0 +1,13 @@
div.rainbow-back-ground-color {
background-image: linear-gradient(
to top,
orangered,
orange,
gold,
lightgreen,
cyan,
dodgerblue,
mediumpurple,
hotpink,
orangered);
}

View File

@ -78,9 +78,6 @@ class Popup extends Component<IPopupProps & IMixinStatusProps & IMixinSettingPro
popup.isOnMouseDown = true; popup.isOnMouseDown = true;
popup.lastMouseLeft = e.clientX; popup.lastMouseLeft = e.clientX;
popup.lastMouseTop = e.clientY; popup.lastMouseTop = e.clientY;
if (this.props.status) {
this.props.status.popup.topping(popup);
}
}} }}
> >
{popup.onRenderHeader()} {popup.onRenderHeader()}
@ -257,6 +254,11 @@ class Popup extends Component<IPopupProps & IMixinStatusProps & IMixinSettingPro
className: "popup-layer" + (hasAnimate ? " show-scale" : ""), className: "popup-layer" + (hasAnimate ? " show-scale" : ""),
backgroundLevel: BackgroundLevel.Level4, backgroundLevel: BackgroundLevel.Level4,
}, this.props.setting).join(" ")} }, this.props.setting).join(" ")}
onClick={() => {
if (this.props.status) {
this.props.status.popup.topping(popup);
}
}}
> >
{this.renderDragLine(ResizeDragDirection.top, popup)} {this.renderDragLine(ResizeDragDirection.top, popup)}
<div className="popup-layer-container"> <div className="popup-layer-container">

View File

@ -163,8 +163,12 @@ class PopupController extends Emitter<IPopupControllerEvent> {
newPopup = new (popup ?? Popup)(props); newPopup = new (popup ?? Popup)(props);
} }
newPopup.init(this, `P-${this.idIndex ++}`); newPopup.init(this, `P-${this.idIndex ++}`);
this.popups.push(newPopup);
this.sortPopup(); // 延迟渲染防止焦点冲突
setTimeout(() => {
this.popups.push(newPopup);
this.sortPopup();
});
return newPopup; return newPopup;
} }

View File

@ -54,6 +54,7 @@ const EN_US = {
"Popup.Setting.Title": "Preferences setting", "Popup.Setting.Title": "Preferences setting",
"Popup.Add.Behavior.Title": "Add behavior", "Popup.Add.Behavior.Title": "Add behavior",
"Popup.Add.Behavior.Action.Add": "Add all select behavior", "Popup.Add.Behavior.Action.Add": "Add all select behavior",
"Popup.Add.Behavior.Select.Counter": "Selected {count} behavior",
"Popup.Behavior.Info.Title": "Behavior details: {behavior}", "Popup.Behavior.Info.Title": "Behavior details: {behavior}",
"Popup.Behavior.Info.Confirm": "OK, I know it", "Popup.Behavior.Info.Confirm": "OK, I know it",
"Build.In.Label.Name.All.Group": "All group", "Build.In.Label.Name.All.Group": "All group",

View File

@ -54,6 +54,7 @@ const ZH_CN = {
"Popup.Setting.Title": "首选项设置", "Popup.Setting.Title": "首选项设置",
"Popup.Add.Behavior.Title": "添加行为", "Popup.Add.Behavior.Title": "添加行为",
"Popup.Add.Behavior.Action.Add": "添加全部选中行为", "Popup.Add.Behavior.Action.Add": "添加全部选中行为",
"Popup.Add.Behavior.Select.Counter": "已选择 {count} 个行为",
"Popup.Behavior.Info.Title": "行为详情: {behavior}", "Popup.Behavior.Info.Title": "行为详情: {behavior}",
"Popup.Behavior.Info.Confirm": "好的, 我知道了", "Popup.Behavior.Info.Confirm": "好的, 我知道了",
"Build.In.Label.Name.All.Group": "全部群", "Build.In.Label.Name.All.Group": "全部群",