diff --git a/source/Component/BehaviorPopup/BehaviorPopup.scss b/source/Component/BehaviorPopup/BehaviorPopup.scss index aaea67b..290e2cf 100644 --- a/source/Component/BehaviorPopup/BehaviorPopup.scss +++ b/source/Component/BehaviorPopup/BehaviorPopup.scss @@ -3,4 +3,9 @@ div.behavior-popup { width: 100%; height: 100%; +} + +div.behavior-popup-search-box { + padding: 10px 0 0 10px; + width: calc(100% - 10px); } \ No newline at end of file diff --git a/source/Component/BehaviorPopup/BehaviorPopup.tsx b/source/Component/BehaviorPopup/BehaviorPopup.tsx index f3a8761..53c062f 100644 --- a/source/Component/BehaviorPopup/BehaviorPopup.tsx +++ b/source/Component/BehaviorPopup/BehaviorPopup.tsx @@ -1,13 +1,18 @@ import { Component, ReactNode } from "react"; import { Popup } from "@Context/Popups"; -import { Theme } from "@Component/Theme/Theme"; import { Localization } from "@Component/Localization/Localization"; +import { SearchBox } from "@Component/SearchBox/SearchBox"; +import { ConfirmContent } from "@Component/ConfirmPopup/ConfirmPopup"; import "./BehaviorPopup.scss"; interface IBehaviorPopupProps { } +interface IBehaviorPopupState { + searchValue: string; +} + class BehaviorPopup extends Popup { public minWidth: number = 400; @@ -24,10 +29,36 @@ class BehaviorPopup extends Popup { } } -class BehaviorPopupComponent extends Component { +class BehaviorPopupComponent extends Component { + + state: Readonly = { + searchValue: "" + }; + + private renderHeader = () => { + return
+ { + this.setState({ + searchValue: value + }); + }} + value={this.state.searchValue} + /> +
; + } public render(): ReactNode { - return + return + + } } diff --git a/source/Component/ConfirmPopup/ConfirmPopup.scss b/source/Component/ConfirmPopup/ConfirmPopup.scss index 76e46ae..76c46d6 100644 --- a/source/Component/ConfirmPopup/ConfirmPopup.scss +++ b/source/Component/ConfirmPopup/ConfirmPopup.scss @@ -4,10 +4,35 @@ div.confirm-root { width: 100%; height: 100%; + div.header-view { + width: 100%; + } + div.content-views { width: 100%; - height: calc( 100% - 36px ); box-sizing: border-box; + overflow: scroll; + -ms-overflow-style: none; + flex-shrink: 1; + } + + div.content-views::-webkit-scrollbar { + width : 8px; /*高宽分别对应横竖滚动条的尺寸*/ + height: 0; + } + + div.content-views::-webkit-scrollbar-thumb { + /*滚动条里面小方块*/ + border-radius: 8px; + } + + div.content-views::-webkit-scrollbar-track { + /*滚动条里面轨道*/ + border-radius: 8px; + background-color: rgba($color: #000000, $alpha: 0); + } + + div.content-views.has-padding { padding: 10px; } @@ -35,14 +60,27 @@ div.confirm-root { div.action-button.red { color: $lt-red; } + + div.action-button.blue { + color: $lt-blue; + } + + div.action-button.disable { + opacity: .75; + cursor: not-allowed; + } } } div.dark.confirm-root { + div.content-views::-webkit-scrollbar-thumb { + background-color: $lt-bg-color-lvl1-dark; + } + div.action-view { - div.action-button { + div.action-button, div.action-button.disable:hover { background-color: $lt-bg-color-lvl3-dark; } @@ -54,9 +92,13 @@ div.dark.confirm-root { div.light.confirm-root { + div.content-views::-webkit-scrollbar-thumb { + background-color: $lt-bg-color-lvl1-light; + } + div.action-view { - div.action-button { + div.action-button, div.action-button.disable:hover { background-color: $lt-bg-color-lvl3-light; } diff --git a/source/Component/ConfirmPopup/ConfirmPopup.tsx b/source/Component/ConfirmPopup/ConfirmPopup.tsx index b530ea8..a3c7aba 100644 --- a/source/Component/ConfirmPopup/ConfirmPopup.tsx +++ b/source/Component/ConfirmPopup/ConfirmPopup.tsx @@ -1,5 +1,5 @@ import { Popup } from "@Context/Popups"; -import { ReactNode } from "react"; +import { Component, ReactNode } from "react"; import { Message } from "@Component/Message/Message"; import { Theme } from "@Component/Theme/Theme"; import { AllI18nKeys, Localization } from "@Component/Localization/Localization"; @@ -7,12 +7,12 @@ import "./ConfirmPopup.scss"; interface IConfirmPopupProps { titleI18N?: AllI18nKeys; - infoI18n: AllI18nKeys; + infoI18n?: AllI18nKeys; yesI18n?: AllI18nKeys; noI18n?: AllI18nKeys; + red?: "yes" | "no"; yes?: () => any; no?: () => any; - red?: "yes" | "no"; } class ConfirmPopup extends Popup { @@ -24,37 +24,139 @@ class ConfirmPopup extends Popup { return } + private genActionClickFunction(fn?: () => any): () => any { + return () => { + if (fn) fn(); + this.close(); + }; + } + public render(): ReactNode { - const yesClassList: string[] = ["action-button", "yes-button"]; - const noClassList: string[] = ["action-button", "no-button"]; - if (this.props.red === "no") { - noClassList.push("red"); + const actionList: IActionButtonProps[] = []; + + if (this.props.yesI18n || this.props.yes) { + actionList.push({ + i18nKey: this.props.yesI18n ?? "Popup.Action.Yes", + onClick: this.genActionClickFunction(this.props.yes), + color: this.props.red === "yes" ? "red" : undefined + }); } - if (this.props.red === "yes") { - yesClassList.push("red"); + + if (this.props.noI18n || this.props.no) { + actionList.push({ + i18nKey: this.props.noI18n ?? "Popup.Action.Yes", + onClick: this.genActionClickFunction(this.props.no), + color: this.props.red === "no" ? "red" : undefined + }); + } + + return + {this.props.infoI18n ? : null} + + } +} + +interface IConfirmContentProps { + hidePadding?: boolean; + className?: string; + actions: IActionButtonProps[]; + header?: () => ReactNode; + headerHeight?: number; +} + +interface IActionButtonProps { + className?: string; + disable?: boolean; + color?: "red" | "blue"; + i18nKey: AllI18nKeys; + i18nOption?: Record; + onClick?: () => void; +} + +class ConfirmContent extends Component { + + public renderActionButton(props: IActionButtonProps, key: number): ReactNode { + + const classList = ["action-button"]; + if (props.className) { + classList.push(props.className); + } + + if (props.color === "red") { + classList.push("red"); + } + + if (props.color === "blue") { + classList.push("blue"); + } + + if (props.disable) { + classList.push("disable"); + } + + return
+ +
+ } + + private getHeaderHeight(): number { + return this.props.headerHeight ?? 0; + } + + private renderHeader() { + return
+ {this.props.header ? this.props.header() : null} +
+ } + + public render(): ReactNode { + + const contentClassNameList: string[] = ["content-views"]; + + if (this.props.className) { + contentClassNameList.push(this.props.className); + } + + if (!this.props.hidePadding) { + contentClassNameList.push("has-padding"); } return -
- + + {this.props.header ? this.renderHeader() : null} + +
+ {this.props.children}
+
-
{ - this.props.yes ? this.props.yes() : null; - this.close(); - }}> - -
-
{ - this.props.no ? this.props.no() : null; - this.close(); - }}> - -
+ { + this.props.actions.map((prop, index) => { + return this.renderActionButton(prop, index); + }) + }
; } } -export { ConfirmPopup } \ No newline at end of file +export { ConfirmPopup, ConfirmContent } \ No newline at end of file diff --git a/source/Component/SearchBox/SearchBox.scss b/source/Component/SearchBox/SearchBox.scss new file mode 100644 index 0000000..a7636fd --- /dev/null +++ b/source/Component/SearchBox/SearchBox.scss @@ -0,0 +1,95 @@ +@import "../Theme/Theme.scss"; + +$search-box-height: 26px; + +div.search-box-root { + min-height: $search-box-height; + max-width: 280px; + width: 100%; + border-radius: 3px; + display: flex; + cursor: pointer; + overflow: hidden; + + div.search-icon { + min-width: $search-box-height; + height: $search-box-height; + flex-shrink: 0; + display: flex; + justify-content: center; + align-items: center; + user-select: none; + } + + div.input-box { + width: calc(100% - 26px); + height: $search-box-height; + + input { + width: 100%; + height: 100%; + padding: 0; + margin: 0; + border: 0; + outline: none; + background-color: transparent; + vertical-align: middle; + } + } + + div.clean-box { + height: $search-box-height; + width: 0; + display: flex; + align-items: center; + + div.clean-box-view { + flex-shrink: 0; + height: 24px; + width: 24px; + display: flex; + justify-content: center; + align-items: center; + position: relative; + right: 24px; + border-radius: 3px; + user-select: none; + } + } +} + +div.dark.search-box-root { + + div.clean-box { + + div.clean-box-view:hover { + background-color: $lt-bg-color-lvl2-dark; + } + + div.clean-box-view { + background-color: $lt-bg-color-lvl3-dark; + } + } + + div.input-box input { + color: $lt-font-color-normal-dark; + } +} + +div.light.search-box-root { + + div.clean-box { + + div.clean-box-view:hover { + background-color: $lt-bg-color-lvl3-light; + } + + div.clean-box-view { + background-color: $lt-bg-color-lvl2-light; + } + } + + div.input-box input { + color: $lt-font-color-normal-light; + } +} \ No newline at end of file diff --git a/source/Component/SearchBox/SearchBox.tsx b/source/Component/SearchBox/SearchBox.tsx new file mode 100644 index 0000000..07c3ceb --- /dev/null +++ b/source/Component/SearchBox/SearchBox.tsx @@ -0,0 +1,60 @@ +import { AllI18nKeys, I18N } from "@Component/Localization/Localization"; +import { BackgroundLevel, FontLevel, Theme } from "@Component/Theme/Theme"; +import { useSettingWithEvent, IMixinSettingProps } from "@Context/Setting"; +import { Icon } from "@fluentui/react"; +import { Component, ReactNode } from "react"; +import "./SearchBox.scss"; + +interface ISearchBoxProps { + value?: string; + valueChange?: (value: string) => void; + placeholderI18N?: AllI18nKeys; + className?: string; +} + +@useSettingWithEvent("language") +class SearchBox extends Component { + + private renderCleanBox() { + return
+
{ + if (this.props.valueChange) { + this.props.valueChange("") + } + }} + > + +
+
; + } + + public render(): ReactNode { + return +
+ +
+
+ { + if (e.target instanceof HTMLInputElement && this.props.valueChange) { + this.props.valueChange(e.target.value) + } + }} + /> +
+ {this.props.value ? this.renderCleanBox() : null} +
+ } +} + +export { SearchBox }; \ No newline at end of file diff --git a/source/Localization/EN-US.ts b/source/Localization/EN-US.ts index 23c8276..8db5a9d 100644 --- a/source/Localization/EN-US.ts +++ b/source/Localization/EN-US.ts @@ -53,10 +53,12 @@ const EN_US = { "Popup.Delete.Objects.Confirm": "Are you sure you want to delete this object(s)? The object is deleted and cannot be recalled.", "Popup.Setting.Title": "Preferences setting", "Popup.Add.Behavior.Title": "Add behavior", + "Popup.Add.Behavior.Action.Add": "Add all select behavior", "Build.In.Label.Name.All.Group": "All group", "Build.In.Label.Name.All.Range": "All range", "Behavior.Template.Title": "Behavior", "Behavior.Template.Intro": "This is a template behavior", + "Common.Search.Placeholder": "Search in here...", "Common.No.Data": "No Data", "Common.No.Unknown.Error": "Unknown error", "Common.Attr.Title.Basic": "Basic properties", diff --git a/source/Localization/ZH-CN.ts b/source/Localization/ZH-CN.ts index 60b890e..69ff4ce 100644 --- a/source/Localization/ZH-CN.ts +++ b/source/Localization/ZH-CN.ts @@ -53,10 +53,12 @@ const ZH_CN = { "Popup.Delete.Objects.Confirm": "你确定要删除这个(些)对象吗?对象被删除将无法撤回。", "Popup.Setting.Title": "首选项设置", "Popup.Add.Behavior.Title": "添加行为", + "Popup.Add.Behavior.Action.Add": "添加全部选中行为", "Build.In.Label.Name.All.Group": "全部群", "Build.In.Label.Name.All.Range": "全部范围", "Behavior.Template.Title": "行为", "Behavior.Template.Intro": "这是一个模板行为", + "Common.Search.Placeholder": "在此处搜索...", "Common.No.Data": "暂无数据", "Common.No.Unknown.Error": "未知错误", "Common.Attr.Title.Basic": "基础属性",