This commit is contained in:
2022-12-09 16:41:41 +08:00
parent c1cce5a7c2
commit ff7aa8774f
2003 changed files with 156639 additions and 140 deletions

View File

@@ -0,0 +1,332 @@
<script lang="ts">
export default {
name: "LayTagInput",
};
</script>
<script lang="ts" setup>
import "./index.less";
import LayTag, { TagProps } from "../tag/index.vue";
import LayToopTip from "../tooltip/index.vue";
import {
onMounted,
shallowRef,
ref,
watch,
computed,
reactive,
nextTick,
} from "vue";
import { isObject, reactiveOmit, useResizeObserver } from "@vueuse/core";
import { LayIcon } from "@layui/icons-vue";
import { TagInputSize } from "./inerface";
export interface TagData {
value?: string | number;
label?: string;
closable?: boolean;
[other: string]: any;
}
export interface TagInputProps {
modelValue?: (string | number | TagData)[];
inputValue?: string;
disabled?: boolean;
placeholder?: string;
readonly?: boolean;
allowClear?: boolean;
max?: number;
minCollapsedNum?: number;
collapseTagsTooltip?: boolean;
size?: TagInputSize;
tagProps?: TagProps;
disabledInput?: boolean;
}
const props = withDefaults(defineProps<TagInputProps>(), {
placeholder: undefined,
minCollapsedNum: 0,
size: "md",
});
const emit = defineEmits([
"update:modelValue",
"update:inputValue",
"change",
"inputValueChange",
"remove",
"clear",
"focus",
"blur",
"pressEnter",
]);
const mirrorRefEl = shallowRef<HTMLElement | undefined>(undefined);
const inputRefEl = shallowRef<HTMLInputElement | undefined>(undefined);
const oldInputValue = ref<string>("");
const compositionValue = ref<string>("");
const isComposing = ref(false);
const inputStyle = reactive({ width: "15px" });
const _tagProps = reactive(props.tagProps ?? {});
const tagProps = reactiveOmit(_tagProps, "closable", "size", "disabled");
const inputValue = computed({
get() {
return props.inputValue;
},
set(val) {
emit("update:inputValue", val);
emit("inputValueChange", val);
},
});
const tagData = computed({
get() {
return props.modelValue;
},
set(val) {
emit("update:modelValue", val);
emit("change", val);
},
});
const normalizedTags = computed(() => normalizedTagData(tagData.value ?? []));
const computedTagData = computed(() => {
if (!normalizedTags.value) return;
return props.minCollapsedNum
? normalizedTags.value?.slice(0, props.minCollapsedNum)
: normalizedTags.value;
});
const collapsedTagData = computed(() => {
if (!normalizedTags.value) return;
return props.minCollapsedNum &&
normalizedTags.value?.length > props.minCollapsedNum
? normalizedTags.value?.slice(props.minCollapsedNum)
: [];
});
const handleInput = function (e: Event) {
if (isComposing.value) {
return;
}
inputValue.value = (e.target as HTMLInputElement).value;
};
const handleComposition = (e: CompositionEvent) => {
if (e.type === "compositionend") {
isComposing.value = false;
compositionValue.value = "";
handleInput(e);
} else {
isComposing.value = true;
compositionValue.value = inputValue.value + (e.data ?? "");
}
};
const handleEnter = (e: KeyboardEvent) => {
e.preventDefault();
const valueStr = inputValue.value ? String(inputValue.value).trim() : "";
if (!valueStr || !tagData.value) return;
const isLimit = props.max && tagData.value?.length >= props.max;
if (!isLimit) {
tagData.value =
tagData.value instanceof Array
? tagData.value.concat(String(valueStr))
: [valueStr];
inputValue.value = "";
}
emit("pressEnter", inputValue.value, e);
};
const handleBackspaceKeyUp = (e: KeyboardEvent) => {
if (!tagData.value || !tagData.value.length) return;
if (!oldInputValue.value && ["Backspace", "Delete"].includes(e.code)) {
const lastIndex = normalizedTags.value.length - 1;
handleClose(normalizedTags.value[lastIndex].value, lastIndex, e);
}
oldInputValue.value = inputValue.value ?? "";
};
const handleFocus = (e: FocusEvent) => {
emit("focus", e);
(inputRefEl.value as HTMLInputElement)?.focus();
};
const handleBlur = (e: FocusEvent) => {
emit("blur", e);
(inputRefEl.value as HTMLInputElement)?.blur();
};
const handleClearClick = (e: MouseEvent) => {
if (props.disabled || props.readonly || !props.allowClear) {
return;
}
tagData.value = [];
emit("clear", e);
};
const handleClose = (
value: string | number | undefined,
index: number,
e: Event
) => {
if (!tagData.value) return;
const arr = [...tagData.value];
arr.splice(index, 1);
tagData.value = arr;
emit("remove", value, e);
};
const handleMouseDown = (e: MouseEvent) => {
if (inputRefEl.value) {
e.preventDefault();
inputRefEl.value.focus();
}
};
const setInputWidth = (width: number) => {
if (width > 15) {
inputStyle.width = `${width}px`;
} else {
inputStyle.width = "15px";
}
};
const handleResize = () => {
if (mirrorRefEl.value) {
setInputWidth(mirrorRefEl.value.offsetWidth);
}
};
const cls = computed(() => [
`layui-tag-input`,
`layui-tag-input-${props.size}`,
{
"layui-tag-input-disabled": props.disabled,
},
]);
const normalizedTagData = (value: Array<string | number | TagData>) =>
value.map((item) => {
if (isObject(item)) return item;
return {
value: item,
label: String(item),
closable: true,
};
});
useResizeObserver(mirrorRefEl, () => {
handleResize();
});
watch(
() => inputValue.value,
(val) => {
if (inputRefEl.value && !isComposing.value) {
nextTick(() => {
inputRefEl.value!.value = val ?? "";
});
}
}
);
const moreCount = computed(() => {
if (tagData.value && computedTagData.value) {
return tagData.value.length - computedTagData.value.length;
}
});
onMounted(() => {
handleResize();
});
defineExpose({
focus: handleFocus,
blur: handleBlur,
});
</script>
<template>
<div :class="cls" @mousedown="handleMouseDown">
<span ref="mirrorRefEl" class="layui-tag-input-mirror">
{{ compositionValue || inputValue || placeholder }}
</span>
<span v-if="$slots.prefix">
<slot name="prefix"></slot>
</span>
<span class="layui-tag-input-inner">
<template
v-for="(item, index) of computedTagData"
:key="`${item}-${index}`"
>
<LayTag
v-bind="tagProps"
:closable="!readonly && !disabled && item.closable"
:size="size"
@close="handleClose(item.value, index, $event)"
>
{{ item.label }}
</LayTag>
</template>
<template v-if="computedTagData?.length != tagData?.length">
<LayToopTip
:isDark="false"
trigger="hover"
popperStyle="padding:6px"
:disabled="!collapseTagsTooltip"
>
<LayTag v-bind="tagProps" key="more" :closable="false" :size="size">
+{{ moreCount }}...
</LayTag>
<template #content>
<div class="layui-tag-input-collapsed-panel">
<LayTag
v-for="(item, index) of collapsedTagData"
:key="`${item}-${index}`"
v-bind="tagProps"
:closable="!readonly && !disabled && item.closable"
:size="size"
@close="
handleClose(
item.value,
index + (minCollapsedNum ?? 0),
$event
)
"
>
{{ item.label }}
</LayTag>
</div>
</template>
</LayToopTip>
</template>
<template v-if="!disabledInput">
<input
ref="inputRefEl"
class="layui-tag-input-inner-input"
:style="inputStyle"
:disabled="disabled"
:placeholder="placeholder"
:readonly="readonly"
@keydown.enter="handleEnter"
@keyup="handleBackspaceKeyUp"
@input="handleInput"
@focus="handleFocus"
@blur="handleBlur"
@compositionstart="handleComposition"
@compositionupdate="handleComposition"
@compositionend="handleComposition"
/>
</template>
</span>
<span
v-if="allowClear && tagData?.length && !disabled"
class="layui-tag-input-clear"
>
<lay-icon type="layui-icon-close-fill" @click.stop="handleClearClick" />
</span>
<span class="layui-tag-input-suffix" v-if="$slots.suffix">
<slot name="suffix"></slot>
</span>
</div>
</template>

