Line data Source code
1 : /*
2 : * Copyright (C) 2025 Red Hat, Inc.
3 : * SPDX-License-Identifier: LGPL-2.1-or-later
4 : */
5 :
6 2 : import cockpit from "cockpit";
7 2 : import React from "react";
8 2 : import { createRoot, Container } from 'react-dom/client';
9 :
10 : import '../lib/patternfly/patternfly-6-cockpit.scss';
11 :
12 : import { Page, PageSection } from "@patternfly/react-core/dist/esm/components/Page/index.js";
13 : import { Button } from "@patternfly/react-core/dist/esm/components/Button/index.js";
14 : import { show_modal_dialog } from "cockpit-components-dialog.jsx";
15 :
16 : import { PatternDialogBody } from "./react-demo-dialog";
17 : import { FileAcDemo, FileAcDemoPreselected } from "./react-demo-file-autocomplete";
18 : import { TypeaheadDemo } from "./react-demo-typeahead";
19 : import { MultiTypeaheadDemo } from "./react-demo-multi-typeahead";
20 : import { CardsDemo } from "./react-demo-cards";
21 : import { UploadDemo } from "./react-demo-file-upload";
22 :
23 : import 'cockpit-dark-theme'; // once per page
24 : import 'page.scss';
25 :
26 : /* -----------------------------------------------------------------------------
27 : Modal Dialog
28 : -----------------------------------------------------------------------------
29 : */
30 :
31 2 : let lastAction: string = "";
32 :
33 0 : const onDialogStandardClicked = function(mode: string, progress_cb: (text: string, cancel?: () => void) => void) {
34 0 : lastAction = mode;
35 :
36 0 : return new Promise<void>((resolve, reject) => {
37 0 : progress_cb("Starting something long");
38 0 : if (mode == 'steps') {
39 0 : let interval: number = 0;
40 0 : const cancel = function() {
41 0 : window.clearTimeout(interval);
42 0 : progress_cb("Canceling");
43 0 : window.setTimeout(function() {
44 0 : reject(new Error("Action canceled"));
45 0 : }, 1000);
46 0 : };
47 0 : let count = 0;
48 0 : interval = window.setInterval(function() {
49 0 : count += 1;
50 0 : progress_cb("Step " + count, cancel);
51 0 : }, 500);
52 0 : window.setTimeout(function() {
53 0 : window.clearTimeout(interval);
54 0 : resolve();
55 0 : }, 5000);
56 0 : } else if (mode == 'reject') {
57 0 : reject(new Error("Some error occurred"));
58 0 : } else {
59 0 : resolve();
60 0 : }
61 0 : });
62 0 : };
63 :
64 0 : const onDialogDone = function(success: boolean) {
65 0 : const result = success ? "successful" : "Canceled";
66 0 : const action = success ? lastAction : "no action";
67 0 : document.getElementById("demo-dialog-result")!.textContent = "Dialog closed: " + result + "(" + action + ")";
68 0 : };
69 :
70 0 : const onStandardDemoClicked = (staticError: React.ReactNode) => {
71 0 : const dialogProps = {
72 0 : title: "This shouldn't be seen",
73 0 : body: React.createElement(PatternDialogBody, { clickNested: onStandardDemoClicked }),
74 0 : static_error: staticError,
75 0 : };
76 : // also test modifying properties in subsequent render calls
77 0 : const footerProps = {
78 0 : actions: [
79 0 : {
80 0 : clicked: onDialogStandardClicked.bind(null, 'standard action'),
81 0 : caption: "OK",
82 0 : style: 'primary',
83 0 : },
84 0 : {
85 0 : clicked: onDialogStandardClicked.bind(null, 'dangerous action'),
86 0 : caption: "Danger",
87 0 : style: 'danger',
88 0 : },
89 0 : {
90 0 : clicked: onDialogStandardClicked.bind(null, 'steps'),
91 0 : caption: "Wait",
92 0 : },
93 0 : ],
94 0 : dialog_done: onDialogDone,
95 0 : };
96 0 : const dialogObj = show_modal_dialog(dialogProps, footerProps);
97 : // if this failed, exit (trying to create a nested dialog)
98 0 : if (!dialogObj)
99 0 : return;
100 0 : footerProps.actions.push(
101 0 : {
102 0 : clicked: onDialogStandardClicked.bind(null, 'reject'),
103 0 : caption: "Error",
104 0 : style: 'primary',
105 0 : });
106 0 : dialogObj.setFooterProps(footerProps);
107 0 : dialogProps.title = "Example React Dialog";
108 0 : dialogObj.setProps(dialogProps);
109 0 : };
110 :
111 2 : const ReactPatterns = () => {
112 2 : const narrow = { maxWidth: 600 };
113 :
114 2 : return (
115 2 : <Page isContentFilled className="no-masthead-sidebar">
116 2 : <PageSection>
117 2 : <h3>Select file</h3>
118 2 : <div id="demo-file-ac" style={narrow}>
119 2 : <FileAcDemo />
120 2 : </div>
121 2 : <div id="demo-file-ac-preselected" style={narrow}>
122 2 : <FileAcDemoPreselected />
123 2 : </div>
124 2 : </PageSection>
125 :
126 2 : <PageSection id="demo-typeahead">
127 2 : <h3>Typeahead</h3>
128 2 : <div style={narrow}>
129 2 : <TypeaheadDemo />
130 2 : </div>
131 2 : </PageSection>
132 :
133 2 : <PageSection id="demo-multi-typeahead">
134 2 : <h3>Multi Typeahead</h3>
135 2 : <div style={narrow}>
136 2 : <MultiTypeaheadDemo />
137 2 : </div>
138 2 : </PageSection>
139 :
140 2 : <PageSection>
141 2 : <h3>Dialogs</h3>
142 2 : <Button
143 2 : id="demo-show-dialog"
144 2 : onClick={onStandardDemoClicked.bind(null, null)}
145 2 : >
146 : Show Dialog
147 2 : </Button>
148 2 : <Button
149 2 : id="demo-show-error-dialog"
150 2 : onClick={onStandardDemoClicked.bind(null, 'Some static error')}
151 2 : >
152 : Show Error-Dialog
153 2 : </Button>
154 2 : <div id="demo-dialog-result" />
155 2 : </PageSection>
156 :
157 2 : <PageSection>
158 2 : <h3>Cards</h3>
159 2 : <CardsDemo />
160 2 : </PageSection>
161 :
162 2 : <PageSection id="demo-upload">
163 2 : <h3>Upload</h3>
164 2 : <div style={narrow}>
165 2 : <UploadDemo />
166 2 : </div>
167 2 : </PageSection>
168 2 : </Page>
169 : );
170 2 : };
171 :
172 2 : function init_app(rootElement: Container) {
173 2 : const root = createRoot(rootElement);
174 2 : root.render(<ReactPatterns />);
175 2 : }
176 :
177 2 : document.addEventListener("DOMContentLoaded", function() {
178 2 : cockpit.transport.wait(function() {
179 2 : init_app(document.getElementById('app')!);
180 2 : });
181 2 : });
|