Line data Source code
1 : /*
2 : * Copyright (C) 2025 Red Hat, Inc.
3 : * SPDX-License-Identifier: LGPL-2.1-or-later
4 : */
5 :
6 5 : import cockpit from "cockpit";
7 5 : import React from "react";
8 5 : 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 5 : 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 5 : const ReactPatterns = () => {
112 5 : const narrow = { maxWidth: 600 };
113 :
114 5 : return (
115 5 : <Page isContentFilled className="no-masthead-sidebar">
116 5 : <PageSection>
117 5 : <h3>Select file</h3>
118 5 : <div id="demo-file-ac" style={narrow}>
119 5 : <FileAcDemo />
120 5 : </div>
121 5 : <div id="demo-file-ac-preselected" style={narrow}>
122 5 : <FileAcDemoPreselected />
123 5 : </div>
124 5 : </PageSection>
125 :
126 5 : <PageSection id="demo-typeahead">
127 5 : <h3>Typeahead</h3>
128 5 : <div style={narrow}>
129 5 : <TypeaheadDemo />
130 5 : </div>
131 5 : </PageSection>
132 :
133 5 : <PageSection id="demo-multi-typeahead">
134 5 : <h3>Multi Typeahead</h3>
135 5 : <div style={narrow}>
136 5 : <MultiTypeaheadDemo />
137 5 : </div>
138 5 : </PageSection>
139 :
140 5 : <PageSection>
141 5 : <h3>Dialogs</h3>
142 5 : <Button
143 5 : id="demo-show-dialog"
144 5 : onClick={onStandardDemoClicked.bind(null, null)}
145 5 : >
146 : Show Dialog
147 5 : </Button>
148 5 : <Button
149 5 : id="demo-show-error-dialog"
150 5 : onClick={onStandardDemoClicked.bind(null, 'Some static error')}
151 5 : >
152 : Show Error-Dialog
153 5 : </Button>
154 5 : <div id="demo-dialog-result" />
155 5 : </PageSection>
156 :
157 5 : <PageSection>
158 5 : <h3>Cards</h3>
159 5 : <CardsDemo />
160 5 : </PageSection>
161 :
162 5 : <PageSection id="demo-upload">
163 5 : <h3>Upload</h3>
164 5 : <div style={narrow}>
165 5 : <UploadDemo />
166 5 : </div>
167 5 : </PageSection>
168 5 : </Page>
169 : );
170 5 : };
171 :
172 5 : function init_app(rootElement: Container) {
173 5 : const root = createRoot(rootElement);
174 5 : root.render(<ReactPatterns />);
175 5 : }
176 :
177 5 : document.addEventListener("DOMContentLoaded", function() {
178 5 : cockpit.transport.wait(function() {
179 5 : init_app(document.getElementById('app')!);
180 5 : });
181 5 : });
|