View File

@@ -0,0 +1,76 @@
import { w as withInstall } from "../badge/index2.js";
import { defineComponent, computed, openBlock, createElementBlock, normalizeClass, unref, normalizeStyle, createCommentVNode, renderSlot } from "vue";
var index = /* @__PURE__ */ (() => ":root{--button-primary-text-color: #fff;--button-primary-background-color: var(--global-primary-color);--button-primary-border-color: var(--global-primary-color);--button-normal-text-color: #fff;--button-normal-background-color: var(--global-normal-color);--button-normal-border-color: var(--global-normal-color);--button-warm-text-color: #fff;--button-warm-background-color: var(--global-warm-color);--button-warm-border-color: var(--global-warm-color);--button-danger-text-color: #fff;--button-danger-background-color: var(--global-danger-color);--button-danger-border-color: var(--global-danger-color);--button-border-radius: var(--global-border-radius);--button-border-color: var(--global-neutral-color-6);--button-background-color: 0 0;--button-text-color: #666}.layui-btn{height:38px;line-height:36px;padding:0 18px;font-size:14px;text-align:center;white-space:nowrap;color:var(--button-text-color);background:var(--button-background-color);border-radius:var(--button-border-radius);border-color:var(--button-border-color);border-width:1px;border-style:solid;cursor:pointer}.layui-btn-primary{color:var(--button-primary-text-color);background-color:var(--button-primary-background-color);border-color:var(--button-primary-border-color)}.layui-btn-normal{color:var(--button-normal-text-color);background-color:var(--button-normal-background-color);border-color:var(--button-normal-border-color)}.layui-btn-warm{color:var(--button-warm-text-color);background-color:var(--button-warm-background-color);border-color:var(--button-warm-border-color)}.layui-btn-danger{color:var(--button-danger-text-color);background-color:var(--button-danger-background-color);border-color:var(--button-danger-border-color)}.layui-btn:hover{opacity:.8;filter:alpha(opacity=80)}.layui-btn:active{opacity:1;filter:alpha(opacity=100)}.layui-btn-lg{height:44px;line-height:44px;padding:0 25px;font-size:16px}.layui-btn-sm{height:30px;line-height:30px;padding:0 10px;font-size:12px}.layui-btn-xs{height:22px;line-height:22px;padding:0 5px;font-size:12px}.layui-btn-xs i{font-size:12px!important}.layui-btn-fluid{width:100%}.layui-btn-radius{border-radius:100px}.layui-btn-disabled,.layui-btn-disabled:active,.layui-btn-disabled:hover{border-color:#eee!important;background-color:#fbfbfb!important;color:#d2d2d2!important;cursor:not-allowed!important;opacity:1}.layui-btn+.layui-btn{margin-left:10px}.layui-btn .layui-icon{padding:0 2px;vertical-align:middle\\ ;vertical-align:bottom}\n")();
const ButtonEmits = {
click: (evt) => evt instanceof MouseEvent
};
const _hoisted_1 = ["type"];
const __default__ = {
name: "LayButton"
};
const _sfc_main = defineComponent({
...__default__,
props: {
type: null,
size: null,
prefixIcon: null,
suffixIcon: null,
loadingIcon: { default: "layui-icon-loading-one" },
borderStyle: { default: "soild" },
border: null,
fluid: { type: Boolean, default: false },
radius: { type: Boolean, default: false },
loading: { type: Boolean, default: false },
disabled: { type: Boolean },
nativeType: { default: "button" }
},
emits: ButtonEmits,
setup(__props, { emit: emits }) {
const props = __props;
const onClick = (event) => {
if (!props.disabled) {
emits("click", event);
}
};
const styles = computed(() => {
return {
border: `1px ${props.borderStyle}`
};
});
const classes = computed(() => {
return [
{
"layui-btn-fluid": props.fluid,
"layui-btn-radius": props.radius,
"layui-btn-disabled": props.disabled
},
props.type ? `layui-btn-${props.type}` : "",
props.size ? `layui-btn-${props.size}` : "",
props.border ? `layui-border-${props.border}` : ""
];
});
return (_ctx, _cache) => {
return openBlock(), createElementBlock("button", {
class: normalizeClass(["layui-btn", unref(classes)]),
style: normalizeStyle(unref(styles)),
type: __props.nativeType,
onClick
}, [
__props.prefixIcon ? (openBlock(), createElementBlock("i", {
key: 0,
class: normalizeClass(`layui-icon ${__props.prefixIcon}`)
}, null, 2)) : createCommentVNode("", true),
__props.loading ? (openBlock(), createElementBlock("i", {
key: 1,
class: normalizeClass([__props.loadingIcon, "layui-icon layui-anim layui-anim-rotate layui-anim-loop"])
}, null, 2)) : renderSlot(_ctx.$slots, "default", { key: 2 }),
__props.suffixIcon ? (openBlock(), createElementBlock("i", {
key: 3,
class: normalizeClass(`layui-icon ${__props.suffixIcon}`)
}, null, 2)) : createCommentVNode("", true)
], 14, _hoisted_1);
};
}
});
const component = withInstall(_sfc_main);
export { _sfc_main as _, component as c };

