fix(ui): 按原版 DOM 重构滑杆与开关结构
This commit is contained in:
1 parent
8ab4e62838
commit
dd233e6fb9
3 files changed
+206
-98
No files matched your search
@@ -91,4 +91,38 @@
|
||||
| 滑杆进度渐变 | `hUi.tap` | `module_82347.js:200507` |
|
||||
| `SettingsComponentStyle-slider` | `kNi.z1jp_1` | `module_82347.js:89620` |
|
||||
|
||||
`ksc-130` 是 `SettingsComponentStyle-slider` 的 Emotion 生成类名;当前实现用 `data-ui="SettingsComponentStyle-slider"` 标记,等价于给滑杆挂上该样式。
|
||||
`ksc-130` 是 `SettingsComponentStyle-slider` 的 Emotion 生成类名;当前实现直接使用 `InputRangeComponentStyle-*` / `SettingsComponentStyle-slider` 语义类名,并用 `ksc-56` / `ksc-57` 标记标签与滑杆。
|
||||
|
||||
## 7. 真实 DOM 结构
|
||||
|
||||
滑杆:
|
||||
|
||||
```html
|
||||
<div class="InputRangeComponentStyle-blockSoundVolume SettingsComponentStyle-slider">
|
||||
<p>主音量<span class="ksc-56">(80%)</span></p>
|
||||
<div class="InputRangeComponentStyle-soundVolume">
|
||||
<input type="range" min="0" max="100" step="1"
|
||||
class="ksc-57 InputRangeComponentStyle-range" value="80">
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
开关:
|
||||
|
||||
```html
|
||||
<div class="ksc-46">
|
||||
<div class="CheckBoxComponentStyle-oneSetting">
|
||||
<div class="CheckBoxStyle-checkbox">
|
||||
<label>
|
||||
<input type="checkbox" class="ksc-47" checked>
|
||||
<span class="ksc-62 -displayFlex"></span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="ksc-63 -flexStartAlignCenter">
|
||||
<label>显示帧数和延迟</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
当前实现已按这个结构输出;带描述的开关行增加 `has-description` 修饰类,让描述与标签对齐。
|
||||
@@ -24,6 +24,7 @@ import react.dom.html.ReactHTML.h3
|
||||
import react.dom.html.ReactHTML.i
|
||||
import react.dom.html.ReactHTML.img
|
||||
import react.dom.html.ReactHTML.input
|
||||
import react.dom.html.ReactHTML.label
|
||||
import react.dom.html.ReactHTML.option
|
||||
import react.dom.html.ReactHTML.p
|
||||
import react.dom.html.ReactHTML.select
|
||||
@@ -2356,37 +2357,6 @@ private val SettingsChevron: csstype.PropertiesBuilder.() -> Unit = {
|
||||
Style("flex", "0 0 0.813em")
|
||||
}
|
||||
|
||||
private val SettingsSliderWrap: csstype.PropertiesBuilder.() -> Unit = {
|
||||
Style("display", "flex")
|
||||
Style("flex-direction", "column")
|
||||
Style("justify-content", "space-between")
|
||||
Style("align-items", "flex-start")
|
||||
Style("width", "31.25em")
|
||||
Style("height", "3.188em")
|
||||
Style("min-height", "3.188em")
|
||||
Style("margin-bottom", "1.75em")
|
||||
}
|
||||
|
||||
private val SettingsSliderLabel: csstype.PropertiesBuilder.() -> Unit = {
|
||||
Style("display", "flex")
|
||||
Style("justify-content", "space-between")
|
||||
Style("align-items", "center")
|
||||
Style("width", "100%")
|
||||
Style("color", "white")
|
||||
Style("font-size", "0.9375em")
|
||||
Style("margin-top", "0")
|
||||
}
|
||||
|
||||
private val SettingsRange: csstype.PropertiesBuilder.() -> Unit = {
|
||||
Style("cursor", "pointer")
|
||||
Style("-webkit-appearance", "none")
|
||||
Style("appearance", "none")
|
||||
Style("width", "100%")
|
||||
Style("height", "0.563em")
|
||||
Style("outline", "none")
|
||||
Style("border-radius", "0")
|
||||
}
|
||||
|
||||
private fun SettingsSliderGradient(Value: Int): String {
|
||||
val Percent = Value.coerceIn(0, 100)
|
||||
return "linear-gradient(to right, #ffbc09 0%, #ffbc09 $Percent%, rgba(255, 255, 255, 0.1) $Percent%, rgba(255, 255, 255, 0.1) 100%)"
|
||||
@@ -2430,46 +2400,6 @@ private val SettingsActionButton: csstype.PropertiesBuilder.() -> Unit = {
|
||||
Style("padding", "0 1em")
|
||||
}
|
||||
|
||||
private val SettingsCheckboxRow: csstype.PropertiesBuilder.() -> Unit = {
|
||||
Style("display", "flex")
|
||||
Style("align-items", "center")
|
||||
Style("justify-content", "flex-start")
|
||||
Style("width", "100%")
|
||||
Style("height", "2.25em")
|
||||
Style("margin-bottom", "1.25em")
|
||||
Style("gap", "0.75em")
|
||||
}
|
||||
|
||||
private fun SettingsCheckbox(On: Boolean): csstype.PropertiesBuilder.() -> Unit = {
|
||||
Style("position", "relative")
|
||||
Style("display", "flex")
|
||||
Style("align-items", "center")
|
||||
Style("justify-content", "center")
|
||||
Style("width", "2.75em")
|
||||
Style("height", "1.5em")
|
||||
Style("flex", "0 0 2.75em")
|
||||
Style("box-sizing", "border-box")
|
||||
Style("background-color", if (On) "rgba(118, 255, 51, 0.25)" else "rgba(191, 213, 255, 0.25)")
|
||||
Style("border", "0.063em solid rgba(255, 255, 255, 0.2)")
|
||||
Style("border-radius", "6.25rem")
|
||||
Style("cursor", "pointer")
|
||||
Style("padding", "0")
|
||||
Style("transition", "background-color 0.2s")
|
||||
}
|
||||
|
||||
private fun SettingsToggleKnob(On: Boolean): csstype.PropertiesBuilder.() -> Unit = {
|
||||
Style("position", "absolute")
|
||||
Style("top", "50%")
|
||||
Style("left", if (On) "calc(100% - 1.3em)" else "0.2em")
|
||||
Style("width", "1.1em")
|
||||
Style("height", "1.1em")
|
||||
Style("border-radius", "50%")
|
||||
Style("background-color", "white")
|
||||
Style("transform", "translateY(-50%)")
|
||||
Style("transition", "left 0.2s")
|
||||
Style("pointer-events", "none")
|
||||
}
|
||||
|
||||
private val SettingsKeysGrid: csstype.PropertiesBuilder.() -> Unit = {
|
||||
Style("display", "grid")
|
||||
Style("grid-template-columns", "1fr 1fr")
|
||||
@@ -2570,41 +2500,57 @@ private val SettingsScreen = FC<Props> {
|
||||
is SettingEntry.Toggle -> {
|
||||
val On = Toggles[Entry.Id] ?: Entry.Default
|
||||
div {
|
||||
css(SettingsCheckboxRow)
|
||||
Ui("CheckBoxComponentStyle-oneSetting")
|
||||
button {
|
||||
css(SettingsCheckbox(On))
|
||||
onClick = { Toggles = Toggles + (Entry.Id to !On) }
|
||||
div { css(SettingsToggleKnob(On)) }
|
||||
}
|
||||
asDynamic()["className"] = "ksc-46"
|
||||
div {
|
||||
css { Style("flex", "1"); Style("min-width", "0") }
|
||||
div { css(SettingsLabel); +Entry.Label }
|
||||
if (Entry.Desc.isNotEmpty()) div { css(SettingsDesc); +Entry.Desc }
|
||||
asDynamic()["className"] = "CheckBoxComponentStyle-oneSetting" + if (Entry.Desc.isNotEmpty()) " has-description" else ""
|
||||
div {
|
||||
asDynamic()["className"] = "CheckBoxStyle-checkbox"
|
||||
label {
|
||||
input {
|
||||
asDynamic()["className"] = "ksc-47"
|
||||
asDynamic()["type"] = "checkbox"
|
||||
asDynamic()["checked"] = On
|
||||
onChange = { Toggles = Toggles + (Entry.Id to !On) }
|
||||
}
|
||||
span {
|
||||
asDynamic()["className"] = "ksc-62 -displayFlex"
|
||||
asDynamic()["data-on"] = On.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
div {
|
||||
asDynamic()["className"] = "ksc-63 -flexStartAlignCenter" + if (Entry.Desc.isNotEmpty()) " has-description" else ""
|
||||
label { +Entry.Label }
|
||||
if (Entry.Desc.isNotEmpty()) div { css(SettingsDesc); +Entry.Desc }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
is SettingEntry.Slider -> {
|
||||
val Value = Sliders[Entry.Id] ?: Entry.Default
|
||||
div {
|
||||
css(SettingsSliderWrap)
|
||||
Ui("InputRangeComponentStyle-blockSoundVolume")
|
||||
asDynamic()["className"] = "InputRangeComponentStyle-blockSoundVolume SettingsComponentStyle-slider"
|
||||
p {
|
||||
+Entry.Label
|
||||
span {
|
||||
asDynamic()["className"] = "ksc-56"
|
||||
+"($Value%)"
|
||||
}
|
||||
}
|
||||
div {
|
||||
css(SettingsSliderLabel)
|
||||
span { +Entry.Label }
|
||||
span { +"$Value%" }
|
||||
}
|
||||
input {
|
||||
css {
|
||||
Apply(SettingsRange)
|
||||
Style("background", SettingsSliderGradient(Value))
|
||||
asDynamic()["className"] = "InputRangeComponentStyle-soundVolume"
|
||||
input {
|
||||
asDynamic()["className"] = "ksc-57 InputRangeComponentStyle-range"
|
||||
asDynamic()["type"] = "range"
|
||||
asDynamic()["min"] = "0"
|
||||
asDynamic()["max"] = "100"
|
||||
asDynamic()["step"] = "1"
|
||||
asDynamic()["value"] = "$Value"
|
||||
val StyleObj = js("({})")
|
||||
StyleObj["background"] = SettingsSliderGradient(Value)
|
||||
asDynamic()["style"] = StyleObj
|
||||
onChange = { E -> Sliders = Sliders + (Entry.Id to (E.target.value.toString().toIntOrNull() ?: Value)) }
|
||||
}
|
||||
Ui("SettingsComponentStyle-slider")
|
||||
asDynamic()["type"] = "range"
|
||||
asDynamic()["min"] = "0"
|
||||
asDynamic()["max"] = "100"
|
||||
asDynamic()["value"] = "$Value"
|
||||
onChange = { E -> Sliders = Sliders + (Entry.Id to (E.target.value.toString().toIntOrNull() ?: Value)) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -984,4 +984,132 @@ body { margin: 0; overflow: hidden; background: #001926; color: #fff; }
|
||||
border: 0;
|
||||
border-radius: 0.25em;
|
||||
background: #ffbc09;
|
||||
}
|
||||
|
||||
/* 原版设置页语义类 */
|
||||
.-displayFlex { display: flex; }
|
||||
.-flexStartAlignCenter { display: flex; align-items: center; justify-content: flex-start; }
|
||||
|
||||
.InputRangeComponentStyle-blockSoundVolume {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
width: 31.25em;
|
||||
height: 3.188em;
|
||||
min-height: 3.188em;
|
||||
margin-bottom: 1.75em;
|
||||
}
|
||||
.SettingsComponentStyle-slider { width: 31.25em; }
|
||||
.InputRangeComponentStyle-blockSoundVolume p {
|
||||
margin: 0;
|
||||
color: #fff;
|
||||
font-family: BaseFontRegular, "Segoe UI", sans-serif;
|
||||
font-size: 0.9375em;
|
||||
line-height: normal;
|
||||
}
|
||||
.InputRangeComponentStyle-blockSoundVolume p span { margin-left: 0.2em; }
|
||||
.InputRangeComponentStyle-soundVolume {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
height: 1.25em;
|
||||
}
|
||||
.InputRangeComponentStyle-range {
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
width: 100%;
|
||||
height: 0.563em;
|
||||
outline: none;
|
||||
cursor: pointer;
|
||||
border-radius: 0;
|
||||
}
|
||||
.InputRangeComponentStyle-range::-webkit-slider-runnable-track {
|
||||
width: 100%;
|
||||
height: 0.375em;
|
||||
background: transparent;
|
||||
}
|
||||
.InputRangeComponentStyle-range::-webkit-slider-thumb {
|
||||
-webkit-appearance: none;
|
||||
width: 1.875em;
|
||||
height: 1em;
|
||||
border-radius: 0.25em;
|
||||
background: #ffbc09;
|
||||
margin-top: -0.3em;
|
||||
}
|
||||
.InputRangeComponentStyle-range::-moz-range-track { background: transparent; }
|
||||
.InputRangeComponentStyle-range::-moz-range-progress { background: #ffbc09; }
|
||||
.InputRangeComponentStyle-range::-moz-range-thumb {
|
||||
width: 1.875em;
|
||||
height: 1em;
|
||||
border: 0;
|
||||
border-radius: 0.25em;
|
||||
background: #ffbc09;
|
||||
}
|
||||
|
||||
.CheckBoxComponentStyle-oneSetting {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
width: 100%;
|
||||
height: 2.25em;
|
||||
margin-bottom: 1.25em;
|
||||
}
|
||||
.CheckBoxComponentStyle-oneSetting.has-description {
|
||||
height: auto;
|
||||
min-height: 2.25em;
|
||||
align-items: flex-start;
|
||||
}
|
||||
.CheckBoxStyle-checkbox { display: flex; z-index: 1; }
|
||||
.CheckBoxStyle-checkbox label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
.CheckBoxStyle-checkbox input {
|
||||
position: absolute;
|
||||
width: 0;
|
||||
height: 0;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
.ksc-62 {
|
||||
position: relative;
|
||||
display: flex;
|
||||
width: 2.75em;
|
||||
height: 1.5em;
|
||||
border: 0.063em solid rgba(255, 255, 255, 0.2);
|
||||
border-radius: 6.25rem;
|
||||
background: rgba(191, 213, 255, 0.25);
|
||||
transition: background-color 0.2s;
|
||||
}
|
||||
.ksc-62[data-on="true"] { background: rgba(118, 255, 51, 0.25); }
|
||||
.ksc-62::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 0.2em;
|
||||
width: 1.1em;
|
||||
height: 1.1em;
|
||||
border-radius: 50%;
|
||||
background: #fff;
|
||||
transform: translateY(-50%);
|
||||
transition: left 0.2s;
|
||||
}
|
||||
.ksc-62[data-on="true"]::after { left: calc(100% - 1.3em); }
|
||||
.ksc-63 { display: flex; align-items: center; justify-content: flex-start; }
|
||||
.ksc-63.has-description {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
justify-content: flex-start;
|
||||
gap: 0.15em;
|
||||
padding-top: 0.2em;
|
||||
}
|
||||
.ksc-63 label {
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
font-family: BaseFontRegular, "Segoe UI", sans-serif;
|
||||
font-size: 1em;
|
||||
line-height: normal;
|
||||
}
|
||||
Reference in new issue
Block a user