diff --git a/source/Component/CommandBar/CommandBar.tsx b/source/Component/CommandBar/CommandBar.tsx index bd21180..e6d38be 100644 --- a/source/Component/CommandBar/CommandBar.tsx +++ b/source/Component/CommandBar/CommandBar.tsx @@ -4,6 +4,7 @@ import { LocalizationTooltipHost } from "../Localization/LocalizationTooltipHost import { useSetting, IMixinSettingProps } from "@Context/Setting"; import { useStatusWithEvent, IMixinStatusProps } from "@Context/Status"; import { AllI18nKeys } from "../Localization/Localization"; +import { ConfirmPopup } from "@Component/ConfirmPopup/ConfirmPopup"; import { Component, ReactNode } from "react"; import { MouseMod } from "@GLRender/ClassicRenderer"; import "./CommandBar.scss"; @@ -72,7 +73,7 @@ class CommandBar extends Component { - this.props.status ? this.props.status.popup.showPopup() : undefined; + // this.props.status?.popup.showPopup(ConfirmPopup, {}); } })} diff --git a/source/Component/ConfirmPopup/ConfirmPopup.scss b/source/Component/ConfirmPopup/ConfirmPopup.scss new file mode 100644 index 0000000..76e46ae --- /dev/null +++ b/source/Component/ConfirmPopup/ConfirmPopup.scss @@ -0,0 +1,67 @@ +@import "../Theme/Theme.scss"; + +div.confirm-root { + width: 100%; + height: 100%; + + div.content-views { + width: 100%; + height: calc( 100% - 36px ); + box-sizing: border-box; + padding: 10px; + } + + div.action-view { + width: 100%; + height: 36px; + display: flex; + box-sizing: border-box; + padding-right: 5px; + padding-bottom: 10px; + justify-content: flex-end; + align-items: center; + + div.action-button { + height: 26px; + padding: 0 10px; + border-radius: 3px; + margin: 0 5px; + display: flex; + align-items: center; + cursor: pointer; + user-select: none; + } + + div.action-button.red { + color: $lt-red; + } + } +} + +div.dark.confirm-root { + + div.action-view { + + div.action-button { + background-color: $lt-bg-color-lvl3-dark; + } + + div.action-button:hover { + background-color: $lt-bg-color-lvl2-dark; + } + } +} + +div.light.confirm-root { + + div.action-view { + + div.action-button { + background-color: $lt-bg-color-lvl3-light; + } + + div.action-button:hover { + background-color: $lt-bg-color-lvl2-light; + } + } +} \ No newline at end of file diff --git a/source/Component/ConfirmPopup/ConfirmPopup.tsx b/source/Component/ConfirmPopup/ConfirmPopup.tsx new file mode 100644 index 0000000..b530ea8 --- /dev/null +++ b/source/Component/ConfirmPopup/ConfirmPopup.tsx @@ -0,0 +1,60 @@ +import { Popup } from "@Context/Popups"; +import { ReactNode } from "react"; +import { Message } from "@Component/Message/Message"; +import { Theme } from "@Component/Theme/Theme"; +import { AllI18nKeys, Localization } from "@Component/Localization/Localization"; +import "./ConfirmPopup.scss"; + +interface IConfirmPopupProps { + titleI18N?: AllI18nKeys; + infoI18n: AllI18nKeys; + yesI18n?: AllI18nKeys; + noI18n?: AllI18nKeys; + yes?: () => any; + no?: () => any; + red?: "yes" | "no"; +} +class ConfirmPopup extends Popup { + + public width: number = 300; + + public height: number = 180; + + public onRenderHeader(): ReactNode { + return + } + + 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"); + } + if (this.props.red === "yes") { + yesClassList.push("red"); + } + + return +
+ +
+
+
{ + this.props.yes ? this.props.yes() : null; + this.close(); + }}> + +
+
{ + this.props.no ? this.props.no() : null; + this.close(); + }}> + +
+
+
; + } +} + +export { ConfirmPopup } \ No newline at end of file diff --git a/source/Component/Popup/Popup.scss b/source/Component/Popup/Popup.scss index a39b8d6..77203ec 100644 --- a/source/Component/Popup/Popup.scss +++ b/source/Component/Popup/Popup.scss @@ -34,15 +34,31 @@ div.popup-layer.show-scale { div.popup-mask { position: absolute; - cursor: pointer; width: 100%; height: 100%; } +div.focus.popup-layer { + border: 0.8px solid #514feb; +} + div.popup-layer { position: absolute; border-radius: 3px; - overflow: hidden; + transition: none; + box-sizing: border-box; + border: 0.8px solid transparent; + + div.popup-layer-container { + width: 100%; + height: 100%; + display: flex; + } + + div.popup-layer-root-content { + width: 100%; + height: 100%; + } div.popup-layer-header { min-height: $header-height; @@ -59,9 +75,9 @@ div.popup-layer { user-select: none; span { - padding-left: 8px; + padding-left: 10px; display: inline-block; - vertical-align: middle; + vertical-align: bottom; white-space: nowrap; text-overflow: ellipsis; overflow: hidden; @@ -79,12 +95,56 @@ div.popup-layer { cursor: pointer; } } + + div.popup-layer-content { + height: calc( 100% - 32px ); + width: 100%; + overflow: hidden; + } + + div.drag-line-root.drag-line-y { + flex-direction: column; + } + + div.drag-line-root { + display: flex; + justify-content: center; + align-items: center; + position: relative; + + div.drag-line { + transition: all 300ms ease-in-out; + display: flex; + } + + div.render-drag-block-root { + height: 0; + width: 0; + display: flex; + justify-content: center; + align-items: center; + + div.render-drag-block { + min-width: 5px; + min-height: 5px; + position: relative; + } + } + + div.drag-line:hover { + background-color: $lt-blue; + } + + div.drag-line.hover { + background-color: $lt-blue; + } + } } div.popup-layer.dark { + box-shadow: 0 0 15px rgba(0, 0, 0, 0.3); div.popup-layer-header { - background-color: $lt-bg-color-lvl3-dark; div.header-close-icon:hover { background-color: $lt-bg-color-lvl2-dark; @@ -94,9 +154,9 @@ div.popup-layer.dark { } div.popup-layer.light { + box-shadow: 0 0 15px rgba(0, 0, 0, 0.15); div.popup-layer-header { - background-color: $lt-bg-color-lvl3-light; div.header-close-icon:hover { background-color: $lt-bg-color-lvl2-light; diff --git a/source/Component/Popup/Popup.tsx b/source/Component/Popup/Popup.tsx index 2bfaefe..8d9e0c5 100644 --- a/source/Component/Popup/Popup.tsx +++ b/source/Component/Popup/Popup.tsx @@ -1,18 +1,22 @@ import { Component, ReactNode } from "react"; import { IMixinStatusProps, useStatusWithEvent } from "@Context/Status"; -import { BackgroundLevel, Theme } from "@Component/Theme/Theme"; -import { Popup as PopupModel } from "@Context/Popups"; +import { IMixinSettingProps, useSettingWithEvent } from "@Context/Setting"; +import { BackgroundLevel, FontLevel, getClassList, Theme } from "@Component/Theme/Theme"; +import { Popup as PopupModel, ResizeDragDirection } from "@Context/Popups"; import { Icon } from "@fluentui/react"; import "./Popup.scss"; interface IPopupProps {} +@useSettingWithEvent("themes") @useStatusWithEvent("popupChange") -class Popup extends Component { +class Popup extends Component { - public renderMask(index?: number, click?: () => void, key?: string): ReactNode { - const classList: string[] = ["popup-mask", "show-fade"]; - return void, key?: string): ReactNode { + const classList: string[] = ["popup-mask", "show-fade", + ...getClassList({}, this.props.setting) + ]; + return
{ /> } - public renderRootMask(): ReactNode { + private renderRootMask(): ReactNode { if (this.props.status) { const needMask = this.props.status.popup.popups.some(popup => popup.needMask); if (!needMask) return null; @@ -38,7 +42,7 @@ class Popup extends Component { } } - public renderMaskList(): ReactNode { + private renderMaskList(): ReactNode { if (this.props.status) { return this.props.status.popup.popups .filter((popup) => { @@ -60,9 +64,22 @@ class Popup extends Component { } } - public renderHeader(popup: PopupModel): ReactNode { - return
-
+ private renderHeader(popup: PopupModel): ReactNode { + return
+
{ + popup.isOnMouseDown = true; + popup.lastMouseLeft = e.clientX; + popup.lastMouseTop = e.clientY; + }} + > {popup.onRenderHeader()}
{
} - public renderLayer(popup: PopupModel) { + private renderContent(popup: PopupModel) { + return
+ {popup.render()} +
+ } + + private renderDragBlock(dir: ResizeDragDirection, popup: PopupModel) { + return
+
{ + popup.lastMouseLeft = e.clientX; + popup.lastMouseTop = e.clientY; + popup.resizeDragDirection = dir; + popup.isResizeMouseDown = true; + }} + onMouseEnter={() => { + popup.resizeHoverDirection = dir; + this.forceUpdate(); + }} + onMouseLeave={() => { + popup.resizeHoverDirection = undefined; + this.forceUpdate(); + }} + /> +
+ } + + private mapDirToCursor = new Map([ + [ResizeDragDirection.rightTop, "sw-resize"], + [ResizeDragDirection.rightBottom, "nw-resize"], + [ResizeDragDirection.leftBottom, "sw-resize"], + [ResizeDragDirection.LeftTop, "nw-resize"] + ]); + + private renderDragLine(dir: ResizeDragDirection, popup: PopupModel) { + let xy: boolean = false; + const dragLineCList: string[] = ["drag-line"]; + + if (dir === ResizeDragDirection.top || dir === ResizeDragDirection.bottom) { + xy = false; + } + if (dir === ResizeDragDirection.left || dir === ResizeDragDirection.right) { + xy = true; + } + if ( + ( + dir === ResizeDragDirection.top && + ( + popup.resizeHoverDirection === ResizeDragDirection.LeftTop || + popup.resizeHoverDirection === ResizeDragDirection.rightTop + ) + ) || + ( + dir === ResizeDragDirection.bottom && + ( + popup.resizeHoverDirection === ResizeDragDirection.leftBottom || + popup.resizeHoverDirection === ResizeDragDirection.rightBottom + ) + ) || + ( + dir === ResizeDragDirection.right && + ( + popup.resizeHoverDirection === ResizeDragDirection.rightTop || + popup.resizeHoverDirection === ResizeDragDirection.rightBottom + ) + ) || + ( + dir === ResizeDragDirection.left && + ( + popup.resizeHoverDirection === ResizeDragDirection.leftBottom || + popup.resizeHoverDirection === ResizeDragDirection.LeftTop + ) + ) + ) { + dragLineCList.push("hover") + } + + return
+ { + xy && dir === ResizeDragDirection.left ? this.renderDragBlock( + ResizeDragDirection.LeftTop, popup + ) : null + } + { + xy && dir === ResizeDragDirection.right ? this.renderDragBlock( + ResizeDragDirection.rightTop, popup + ) : null + } + { + !xy && dir === ResizeDragDirection.bottom ? this.renderDragBlock( + ResizeDragDirection.leftBottom, popup + ) : null + } +
{ + popup.lastMouseLeft = e.clientX; + popup.lastMouseTop = e.clientY; + popup.resizeDragDirection = dir; + popup.isResizeMouseDown = true; + }} + /> + { + !xy && dir === ResizeDragDirection.bottom ? this.renderDragBlock( + ResizeDragDirection.rightBottom, popup + ) : null + } +
+ } + + private renderLayer(popup: PopupModel) { const pageWidth = document.documentElement.clientWidth; const pageHeight = document.documentElement.clientHeight; - const top = (pageHeight - popup.height) / 2; - const left = (pageWidth - popup.width) / 2; + if (isNaN(popup.top)) { + popup.top = (pageHeight - popup.height) / 2; + } + if (isNaN(popup.left)) { + popup.left = (pageWidth - popup.width) / 2; + } return - {this.renderHeader(popup)} + {this.renderDragLine(ResizeDragDirection.top, popup)} +
+ {this.renderDragLine(ResizeDragDirection.left, popup)} +
+ {this.renderHeader(popup)} + {this.renderContent(popup)} +
+ {this.renderDragLine(ResizeDragDirection.right, popup)} +
+ {this.renderDragLine(ResizeDragDirection.bottom, popup)}
} + private isMouseDown: boolean = false; + + private handelMouseDown = () => { + this.isMouseDown = true; + } + + private handelMouseUp = () => { + this.isMouseDown = false; + if (this.props.status) { + this.props.status.popup.popups.forEach((popup) => { + popup.isOnMouseDown = false; + popup.resizeDragDirection = undefined; + popup.isResizeMouseDown = false; + }); + } + } + + private resize(popup: PopupModel, dis: number, dir: boolean, lsk: boolean) { + + if (dir) { + // Y + popup.isResizeOverFlowY = false; + const heightBackup = popup.height + const topBackup = popup.top; + if (lsk) { + popup.height += dis; + } else { + popup.top += dis; + popup.height -= dis; + } + if (popup.height < popup.minHeight) { + popup.height = heightBackup; + popup.top = topBackup; + popup.isResizeOverFlowY = true; + } + } else { + // X + popup.isResizeOverFlowX = false; + const leftBackup = popup.left + const widthBackup = popup.width; + if (lsk) { + popup.width += dis; + } else { + popup.left += dis; + popup.width -= dis; + } + if (popup.width < popup.minWidth) { + popup.width = widthBackup; + popup.left = leftBackup; + popup.isResizeOverFlowX = true; + } + } + } + + private handelMouseMove = (e: MouseEvent) => { + let isActionSuccess: boolean = false; + if ( + this.isMouseDown && + this.props.status && + this.props.status.popup.popups.some(popup => popup.isOnMouseDown) + ) { + this.props.status.popup.popups.forEach((popup) => { + if (popup.isOnMouseDown) { + popup.top += e.clientY - popup.lastMouseTop; + popup.left += e.clientX - popup.lastMouseLeft; + popup.lastMouseLeft = e.clientX; + popup.lastMouseTop = e.clientY; + isActionSuccess = true; + this.forceUpdate(); + } + }); + } + if (this.props.status) { + this.props.status.popup.popups.forEach((popup) => { + if (popup.resizeDragDirection) { + + let moveX = e.clientX - popup.lastMouseLeft; + let moveY = e.clientY - popup.lastMouseTop; + switch (popup.resizeDragDirection) { + + case ResizeDragDirection.LeftTop: + this.resize(popup, moveX, false, false); + this.resize(popup, moveY, true, false); + break; + + case ResizeDragDirection.leftBottom: + this.resize(popup, moveX, false, false); + this.resize(popup, moveY, true, true); + break; + + case ResizeDragDirection.rightTop: + this.resize(popup, moveX, false, true); + this.resize(popup, moveY, true, false); + break; + + case ResizeDragDirection.rightBottom: + this.resize(popup, moveX, false, true); + this.resize(popup, moveY, true, true); + break; + + case ResizeDragDirection.top: + this.resize(popup, moveY, true, false); + break; + + case ResizeDragDirection.left: + this.resize(popup, moveX, false, false); + break; + + case ResizeDragDirection.bottom: + this.resize(popup, moveY, true, true); + break; + + case ResizeDragDirection.right: + this.resize(popup, moveX, false, true); + break; + } + if (!popup.isResizeOverFlowX) { + popup.lastMouseLeft = e.clientX; + } + if (!popup.isResizeOverFlowY) { + popup.lastMouseTop = e.clientY; + } + isActionSuccess = true; + this.forceUpdate(); + } + }); + } + if (isActionSuccess) { + e.preventDefault(); + } + } + + public componentDidMount() { + window.addEventListener("mousemove", this.handelMouseMove); + window.addEventListener("mousedown", this.handelMouseDown); + window.addEventListener("mouseup", this.handelMouseUp); + } + + public componentWillUnmount() { + window.removeEventListener("mousemove", this.handelMouseMove); + window.removeEventListener("mousedown", this.handelMouseDown); + window.removeEventListener("mouseup", this.handelMouseUp); + } + public render(): ReactNode { return <> {this.renderRootMask()} diff --git a/source/Component/Theme/Theme.tsx b/source/Component/Theme/Theme.tsx index 20669ba..bea5f42 100644 --- a/source/Component/Theme/Theme.tsx +++ b/source/Component/Theme/Theme.tsx @@ -1,4 +1,4 @@ -import { useSetting, Themes, IMixinSettingProps } from "@Context/Setting"; +import { useSettingWithEvent, Themes, IMixinSettingProps, Setting } from "@Context/Setting"; import { Component, ReactNode, DetailedHTMLProps, HTMLAttributes } from "react"; import "./Theme.scss"; @@ -21,12 +21,33 @@ interface IThemeProps { className?: string; fontLevel?: FontLevel; backgroundLevel?: BackgroundLevel; -} +} + +function getClassList(props: IThemeProps, setting?: Setting) { + const classNameList: string[] = []; + + if (props.className) { + classNameList.push(props.className); + } + + const theme = setting ? setting.themes : Themes.dark; + classNameList.push(theme === Themes.light ? "light" : "dark"); + + if (props.fontLevel) { + classNameList.push(`font-${props.fontLevel}`); + } + + if (props.backgroundLevel) { + classNameList.push(`background-${props.backgroundLevel}`); + } + + return classNameList; +} /** * 主题切换 */ -@useSetting +@useSettingWithEvent("themes") class Theme extends Component< IThemeProps & IMixinSettingProps & DetailedHTMLProps< HTMLAttributes, HTMLDivElement @@ -52,22 +73,7 @@ class Theme extends Component< public render(): ReactNode { const setting = this.props.setting; - const classNameList: string[] = []; - - if (this.props.className) { - classNameList.push(this.props.className); - } - - const theme = setting ? setting.themes : Themes.dark; - classNameList.push(theme === Themes.light ? "light" : "dark"); - - if (this.props.fontLevel) { - classNameList.push(`font-${this.props.fontLevel}`); - } - - if (this.props.backgroundLevel) { - classNameList.push(`background-${this.props.backgroundLevel}`); - } + const classNameList = getClassList(this.props, setting); const propsObj = {...this.props}; delete propsObj.className; @@ -82,4 +88,4 @@ class Theme extends Component< } export default Theme; -export { Theme, FontLevel, BackgroundLevel }; \ No newline at end of file +export { Theme, FontLevel, BackgroundLevel, getClassList }; \ No newline at end of file diff --git a/source/Component/TogglesInput/TogglesInput.scss b/source/Component/TogglesInput/TogglesInput.scss index b89fa89..adb948c 100644 --- a/source/Component/TogglesInput/TogglesInput.scss +++ b/source/Component/TogglesInput/TogglesInput.scss @@ -15,6 +15,10 @@ div.toggles-input { cursor: pointer; user-select: none; } + + div.checkbox.red:hover { + color: $lt-red !important; + } } div.dark.text-field-root { diff --git a/source/Component/TogglesInput/TogglesInput.tsx b/source/Component/TogglesInput/TogglesInput.tsx index a355ddb..bf96f22 100644 --- a/source/Component/TogglesInput/TogglesInput.tsx +++ b/source/Component/TogglesInput/TogglesInput.tsx @@ -8,6 +8,7 @@ interface ITogglesInputProps extends ITextFieldProps { onIconName?: string; offIconName?: string; valueChange?: (value: boolean) => any; + red?: boolean; } class TogglesInput extends Component { @@ -20,7 +21,7 @@ class TogglesInput extends Component { customStyle >
Popup; +enum ResizeDragDirection { + top = 1, + rightTop = 2, + right = 3, + rightBottom = 4, + bottom = 5, + leftBottom = 6, + left = 7, + LeftTop = 8 +} /** * 弹窗类型 */ -class Popup { +class Popup

{ + + public props: P; + + public constructor(props: P) { + this.props = props; + } public zIndex() { - return this.index * 2 + this.controller.zIndex; + return this.index * 5 + this.controller.zIndex; } public width: number = 300; - public height: number = 200; - - public top: number = 0; - - public left: number = 0; + public minWidth: number = 300; + public minHeight: number = 200; + public top: number = NaN; + public left: number = NaN; + public lastMouseTop: number = 0; + public lastMouseLeft: number = 0; + public isOnMouseDown: boolean = false; + public resizeHoverDirection?: ResizeDragDirection; + public resizeDragDirection?: ResizeDragDirection; + public isResizeMouseDown: boolean = false; + public isResizeOverFlowX: boolean = false; + public isResizeOverFlowY: boolean = false; /** * 是否关闭 @@ -39,23 +62,18 @@ class Popup { /** * 唯一标识符 */ - public id: string; + public id: string = ""; /** * 控制器 */ - public controller: PopupController; + public controller: PopupController = undefined as any; /** * 渲染层级 */ public index: number = Infinity; - /** - * react 节点 - */ - public reactNode: ReactNode; - /** * 渲染标题 */ @@ -63,13 +81,6 @@ class Popup { return createElement(Localization, {i18nKey: "Popup.Title.Unnamed"}); } - /** - * 渲染函数 - */ - public onRender(p: Popup): ReactNode { - return null; - } - /** * 关闭回调 */ @@ -81,15 +92,14 @@ class Popup { * 渲染节点 */ public render(): ReactNode { - this.reactNode = this.onRender(this); - return this.reactNode; + return null; }; - public close() { + public close(): Popup | undefined { return this.controller.closePopup(this); } - public constructor(controller: PopupController, id: string) { + public init(controller: PopupController, id: string) { this.controller = controller; this.id = id; } @@ -134,8 +144,16 @@ class PopupController extends Emitter { /** * 实例化并开启一个弹窗 */ - public showPopup

(popup?: P): Popup { - let newPopup = new (popup ?? Popup)(this, `P-${this.idIndex ++}`); + public showPopup

>( + popup: (new (props: P) => T) | Popup

, props: P + ): Popup

{ + let newPopup: Popup

; + if (popup instanceof Popup) { + newPopup = popup; + } else { + newPopup = new (popup ?? Popup)(props); + } + newPopup.init(this, `P-${this.idIndex ++}`); this.popups.push(newPopup); this.sortPopup(); return newPopup; @@ -172,4 +190,4 @@ class PopupController extends Emitter { } } -export { Popup, PopupController } \ No newline at end of file +export { Popup, PopupController, ResizeDragDirection } \ No newline at end of file diff --git a/source/Localization/EN-US.ts b/source/Localization/EN-US.ts index 8e4ceb1..8fbaa52 100644 --- a/source/Localization/EN-US.ts +++ b/source/Localization/EN-US.ts @@ -45,6 +45,12 @@ const EN_US = { "Panel.Title.Group.Details.View": "Group", "Panel.Info.Group.Details.View": "Edit view group attributes", "Popup.Title.Unnamed": "Popup message", + "Popup.Title.Confirm": "Confirm message", + "Popup.Action.Yes": "Confirm", + "Popup.Action.No": "Cancel", + "Popup.Action.Objects.Confirm.Title": "Confirm Delete", + "Popup.Action.Objects.Confirm.Delete": "Delete", + "Popup.Delete.Objects.Confirm": "Are you sure you want to delete this object(s)? The object is deleted and cannot be recalled.", "Build.In.Label.Name.All.Group": "All group", "Build.In.Label.Name.All.Range": "All range", "Common.No.Data": "No Data", diff --git a/source/Localization/ZH-CN.ts b/source/Localization/ZH-CN.ts index 784e522..3e2a883 100644 --- a/source/Localization/ZH-CN.ts +++ b/source/Localization/ZH-CN.ts @@ -45,6 +45,12 @@ const ZH_CN = { "Panel.Title.Group.Details.View": "群", "Panel.Info.Group.Details.View": "编辑查看群属性", "Popup.Title.Unnamed": "弹窗消息", + "Popup.Title.Confirm": "确认消息", + "Popup.Action.Yes": "确定", + "Popup.Action.No": "取消", + "Popup.Action.Objects.Confirm.Title": "删除确认", + "Popup.Action.Objects.Confirm.Delete": "删除", + "Popup.Delete.Objects.Confirm": "你确定要删除这个(些)对象吗?对象被删除将无法撤回。", "Build.In.Label.Name.All.Group": "全部群", "Build.In.Label.Name.All.Range": "全部范围", "Common.No.Data": "暂无数据", diff --git a/source/Panel/GroupDetails/GroupDetails.tsx b/source/Panel/GroupDetails/GroupDetails.tsx index b37221a..2010964 100644 --- a/source/Panel/GroupDetails/GroupDetails.tsx +++ b/source/Panel/GroupDetails/GroupDetails.tsx @@ -10,6 +10,7 @@ import { Group, GenMod } from "@Model/Group"; import { AllI18nKeys } from "@Component/Localization/Localization"; import { ComboInput, IDisplayItem } from "@Component/ComboInput/ComboInput"; import { ObjectPicker } from "@Component/ObjectPicker/ObjectPicker"; +import { ConfirmPopup } from "@Component/ConfirmPopup/ConfirmPopup"; import "./GroupDetails.scss"; interface IGroupDetailsProps {} @@ -88,12 +89,21 @@ class GroupDetails extends Component { /> { if (this.props.status) { - this.props.status.model.deleteObject([group]); - this.props.status.setFocusObject(new Set()); + const status = this.props.status; + status.popup.showPopup(ConfirmPopup, { + infoI18n: "Popup.Delete.Objects.Confirm", + titleI18N: "Popup.Action.Objects.Confirm.Title", + yesI18n: "Popup.Action.Objects.Confirm.Delete", + red: "yes", + yes: () => { + status.model.deleteObject([group]); + status.setFocusObject(new Set()); + } + }) } }} /> @@ -147,7 +157,7 @@ class GroupDetails extends Component { /> { group.killIndividuals() diff --git a/source/Panel/LabelDetails/LabelDetails.tsx b/source/Panel/LabelDetails/LabelDetails.tsx index 28691db..5b55fba 100644 --- a/source/Panel/LabelDetails/LabelDetails.tsx +++ b/source/Panel/LabelDetails/LabelDetails.tsx @@ -5,6 +5,7 @@ import { Message } from "@Component/Message/Message"; import { ColorInput } from "@Component/ColorInput/ColorInput"; import { Label } from "@Model/Label"; import { TogglesInput } from "@Component/TogglesInput/TogglesInput"; +import { ConfirmPopup } from "@Component/ConfirmPopup/ConfirmPopup"; import "./LabelDetails.scss"; @useStatusWithEvent("focusLabelChange", "labelAttrChange", "labelChange") @@ -27,10 +28,21 @@ class LabelDetails extends Component { } }}/> - { + { if (this.props.status) { - this.props.status.model.deleteLabel(label); - this.props.status.setLabelObject(); + const status = this.props.status; + status.popup.showPopup(ConfirmPopup, { + infoI18n: "Popup.Delete.Objects.Confirm", + titleI18N: "Popup.Action.Objects.Confirm.Title", + yesI18n: "Popup.Action.Objects.Confirm.Delete", + red: "yes", + yes: () => { + status.model.deleteLabel(label); + status.setLabelObject(); + } + }) } }}/> diff --git a/source/Panel/LabelList/LabelList.tsx b/source/Panel/LabelList/LabelList.tsx index f1f9a80..51704a5 100644 --- a/source/Panel/LabelList/LabelList.tsx +++ b/source/Panel/LabelList/LabelList.tsx @@ -4,6 +4,7 @@ import { useStatusWithEvent, IMixinStatusProps } from "@Context/Status"; import { useSetting, IMixinSettingProps } from "@Context/Setting"; import { Label } from "@Model/Label"; import { Message } from "@Component/Message/Message"; +import { ConfirmPopup } from "@Component/ConfirmPopup/ConfirmPopup"; import "./LabelList.scss"; interface ILabelListProps { @@ -47,8 +48,17 @@ class LabelList extends Component { if (this.props.status) { - this.props.status.model.deleteLabel(label); - this.props.status.setLabelObject(); + const status = this.props.status; + status.popup.showPopup(ConfirmPopup, { + infoI18n: "Popup.Delete.Objects.Confirm", + titleI18N: "Popup.Action.Objects.Confirm.Title", + yesI18n: "Popup.Action.Objects.Confirm.Delete", + red: "yes", + yes: () => { + status.model.deleteLabel(label); + status.setLabelObject(); + } + }) } this.labelInnerClick = true; }} diff --git a/source/Panel/ObjectList/ObjectCommand.tsx b/source/Panel/ObjectList/ObjectCommand.tsx index 71cbe42..0450399 100644 --- a/source/Panel/ObjectList/ObjectCommand.tsx +++ b/source/Panel/ObjectList/ObjectCommand.tsx @@ -1,5 +1,6 @@ import { BackgroundLevel, FontLevel, Theme } from "@Component/Theme/Theme"; import { useStatus, IMixinStatusProps } from "../../Context/Status"; +import { ConfirmPopup } from "@Component/ConfirmPopup/ConfirmPopup"; import { Icon } from "@fluentui/react"; import { Component, ReactNode } from "react"; import { ObjectID } from "@Model/Renderer"; @@ -54,15 +55,24 @@ class ObjectCommand extends Component {

{ - if (this.props.status) { - let deleteId: ObjectID[] = []; - this.props.status.focusObject.forEach((obj) => { - deleteId.push(obj); + if (this.props.status && this.props.status.focusObject.size > 0) { + const status = this.props.status; + status.popup.showPopup(ConfirmPopup, { + infoI18n: "Popup.Delete.Objects.Confirm", + titleI18N: "Popup.Action.Objects.Confirm.Title", + yesI18n: "Popup.Action.Objects.Confirm.Delete", + red: "yes", + yes: () => { + let deleteId: ObjectID[] = []; + status.focusObject.forEach((obj) => { + deleteId.push(obj); + }) + status.model.deleteObject(deleteId); + status.setFocusObject(new Set()); + } }) - this.props.status.model.deleteObject(deleteId); - this.props.status.setFocusObject(new Set()); } }} > diff --git a/source/Panel/ObjectList/ObjectList.scss b/source/Panel/ObjectList/ObjectList.scss index b98f127..e0e197a 100644 --- a/source/Panel/ObjectList/ObjectList.scss +++ b/source/Panel/ObjectList/ObjectList.scss @@ -59,6 +59,10 @@ div.object-list-command-bar { user-select: none; cursor: pointer; } + + div.command-item.red:hover { + color: $lt-red; + } } div.dark.object-list-command-bar { diff --git a/source/Panel/RangeDetails/RangeDetails.tsx b/source/Panel/RangeDetails/RangeDetails.tsx index ff9adb3..f5aeee9 100644 --- a/source/Panel/RangeDetails/RangeDetails.tsx +++ b/source/Panel/RangeDetails/RangeDetails.tsx @@ -1,13 +1,13 @@ import { Component, ReactNode } from "react"; import { AttrInput } from "@Component/AttrInput/AttrInput"; import { useStatusWithEvent, IMixinStatusProps, Status } from "@Context/Status"; -import { AllI18nKeys } from "@Component/Localization/Localization"; import { Message } from "@Component/Message/Message"; import { Range } from "@Model/Range"; import { ObjectID } from "@Model/Renderer"; import { ColorInput } from "@Component/ColorInput/ColorInput"; import { TogglesInput } from "@Component/TogglesInput/TogglesInput"; import { LabelPicker } from "@Component/LabelPicker/LabelPicker"; +import { ConfirmPopup } from "@Component/ConfirmPopup/ConfirmPopup"; import "./RangeDetails.scss"; @useStatusWithEvent("rangeAttrChange", "focusObjectChange", "rangeLabelChange") @@ -53,12 +53,21 @@ class RangeDetails extends Component { /> { - if (this.props.status) { - this.props.status.model.deleteObject([range]); - this.props.status.setFocusObject(new Set()); + if (this.props.status) { + const status = this.props.status; + status.popup.showPopup(ConfirmPopup, { + infoI18n: "Popup.Delete.Objects.Confirm", + titleI18N: "Popup.Action.Objects.Confirm.Title", + yesI18n: "Popup.Action.Objects.Confirm.Delete", + red: "yes", + yes: () => { + status.model.deleteObject([range]); + status.setFocusObject(new Set()); + } + }) } }} />