View File

@@ -0,0 +1,199 @@
::: anchor
:::
::: title 字体使用
:::
::: demo
<template>
<div style="max-width: 100%; ">
<lay-tooltip content="缩小浏览器时...假装这里有文字提示假装这里有文字提示假装这里有文字提示假装这里有文字提示" ref="tooltip" :isAutoShow="true">
缩小浏览器时...假装这里有文字提示假装这里有文字提示假装这里有文字提示假装这里有文字提示
</lay-tooltip>
</div>
<div style="max-width: 124px; margin-top: 20px">
<lay-tooltip content="假装这里有文字提示" ref="tooltip" :isAutoShow="true">
假装这里有文字提示
</lay-tooltip>
</div>
<div style="width: 126px; margin-top: 20px">
<lay-tooltip content="假装这里有文字提示" ref="tooltip" :isAutoShow="true">
假装这里有文字提示
</lay-tooltip>
</div>
</template>
<script>
import { ref } from 'vue';
export default {
setup() {
}
}
</script>
<style>
</style>
:::
::: title 基础使用
:::
::: demo
<template>
<lay-tooltip content="假装这里有文字提示" trigger="click">
<lay-button>tooltip</lay-button>
</lay-tooltip>
</template>
:::
::: title 受控模式
:::
::: demo
<template>
<lay-tooltip :visible="visible" trigger="click" content="假装这里有文字提示" >
<lay-button @mouseenter="visible=true" @mouseleave="visible=false">tooltip</lay-button>
</lay-tooltip>
</template>
<script>
import { ref,watch } from 'vue';
export default {
setup() {
const visible = ref(false)
return {
visible,
}
}
}
</script>
<style>
</style>
:::
::: title 显示位置
:::
::: demo
<template>
<div style="padding: 100px;max-width:400px;">
<div style="text-align: center;">
<lay-tooltip trigger="click" content="假装这里有文字提示">
<lay-button>上边</lay-button>
</lay-tooltip>
</div>
<div>
<lay-tooltip trigger="click" content="假装这里有文字提示假装这里有文字提示假装这里有文字提示假装这里有文字提示假装这里有文字提示" position="left">
<lay-button style="float:left;">左边</lay-button>
</lay-tooltip>
<lay-tooltip trigger="click" content="假装这里有文字提示假装这里有文字提示假装这里有文字提示假装这里有文字提示假装这里有文字提示" position="right">
<lay-button style="float:right;">右边</lay-button>
</lay-tooltip>
<div style="clear: both;"></div>
</div>
<div style="text-align: center;">
<lay-tooltip trigger="click" content="假装这里有文字提示假装这里有文字提示假装这里有文字提示假装这里有文字提示" position="bottom">
<lay-button>下边</lay-button>
</lay-tooltip>
</div>
</div>
</template>
<style>
</style>
:::
::: title 浅色主题
:::
::: demo
<template>
<lay-tooltip content="不明白是是非非,只知我不会不在。" :is-dark="false">
<lay-button >tooltip</lay-button>
</lay-tooltip>
</template>
<script>
</script>
:::
::: title 是否禁用/动态属性
:::
::: demo
<template>
<lay-tooltip :content="content" :is-dark="isDark" :disabled="!disabled">
<lay-button>tooltip</lay-button>
</lay-tooltip>
<lay-switch v-model="disabled" onswitch-text="启用tooltip" unswitch-text="禁用tooltip" style="margin-left: 5px;"></lay-switch>
<lay-switch v-model="isDark" onswitch-text="深色" unswitch-text="浅色" style="margin-left: 5px;"></lay-switch>
</template>
<script>
import { ref } from 'vue';
export default {
setup() {
const contentArr = [
"不明白是是非非,只知我不会不在。",
"千山万水,去程是你,归程也是你。",
"一约既定,万山无阻。",
"时光都淡了,我还伴着你。",
"只问深情,不问西东。",
"感谢曾经在我身边的,一直在我身边。",
"经年再相逢,魂梦与子同。"
];
const rendonCotent = function(){
return contentArr[Math.floor(Math.random() * contentArr.length)];
};
const content = ref(rendonCotent())
const isDark = ref(false)
const disabled = ref(true)
setInterval(()=> content.value = rendonCotent(), 1000)
return {
content,
isDark,
disabled
}
}
}
</script>
<style>
</style>
:::
::: title Tooltip 属性
:::
::: table
| 属性 | 描述 | 可选值 |
| ----------- | -------- | -------------- |
| content | 显示内容 | -- |
| position | 显示位置 | `top`(默认值)、`bottom`、`left`、`right` |
| isDark | 是否为黑色主题 | `true`(默认值)、`false`(浅色) |
| disabled | 是否禁用 | `false`(默认值)、`true`(禁用) |
| isCanHide | 控制是否可以隐藏,可参考`lay-slider`组件 | `true`(默认值)、`false` |
| isAutoShow | 控制超出文本 `...` 时自动展示, 没有 `...` 时不展示 | `false`(默认值)、`true` |
| visible | 控制显示/隐藏| `true` `false`(默认值)|
| enterable | 鼠标能否进入 tooltip 中 | `true`(默认值) `false`|
| trigger | 触发方式| `click` `hover`(默认值) `contextmenu` `focus` `trigger[]`|
:::
::: contributor tooltip
:::
::: previousNext tooltip
:::

