|
1 | | -import {useState} from "react"; |
| 1 | +import {useCallback, useMemo, useState} from "react"; |
| 2 | +import copy from "copy-to-clipboard"; |
2 | 3 | import Form from "antd/es/form"; |
| 4 | +import Alert from "antd/es/alert"; |
3 | 5 | import Button from "antd/es/button"; |
4 | | -import FormItem from "antd/es/form/FormItem"; |
| 6 | +import Switch from "antd/es/switch"; |
| 7 | +import Card from "antd/es/card/Card"; |
| 8 | +import Divider from "antd/es/divider"; |
| 9 | +import {useForm} from "antd/es/form/Form"; |
5 | 10 | import PhoneInput from "antd-phone-input"; |
| 11 | +import FormItem from "antd/es/form/FormItem"; |
| 12 | +import Title from "antd/es/typography/Title"; |
| 13 | +import Paragraph from "antd/es/typography/Paragraph"; |
| 14 | +import SunOutlined from "@ant-design/icons/SunOutlined"; |
| 15 | +import MoonOutlined from "@ant-design/icons/MoonOutlined"; |
| 16 | +import CopyOutlined from "@ant-design/icons/CopyOutlined"; |
| 17 | +import CheckOutlined from "@ant-design/icons/CheckOutlined"; |
| 18 | + |
| 19 | +import "antd/dist/reset.css"; |
6 | 20 |
|
7 | 21 | const Demo = () => { |
| 22 | + const [form] = useForm(); |
8 | 23 | const [value, setValue] = useState(null); |
| 24 | + const [strict, setStrict] = useState(false); |
| 25 | + const [search, setSearch] = useState(false); |
| 26 | + const [copied, setCopied] = useState(false); |
| 27 | + const [dropdown, setDropdown] = useState(true); |
| 28 | + const [disabled, setDisabled] = useState(false); |
9 | 29 |
|
10 | | - const validator = (_: any, {valid}: any) => { |
11 | | - if (valid()) { |
12 | | - return Promise.resolve(); |
13 | | - } |
| 30 | + const validator = useCallback((_: any, {valid}: any) => { |
| 31 | + if (valid(strict)) return Promise.resolve(); |
14 | 32 | return Promise.reject("Invalid phone number"); |
15 | | - } |
| 33 | + }, [strict]) |
| 34 | + |
| 35 | + const code = useMemo(() => { |
| 36 | + let code = "<PhoneInput\n"; |
| 37 | + if (disabled) code += " disabled\n"; |
| 38 | + if (search && dropdown) code += " enableSearch\n"; |
| 39 | + if (!dropdown) code += " disableDropdown\n"; |
| 40 | + if (code === "<PhoneInput\n") code = "<PhoneInput />"; |
| 41 | + else code += "/>"; |
| 42 | + return code; |
| 43 | + }, [disabled, search, dropdown]) |
16 | 44 |
|
17 | 45 | const changeTheme = () => { |
18 | | - if (window.location.pathname === "/dark") { |
19 | | - window.location.replace("/"); |
| 46 | + const pathname = window.location.pathname.replace(/\/$/, ''); |
| 47 | + if (pathname.endsWith("/dark")) { |
| 48 | + window.location.replace(pathname.replace('/dark', '')); |
20 | 49 | } else { |
21 | | - window.location.replace("/dark"); |
| 50 | + window.location.replace(pathname + "/dark"); |
22 | 51 | } |
23 | 52 | } |
24 | 53 |
|
25 | 54 | const handleFinish = ({phone}: any) => setValue(phone); |
26 | 55 |
|
27 | 56 | return ( |
28 | | - <div style={{margin: 20, maxWidth: 400}}> |
29 | | - {value && ( |
30 | | - <pre style={{ |
31 | | - background: window.location.pathname === "/dark" ? "#1f1f1f" : "#efefef", |
32 | | - color: window.location.pathname === "/dark" ? "#efefef" : "#1f1f1f", |
33 | | - padding: 10, marginBottom: 24, |
34 | | - }}> |
35 | | - {JSON.stringify(value, null, 2)} |
36 | | - </pre> |
37 | | - )} |
38 | | - <Form onFinish={handleFinish}> |
39 | | - <FormItem name="phone" rules={[{validator}]}> |
40 | | - <PhoneInput enableSearch/> |
41 | | - </FormItem> |
42 | | - <div style={{display: "flex", gap: 24}}> |
43 | | - <Button htmlType="submit">Preview Value</Button> |
44 | | - <Button htmlType="reset">Reset Value</Button> |
45 | | - <Button onClick={changeTheme}>Change Theme</Button> |
| 57 | + <Card style={{height: "100%", borderRadius: 0, border: "none"}} |
| 58 | + bodyStyle={{ |
| 59 | + padding: 0, |
| 60 | + height: "100%", |
| 61 | + display: "flex", |
| 62 | + justifyContent: "center", |
| 63 | + }}> |
| 64 | + <div style={{ |
| 65 | + minWidth: 415, |
| 66 | + maxWidth: 415, |
| 67 | + display: "flex", |
| 68 | + margin: "10px 20px", |
| 69 | + flexDirection: "column", |
| 70 | + }}> |
| 71 | + <Title level={2}> |
| 72 | + Ant Phone Input Playground |
| 73 | + </Title> |
| 74 | + <Paragraph> |
| 75 | + This is a playground for the Ant Phone Input component. You can change the settings and see how |
| 76 | + the component behaves. Also, see the code for the component and the value it returns. |
| 77 | + </Paragraph> |
| 78 | + <Divider orientation="left" plain>Settings</Divider> |
| 79 | + <div style={{gap: 24, display: "flex", alignItems: "center"}}> |
| 80 | + <Form.Item label="Theme"> |
| 81 | + <Switch |
| 82 | + onChange={changeTheme} |
| 83 | + checkedChildren={<MoonOutlined/>} |
| 84 | + unCheckedChildren={<SunOutlined/>} |
| 85 | + defaultChecked={window.location.pathname.endsWith("/dark")} |
| 86 | + /> |
| 87 | + </Form.Item> |
| 88 | + <Form.Item label="Validation"> |
| 89 | + <Switch |
| 90 | + checkedChildren="strict" |
| 91 | + unCheckedChildren="default" |
| 92 | + onChange={() => setStrict(!strict)} |
| 93 | + /> |
| 94 | + </Form.Item> |
| 95 | + <Form.Item label="Disabled"> |
| 96 | + <Switch onChange={() => setDisabled(!disabled)}/> |
| 97 | + </Form.Item> |
| 98 | + </div> |
| 99 | + <div style={{gap: 24, display: "flex", alignItems: "center"}}> |
| 100 | + <Form.Item label="Search" style={{margin: 0}}> |
| 101 | + <Switch |
| 102 | + disabled={!dropdown} |
| 103 | + checkedChildren="enabled" |
| 104 | + unCheckedChildren="disabled" |
| 105 | + onChange={() => setSearch(!search)} |
| 106 | + /> |
| 107 | + </Form.Item> |
| 108 | + <Form.Item label="Dropdown" style={{margin: 0}}> |
| 109 | + <Switch |
| 110 | + defaultChecked |
| 111 | + checkedChildren="enabled" |
| 112 | + unCheckedChildren="disabled" |
| 113 | + onChange={() => setDropdown(!dropdown)} |
| 114 | + /> |
| 115 | + </Form.Item> |
| 116 | + </div> |
| 117 | + <Divider orientation="left" plain>Code</Divider> |
| 118 | + <div style={{position: "relative"}}> |
| 119 | + <Button |
| 120 | + type="text" |
| 121 | + size="small" |
| 122 | + onClick={() => { |
| 123 | + copy(code); |
| 124 | + setCopied(true); |
| 125 | + setTimeout(() => setCopied(false), 2000); |
| 126 | + }} |
| 127 | + style={{position: "absolute", top: 10, right: 10}} |
| 128 | + icon={copied ? <CheckOutlined style={{color: "green"}}/> : <CopyOutlined/>} |
| 129 | + /> |
| 130 | + <pre style={{ |
| 131 | + background: window.location.pathname.endsWith("/dark") ? "#1f1f1f" : "#efefef", |
| 132 | + color: window.location.pathname.endsWith("/dark") ? "#efefef" : "#1f1f1f", |
| 133 | + padding: 10, |
| 134 | + }}> |
| 135 | + {code} |
| 136 | + </pre> |
| 137 | + </div> |
| 138 | + <Divider orientation="left" plain>Component</Divider> |
| 139 | + <Form form={form} onFinish={handleFinish}> |
| 140 | + <FormItem name="phone" rules={[{validator}]}> |
| 141 | + <PhoneInput |
| 142 | + disabled={disabled} |
| 143 | + enableSearch={search} |
| 144 | + disableDropdown={!dropdown} |
| 145 | + /> |
| 146 | + </FormItem> |
| 147 | + {value && ( |
| 148 | + <pre style={{ |
| 149 | + background: window.location.pathname.endsWith("/dark") ? "#1f1f1f" : "#efefef", |
| 150 | + color: window.location.pathname.endsWith("/dark") ? "#efefef" : "#1f1f1f", |
| 151 | + padding: 10, marginBottom: 24, |
| 152 | + }}> |
| 153 | + {JSON.stringify(value, null, 2)} |
| 154 | + </pre> |
| 155 | + )} |
| 156 | + <div style={{display: "flex", gap: 24, justifyContent: "flex-start"}}> |
| 157 | + <Button htmlType="submit" type="primary">Preview Value</Button> |
| 158 | + <Button htmlType="reset">Reset Value</Button> |
| 159 | + </div> |
| 160 | + </Form> |
| 161 | + <Alert |
| 162 | + type="info" |
| 163 | + style={{marginTop: 24}} |
| 164 | + message={<> |
| 165 | + If your application uses <b>5.x</b> version of <b>Ant Design</b>, you should use this |
| 166 | + <a target="_blank" rel="noreferrer" |
| 167 | + href="//playground.typesnippet.org/antd-phone-input-5.x">playground</a> |
| 168 | + server to test the component. |
| 169 | + </>} |
| 170 | + /> |
| 171 | + <div style={{marginTop: "auto"}}> |
| 172 | + <Divider style={{margin: "10px 0"}}/> |
| 173 | + <div style={{ |
| 174 | + display: "flex", |
| 175 | + alignItems: "center", |
| 176 | + justifyContent: "space-between", |
| 177 | + }}> |
| 178 | + <div> |
| 179 | + © Made with ❤️ by |
| 180 | + <a href="//github.com/typesnippet" target="_blank" rel="noreferrer author"> |
| 181 | + TypeSnippet |
| 182 | + </a> |
| 183 | + </div> |
| 184 | + <div style={{display: "flex", gap: 10}}> |
| 185 | + <a target="_blank" rel="noreferrer" |
| 186 | + href="//github.com/typesnippet/antd-phone-input/blob/master/LICENSE"> |
| 187 | + <img src="//img.shields.io/npm/l/antd-phone-input" alt="MIT License"/> |
| 188 | + </a> |
| 189 | + <a href="//www.npmjs.com/package/antd-phone-input" target="_blank" rel="noreferrer"> |
| 190 | + <img src="//img.shields.io/npm/v/antd-phone-input" alt="NPM Package"/> |
| 191 | + </a> |
| 192 | + </div> |
| 193 | + </div> |
| 194 | + <Paragraph style={{margin: "5px 0 0 0"}}> |
| 195 | + Find the |
| 196 | + <a href="//github.com/typesnippet/antd-phone-input/tree/master/examples/antd4.x" |
| 197 | + target="_blank" rel="noreferrer"> |
| 198 | + source code |
| 199 | + </a> |
| 200 | + of this playground server on our GitHub repo. |
| 201 | + </Paragraph> |
46 | 202 | </div> |
47 | | - </Form> |
48 | | - </div> |
| 203 | + </div> |
| 204 | + </Card> |
49 | 205 | ) |
50 | 206 | } |
51 | 207 |
|
|
0 commit comments