Skip to content

Commit d641d63

Browse files
committed
Fix window closing
1 parent 74699dc commit d641d63

3 files changed

Lines changed: 108 additions & 10 deletions

File tree

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import { createAccessorModelProxy } from "cx/data";
2+
import { LabelsLeftLayout } from "cx/ui";
3+
import { Button, Window, TextField, TextArea, DateField } from "cx/widgets";
4+
5+
// @model
6+
interface PageModel {
7+
contact: {
8+
visible: boolean;
9+
name: string;
10+
email: string;
11+
message: string;
12+
date: string;
13+
};
14+
}
15+
16+
const m = createAccessorModelProxy<PageModel>();
17+
// @model-end
18+
19+
// @index
20+
export default () => (
21+
<div>
22+
<Button
23+
onClick={(e, { store }) => {
24+
store.set(m.contact.visible, true);
25+
}}
26+
>
27+
Open Window
28+
</Button>
29+
<Window
30+
title="Contact"
31+
visible={m.contact.visible}
32+
center
33+
style={{ width: "500px" }}
34+
modal
35+
draggable
36+
closeOnEscape
37+
>
38+
<div style={{ padding: "20px" }}>
39+
<LabelsLeftLayout>
40+
<TextField value={m.contact.name} label="Name" />
41+
<TextField value={m.contact.email} label="Email" />
42+
<TextArea value={m.contact.message} label="Message" rows={5} />
43+
<DateField value={m.contact.date} label="Date" />
44+
</LabelsLeftLayout>
45+
</div>
46+
<div
47+
putInto="footer"
48+
style={{ display: "flex", gap: "8px", justifyContent: "flex-end" }}
49+
>
50+
<Button mod="primary">Submit</Button>
51+
<Button dismiss>Cancel</Button>
52+
</div>
53+
</Window>
54+
</div>
55+
);
56+
// @index-end
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
---
2+
layout: ../../../layouts/DocsLayout.astro
3+
title: Window
4+
---
5+
6+
import ImportPath from "../../../components/ImportPath.astro";
7+
import CodeExample from "../../../components/CodeExample.astro";
8+
9+
import WindowExample from "../../../examples/layout/WindowExample.tsx";
10+
import WindowExampleCode from "../../../examples/layout/WindowExample.tsx?raw";
11+
12+
# Window
13+
14+
<ImportPath path="import { Window } from 'cx/widgets';" />
15+
16+
Windows are overlays with headers, footers, and special appearance. They provide a familiar dialog-like experience for displaying forms, messages, or complex content.
17+
18+
<CodeExample code={WindowExampleCode}>
19+
<WindowExample client:load />
20+
</CodeExample>
21+
22+
Use `putInto="footer"` to place content in the window footer. Add the `dismiss` prop to buttons that should close the window.
23+
24+
## Configuration
25+
26+
| Property | Type | Description |
27+
| -------- | ---- | ----------- |
28+
| `title` | `string` | Text displayed in the header. |
29+
| `header` | `object` | Custom Cx component for advanced headers. |
30+
| `visible` | `boolean` | Controls window visibility. |
31+
| `modal` | `boolean` | Adds a backdrop that masks mouse events for the rest of the page. |
32+
| `backdrop` | `boolean` | Adds a backdrop that dismisses the window when clicked. |
33+
| `center` | `boolean` | Centers the window on the page. |
34+
| `draggable` | `boolean` | Enables dragging the window by its header. |
35+
| `resizable` | `boolean` | Enables window resizing. |
36+
| `closable` | `boolean` | Controls close button visibility. Defaults to `true`. |
37+
| `closeOnEscape` | `boolean` | Closes the window when Escape key is pressed. |
38+
| `fixed` | `boolean` | Disables moving the window by dragging the header. |
39+
| `pad` | `boolean` | Adds default padding to the window body. |

packages/cx/src/data/Binding.ts

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,11 @@ export interface BindingObject {
1313
debounce?: number;
1414
}
1515

16-
export type BindingInput<T = any> = string | BindingObject | Binding<T> | AccessorChain<T>;
16+
export type BindingInput<T = any> =
17+
| string
18+
| BindingObject
19+
| Binding<T>
20+
| AccessorChain<T>;
1721

1822
export type ValueGetter<T> = (state: any) => T | undefined;
1923

@@ -26,7 +30,8 @@ export class Binding<T = any> {
2630
this.path = path;
2731
this.parts = path.split(".") as readonly string[];
2832
let body = "return x";
29-
for (let i = 0; i < this.parts.length; i++) body += '?.["' + this.parts[i] + '"]';
33+
for (let i = 0; i < this.parts.length; i++)
34+
body += '?.["' + this.parts[i] + '"]';
3035
this.value = new Function("x", body) as ValueGetter<T>;
3136
}
3237

@@ -39,7 +44,8 @@ export class Binding<T = any> {
3944

4045
for (let i = 0; i < this.parts.length; i++) {
4146
const part = this.parts[i];
42-
const no = i === this.parts.length - 1 ? value : Object.assign({}, o[part]);
47+
const no =
48+
i === this.parts.length - 1 ? value : Object.assign({}, o[part]);
4349
o[part] = no;
4450
o = no;
4551
}
@@ -81,17 +87,16 @@ export class Binding<T = any> {
8187

8288
if (path instanceof Binding) return path as Binding<T>;
8389

84-
if (isAccessorChain(path)) return this.get<T>((path as AccessorChain<T>).toString());
90+
if (isAccessorChain(path))
91+
return this.get<T>((path as AccessorChain<T>).toString());
8592

8693
throw new Error("Invalid binding definition provided.");
8794
}
8895
}
8996

9097
export function isBinding(value: unknown): value is BindingInput {
91-
if (isObject(value)) {
92-
if (hasStringAtKey(value, "bind")) return true;
93-
if (hasValueAtKey(value, "isAccessorChain", true)) return true;
94-
}
98+
if (isObject(value) && hasStringAtKey(value, "bind")) return true;
99+
if (isAccessorChain(value)) return true;
95100
return value instanceof Binding;
96101
}
97102

@@ -100,5 +105,3 @@ export type BindingValue<B> = B extends Binding<infer T> ? T : unknown;
100105
export function isBindingObject(value: unknown): value is BindingObject {
101106
return isObject(value) && hasStringAtKey(value, "bind");
102107
}
103-
104-
export { bindingCache };

0 commit comments

Comments
 (0)