View File

@@ -0,0 +1,42 @@
#!/bin/sh
#
# An example hook script to prepare the commit log message.
# Called by "git commit" with the name of the file that has the
# commit message, followed by the description of the commit
# message's source. The hook's purpose is to edit the commit
# message file. If the hook fails with a non-zero status,
# the commit is aborted.
#
# To enable this hook, rename this file to "prepare-commit-msg".
# This hook includes three examples. The first one removes the
# "# Please enter the commit message..." help message.
#
# The second includes the output of "git diff --name-status -r"
# into the message, just before the "git status" output. It is
# commented because it doesn't cope with --amend or with squashed
# commits.
#
# The third example adds a Signed-off-by line to the message, that can
# still be edited. This is rarely a good idea.
COMMIT_MSG_FILE=$1
COMMIT_SOURCE=$2
SHA1=$3
/usr/bin/perl -i.bak -ne 'print unless(m/^. Please enter the commit message/..m/^#$/)' "$COMMIT_MSG_FILE"
# case "$COMMIT_SOURCE,$SHA1" in
# ,|template,)
# /usr/bin/perl -i.bak -pe '
# print "\n" . `git diff --cached --name-status -r`
# if /^#/ && $first++ == 0' "$COMMIT_MSG_FILE" ;;
# *) ;;
# esac
# SOB=$(git var GIT_COMMITTER_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p')
# git interpret-trailers --in-place --trailer "$SOB" "$COMMIT_MSG_FILE"
# if test -z "$COMMIT_SOURCE"
# then
# /usr/bin/perl -i.bak -pe 'print "\n" if !$first_line++' "$COMMIT_MSG_FILE"
# fi

View File

@@ -0,0 +1,14 @@
import "../badge/index2.js";
export { c as default } from "./index2.js";
import "vue";
import "../datePicker/index2.js";
import "../_chunks/dayjs/index.js";
import "../_chunks/@umijs/index.js";
import "../input/index2.js";
import "../checkbox/index2.js";
import "../dropdownMenu/index2.js";
import "../dropdown/index2.js";
import "../_chunks/@vueuse/index.js";
import "../_chunks/vue-i18n/index.js";
import "../_chunks/@intlify/index.js";
import "../_chunks/@vue/index.js";

File diff suppressed because one or more lines are too long