This commit is contained in:
2025-03-25 23:25:59 +08:00
parent ddda5c1c68
commit 9579987fd9
61 changed files with 3902 additions and 1 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 205 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 400 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 215 B

@@ -0,0 +1,304 @@
<script lang="tsx">
import { selectionListEmits, selectionListProps, useSelectionList } from "hoci"
import type { CSSProperties, PropType, RendererElement } from "vue";
import { Teleport, Transition, computed, defineComponent, onDeactivated, ref, renderList, renderSlot, watch } from "vue"
import { onClickOutside, useVModel } from "@vueuse/core"
import { classPropType, labelPropType } from "@/components/hooks/types"
export default defineComponent({
name: "ISelect",
props: {
...selectionListProps,
disabled: {
type: Boolean,
default: false
},
width: {
type: Number,
default: 120
},
emptyLabel: {
type: labelPropType
},
highlight: {
type: String,
// warn remind
default: ""
},
labelClass: {
type: String,
default: ""
},
inputClass: {
type: classPropType,
default: ""
},
appendTo: {
type: [Object, String] as PropType<string | RendererElement | null | undefined>,
default() {
return "body"
}
}
},
emits: [...selectionListEmits],
setup(props, context) {
const modelValue = useVModel(props, "modelValue", context.emit, { passive: true })
const { renderItem } = useSelectionList(() => {
return {
...props,
modelValue: modelValue.value,
itemClass: "i-select-dropdown-item",
activeClass: "active",
unactiveClass: "unactive"
}
}, context)
const isOpen = ref(false)
const triggerRef = ref<HTMLElement>()
const selectRef = ref<HTMLElement>()
const dropdownRef = ref<HTMLElement>()
watch(isOpen, onResize)
function collapse() {
isOpen.value = !isOpen.value && !props.disabled
}
// 下拉框位置
const dropdownPosition = ref({ x: 0, y: 0, w: 0, z: 0 })
// 下拉框位置
const dropdownStyle = computed<CSSProperties>(() => {
if (dropdownPosition.value.z == 0) {
return {
left: `${dropdownPosition.value.x}px`,
top: `${dropdownPosition.value.y}px`,
width: `${dropdownPosition.value.w}px`
}
} else {
return {
left: `${dropdownPosition.value.x}px`,
bottom: `${dropdownPosition.value.z}px`,
width: `${dropdownPosition.value.w}px`
}
}
})
function onResize() {
if (triggerRef.value) {
const { width, height, left, top } = triggerRef.value.getBoundingClientRect()
if (window.innerHeight - top > 160) {
dropdownPosition.value = {
w: width,
x: left,
y: top + height + 2,
z: 0
}
} else {
dropdownPosition.value = {
w: width,
x: left,
z: window.innerHeight - top + 2,
y: 0
}
}
}
}
const appendToRef = computed(() => {
return props.appendTo || "body"
})
onClickOutside(triggerRef, () => (isOpen.value = false))
window.addEventListener("resize", onResize)
window.addEventListener("scroll", onResize)
onDeactivated(() => {
window.removeEventListener("resize", onResize)
window.removeEventListener("scroll", onResize)
})
const { slots } = context
function renderLabel() {
const children = renderItem()
if (children) {
if (!Array.isArray(children)) {
return <span class={["i-select-input"].concat(props.inputClass)}>{children}</span>
} else if (children.length > 0) {
return (
<span class={["i-select-input space-x-1"].concat(props.inputClass)}>
{renderList(children, item => (
<span>{item}</span>
))}
</span>
)
}
}
const emptyLabel = typeof props.emptyLabel == "function" ? props.emptyLabel() : props.emptyLabel
return <span class="w-full pl-2 i-select-input i-select-empty-label">{emptyLabel}</span>
}
return () => {
return (
<div class={["min-w-20 w-40 i-select "].concat(props.highlight)} onClick={collapse} ref={selectRef}>
<div
class={{
"i-select-trigger": true,
"disabled": props.disabled
}}
ref={triggerRef}
>
{renderLabel()}
<calc-button disabled={props.disabled} icon="dropdown" class="cursor-pointer"></calc-button>
</div>
<Teleport to={appendToRef.value}>
<Transition name="dropdown" mode="out-in">
<div class="i-select-dropdown" style={dropdownStyle.value} v-show={isOpen.value} ref={dropdownRef}>
{renderSlot(slots, "default")}
</div>
</Transition>
</Teleport>
</div>
)
}
}
})
</script>
<style lang="scss">
$text-color: #937639;
.i-select {
min-width: 80px;
width: 160px;
user-select: none;
outline: none;
height: 16px;
min-height: 16px;
line-height: 100%;
// font-size: 12px;
color: $text-color;
background-color: rgba(0, 0, 0, 1);
border: 1px solid #5b472a;
border-radius: 2px;
padding: 0;
padding-right: 3px;
margin: 0;
display: block;
.i-select-trigger {
padding: 0;
display: flex;
height: 100%;
justify-content: space-between;
align-items: center;
.i-select-input {
padding-left: 8px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
max-width: calc(100% - 20px);
}
.i-select-empty-label {
color: #5e5e5e;
}
&:hover {
.i-select-empty-label {
color: #757575;
}
}
&.disabled {
color: gray;
}
}
.i-select-trigger:hover {
opacity: 1;
}
}
.warn {
color: red !important;
border: 1px solid #aa8651;
}
.remind {
color: #32e432 !important;
border: 1px solid #aa8651;
}
.i-select-dropdown {
position: fixed;
max-height: 160px;
overflow-y: auto;
background: black;
// font-size: 12px;
z-index: 888;
$hoverColor: #002947;
$activeColor: lighten($hoverColor, 5%);
color: $text-color;
.i-select-dropdown-item {
// font-size: 12px;
height: 20px;
line-height: 20px;
margin: 0;
padding: 0 5px;
border: none;
outline: none;
appearance: none;
display: block;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
&.active {
// background-color: $activeColor;
color: #e9c556;
}
&.active::after {
content: "✓";
position: absolute;
right: 5px;
}
&:hover:not(.active) {
// font-size: 12px;
background-color: $hoverColor;
}
}
}
.dropdown-active {
animation: dropdown-enter 0.2s ease-in;
}
.dropdown-active {
animation: dropdown-leave 0.2s ease-in;
}
.dropdown-enter-active {
animation: dropdown-enter 0.2s ease-in;
}
.dropdown-leave-active {
animation: dropdown-leave 0.2s ease-in;
}
.dropdown-enter-active {
animation: dropdown-enter 0.2s ease-in;
}
.dropdown-leave-active {
animation: dropdown-leave 0.2s ease-in;
}
</style>