-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
2d0e958
commit ecded6f
Showing
8 changed files
with
325 additions
and
37 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,33 +1,51 @@ | ||
<!DOCTYPE html> | ||
<html lang="en"> | ||
<head> | ||
<meta charset="UTF-8" /> | ||
<meta http-equiv="X-UA-Compatible" content="IE=edge" /> | ||
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> | ||
<title>Document</title> | ||
<script src="../bundle/babel.min.js"></script> | ||
<!-- <script src="../bundle/react.development.js"></script> --> | ||
<!-- <script src="../bundle/react-dom.development.js"></script> --> | ||
<script src="../packages/myreact/dist/umd/index.development.js"></script> | ||
<script src="../packages/myreact-dom/dist/umd/index.development.js"></script> | ||
</head> | ||
<body> | ||
<div id="root"></div> | ||
<script type="text/babel"> | ||
const { useState } = React; | ||
|
||
const Input = () => { | ||
const [str, setStr] = useState(""); | ||
<head> | ||
<meta charset="UTF-8" /> | ||
<meta http-equiv="X-UA-Compatible" content="IE=edge" /> | ||
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> | ||
<title>Document</title> | ||
<script src="../bundle/babel.min.js"></script> | ||
<!-- <script src="../bundle/react.development.js"></script> --> | ||
<!-- <script src="../bundle/react-dom.development.js"></script> --> | ||
<script src="../packages/myreact/dist/umd/index.development.js"></script> | ||
<script src="../packages/myreact-dom/dist/umd/index.development.js"></script> | ||
</head> | ||
|
||
return ( | ||
<> | ||
<input type="text" value={str} onChange={(e) => setStr(e.target.value)} /> | ||
<input value={str} /> | ||
</> | ||
); | ||
}; | ||
<body> | ||
<div id="root"></div> | ||
<script type="text/babel"> | ||
const { useState } = React; | ||
|
||
ReactDOM.render(<Input />, root); | ||
</script> | ||
</body> | ||
</html> | ||
const Input = () => { | ||
const [str, setStr] = useState(""); | ||
|
||
return ( | ||
<> | ||
<input type="text" value={str} onChange={(e) => setStr(e.target.value)} /> | ||
<input value={str} /> | ||
<input type='checkbox' defaultChecked='123' /> | ||
<label> | ||
Pick a fruit: | ||
<select name="selectedFruit" value='banana'> | ||
<option value="apple">Apple</option> | ||
<option value="banana">Banana</option> | ||
<option value="orange">Orange</option> | ||
</select> | ||
</label> | ||
<progress value={0} /> | ||
<progress value={0.5} /> | ||
<progress value={0.7} /> | ||
<progress value={75} max={100} /> | ||
<progress value={1} /> | ||
<progress value={null} /> | ||
</> | ||
); | ||
}; | ||
|
||
ReactDOM.render(<Input />, root); | ||
</script> | ||
</body> | ||
|
||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
168 changes: 168 additions & 0 deletions
168
packages/myreact-dom/src/client/api/helper/control/select.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,168 @@ | ||
import { type MyReactFiberNode } from "@my-react/react-reconciler"; | ||
|
||
import { log } from "@my-react-dom-shared"; | ||
|
||
type ControlledElement = HTMLSelectElement; | ||
|
||
/** | ||
* @internal | ||
*/ | ||
export const isReadonlySelectElement = (fiber: MyReactFiberNode) => hasControlledSelectProps(fiber) && !fiber.pendingProps.onChange; | ||
|
||
/** | ||
* @internal | ||
*/ | ||
export const isControlledSelectElement = (fiber: MyReactFiberNode) => hasControlledSelectProps(fiber) && typeof fiber.pendingProps.onChange === "function"; | ||
|
||
const generateEmptyChangeFun = (fiber: MyReactFiberNode) => { | ||
return () => { | ||
if (__DEV__) { | ||
log(fiber, "warn", `current controlled element is a readonly element, please provider a 'onChange' props to make the value update`); | ||
} | ||
}; | ||
}; | ||
|
||
/** | ||
* @internal | ||
*/ | ||
export const generateSelectOnChangeFun = (fiber: MyReactFiberNode) => { | ||
const onChange = (...args) => { | ||
const originalOnChange = fiber.pendingProps.onChange; | ||
|
||
const targetOnChange = | ||
typeof originalOnChange !== "function" | ||
? generateEmptyChangeFun(fiber) | ||
: (...args) => { | ||
originalOnChange?.call?.(null, ...args); | ||
}; | ||
|
||
targetOnChange?.call?.(null, ...args); | ||
|
||
requestAnimationFrame(() => { | ||
const dom = fiber.nativeNode; | ||
|
||
const props = fiber.pendingProps; | ||
|
||
const typedDom = dom as ControlledElement; | ||
|
||
const key = "value"; | ||
|
||
if (key in props) { | ||
(typedDom as any)[key] = props[key]; | ||
} | ||
}); | ||
}; | ||
|
||
return onChange; | ||
}; | ||
|
||
/** | ||
* @internal | ||
*/ | ||
export const hasControlledSelectProps = (fiber: MyReactFiberNode) => { | ||
const props = fiber.pendingProps; | ||
|
||
const key = "value"; | ||
|
||
return props[key] !== undefined; | ||
}; | ||
|
||
/** | ||
* @internal | ||
*/ | ||
export const updateControlSelectElement = (fiber: MyReactFiberNode) => { | ||
const pendingProps = fiber.pendingProps; | ||
|
||
const memoizedProps = fiber.memoizedProps; | ||
|
||
const key = "value"; | ||
|
||
if (__DEV__) { | ||
if (pendingProps[key] !== undefined) { | ||
if (!(key in memoizedProps) || memoizedProps[key] === undefined) { | ||
log(fiber, "warn", `current component change from 'unControlled' to 'controlled', this may case some bug`); | ||
} | ||
} else { | ||
if (memoizedProps[key] !== undefined) { | ||
log(fiber, "warn", `current component change from 'controlled' to 'unControlled', this may case some bug`); | ||
} | ||
} | ||
} | ||
}; | ||
|
||
function updateOptions(node: HTMLSelectElement, multiple: boolean, propValue: string | string[], setDefaultSelected: boolean) { | ||
const options: HTMLOptionsCollection = node.options; | ||
|
||
if (multiple) { | ||
const selectedValues = propValue as Array<string>; | ||
const selectedValue: { [key: string]: boolean } = {}; | ||
for (let i = 0; i < selectedValues.length; i++) { | ||
// Prefix to avoid chaos with special keys. | ||
selectedValue["$" + selectedValues[i]] = true; | ||
} | ||
for (let i = 0; i < options.length; i++) { | ||
const selected = Object.prototype.hasOwnProperty.call(selectedValue, "$" + options[i].value); | ||
if (options[i].selected !== selected) { | ||
options[i].selected = selected; | ||
} | ||
if (selected && setDefaultSelected) { | ||
options[i].defaultSelected = true; | ||
} | ||
} | ||
} else { | ||
// Do not set `select.value` as exact behavior isn't consistent across all | ||
// browsers for all cases. | ||
const selectedValue = String(propValue); | ||
let defaultSelected = null; | ||
for (let i = 0; i < options.length; i++) { | ||
if (options[i].value === selectedValue) { | ||
options[i].selected = true; | ||
if (setDefaultSelected) { | ||
options[i].defaultSelected = true; | ||
} | ||
return; | ||
} | ||
if (defaultSelected === null && !options[i].disabled) { | ||
defaultSelected = options[i]; | ||
} | ||
} | ||
if (defaultSelected !== null) { | ||
defaultSelected.selected = true; | ||
} | ||
} | ||
} | ||
|
||
export const initSelect = (fiber: MyReactFiberNode) => { | ||
const element = fiber.nativeNode as HTMLSelectElement; | ||
const multiple = fiber.pendingProps.multiple; | ||
const value = fiber.pendingProps.value; | ||
const defaultValue = fiber.pendingProps.defaultValue; | ||
const node = element; | ||
node.multiple = !!multiple; | ||
if (value != null) { | ||
updateOptions(node, !!multiple, value, false); | ||
} else if (defaultValue != null) { | ||
updateOptions(node, !!multiple, defaultValue, true); | ||
} | ||
}; | ||
|
||
export const updateSelect = (fiber: MyReactFiberNode) => { | ||
const element = fiber.nativeNode as HTMLSelectElement; | ||
const multiple = fiber.pendingProps.multiple; | ||
const value = fiber.pendingProps.value; | ||
const defaultValue = fiber.pendingProps.defaultValue; | ||
const wasMultiple = fiber.memoizedProps.multiple; | ||
const node = element; | ||
|
||
if (value != null) { | ||
updateOptions(node, !!multiple, value, false); | ||
} else if (!!wasMultiple !== !!multiple) { | ||
// For simplicity, reapply `defaultValue` if `multiple` is toggled. | ||
if (defaultValue != null) { | ||
updateOptions(node, !!multiple, defaultValue, true); | ||
} else { | ||
// Revert the select back to its default unselected state. | ||
updateOptions(node, !!multiple, multiple ? [] : "", false); | ||
} | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.