init
This commit is contained in:
139
uni_modules/nutui-uni/components/searchbar/index.scss
Normal file
139
uni_modules/nutui-uni/components/searchbar/index.scss
Normal file
@@ -0,0 +1,139 @@
|
||||
.nut-theme-dark {
|
||||
.nut-searchbar {
|
||||
background: $dark-background;
|
||||
|
||||
&__search-input {
|
||||
background: $dark-background4;
|
||||
}
|
||||
|
||||
&__right-search-icon,
|
||||
&__left-search-icon {
|
||||
color: $dark-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.nut-searchbar {
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: $searchbar-width;
|
||||
padding: $searchbar-padding;
|
||||
color: $searchbar-input-bar-color;
|
||||
background: $searchbar-background;
|
||||
|
||||
&.safe-area-inset-bottom {
|
||||
position: relative;
|
||||
margin-bottom: constant(safe-area-inset-bottom);
|
||||
margin-bottom: env(safe-area-inset-bottom);
|
||||
|
||||
&::after {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: constant(safe-area-inset-bottom);
|
||||
height: env(safe-area-inset-bottom);
|
||||
content: "";
|
||||
background: $searchbar-background;
|
||||
}
|
||||
}
|
||||
|
||||
&::placeholder {
|
||||
color: $searchbar-input-bar-placeholder-color;
|
||||
}
|
||||
|
||||
&__search-input {
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex: 1;
|
||||
align-items: center;
|
||||
height: $searchbar-input-height;
|
||||
padding: $searchbar-input-padding;
|
||||
background: $searchbar-input-background;
|
||||
border-radius: $searchbar-input-border-radius;
|
||||
box-shadow: $searchbar-input-box-shadow;
|
||||
|
||||
&.square {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.nut-searchbar__input-inner {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex: 1;
|
||||
align-items: center;
|
||||
overflow: hidden;
|
||||
|
||||
.nut-searchbar__input-form {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
.nut-searchbar__input-inner-icon {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 7px;
|
||||
}
|
||||
|
||||
.nut-searchbar__input-clear {
|
||||
position: relative;
|
||||
z-index: 10;
|
||||
padding: 0 5px;
|
||||
}
|
||||
|
||||
.nut-searchbar__input-inner-icon-absolute {
|
||||
.nut-searchbar__input-clear {
|
||||
position: absolute;
|
||||
left: -20px;
|
||||
}
|
||||
}
|
||||
|
||||
.nut-searchbar__iptleft-search-icon {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
margin-right: 6px;
|
||||
}
|
||||
|
||||
.nut-searchbar__iptright-search-icon {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.nut-searchbar__input-bar {
|
||||
flex: 1;
|
||||
height: $searchbar-input-height;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
font-size: 14px;
|
||||
line-height: $searchbar-input-height;
|
||||
background-color: transparent;
|
||||
border-color: transparent;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.nut-searchbar__input-inner-absolute {
|
||||
.nut-searchbar__input-bar {
|
||||
box-sizing: border-box;
|
||||
padding-right: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__left-search-icon {
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
&__search-icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
&__right-search-icon {
|
||||
margin-left: 16px;
|
||||
font-size: 14px;
|
||||
color: $searchbar-right-out-color;
|
||||
}
|
||||
}
|
||||
2
uni_modules/nutui-uni/components/searchbar/index.ts
Normal file
2
uni_modules/nutui-uni/components/searchbar/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export * from './searchbar'
|
||||
export * from './type'
|
||||
104
uni_modules/nutui-uni/components/searchbar/searchbar.ts
Normal file
104
uni_modules/nutui-uni/components/searchbar/searchbar.ts
Normal file
@@ -0,0 +1,104 @@
|
||||
import type { InputOnBlurEvent, InputOnFocusEvent, InputOnInputEvent } from '@uni-helper/uni-app-types'
|
||||
import type { CSSProperties, ExtractPropTypes } from 'vue'
|
||||
import { BLUR_EVENT, CHANGE_EVENT, CLEAR_EVENT, FOCUS_EVENT, SEARCH_EVENT, UPDATE_MODEL_EVENT } from '../_constants'
|
||||
import {
|
||||
commonProps,
|
||||
isString,
|
||||
makeNumberProp,
|
||||
makeNumericProp,
|
||||
makeObjectProp,
|
||||
makeStringProp,
|
||||
nullableBooleanProp,
|
||||
numericProp,
|
||||
truthProp,
|
||||
} from '../_utils'
|
||||
import type { InputAlignType, InputConfirmType, InputType } from '../input'
|
||||
import type { SearchbarShape } from './type'
|
||||
|
||||
export const searchbarProps = {
|
||||
...commonProps,
|
||||
/**
|
||||
* @description 当前输入的值
|
||||
*/
|
||||
modelValue: makeNumericProp(''),
|
||||
/**
|
||||
* @description 输入框类型
|
||||
*/
|
||||
inputType: makeStringProp<InputType>('text'),
|
||||
/**
|
||||
* @description 搜索框形状,可选值为 `square` `round`
|
||||
*/
|
||||
shape: makeStringProp<SearchbarShape>('round'),
|
||||
/**
|
||||
* @description 最大输入长度
|
||||
*/
|
||||
maxLength: numericProp,
|
||||
/**
|
||||
* @description 输入框默认占位符
|
||||
*/
|
||||
placeholder: String,
|
||||
/**
|
||||
* @description 是否展示清除按钮
|
||||
*/
|
||||
clearable: truthProp,
|
||||
/**
|
||||
* @description 自定义清除按钮图标
|
||||
*/
|
||||
clearIcon: makeStringProp('circle-close'),
|
||||
/**
|
||||
* @description 输入框外部背景
|
||||
*/
|
||||
background: String,
|
||||
/**
|
||||
* @description 输入框内部背景
|
||||
*/
|
||||
inputBackground: String,
|
||||
/**
|
||||
* @description 聚焦时搜索框样式
|
||||
*/
|
||||
focusStyle: makeObjectProp<CSSProperties>({}),
|
||||
/**
|
||||
* @description 是否自动聚焦
|
||||
*/
|
||||
autofocus: Boolean,
|
||||
/**
|
||||
* @description 是否禁用输入框
|
||||
*/
|
||||
disabled: nullableBooleanProp,
|
||||
/**
|
||||
* @description 输入框只读
|
||||
*/
|
||||
readonly: Boolean,
|
||||
/**
|
||||
* @description 对齐方式,可选 `left` `center` `right`
|
||||
*/
|
||||
inputAlign: makeStringProp<InputAlignType>('left'),
|
||||
/**
|
||||
* @description 键盘右下角按钮的文字,仅在`type='text'`时生效,可选值 `send`:发送、`search`:搜索、`next`:下一个、`go`:前往、`done`:完成
|
||||
*/
|
||||
confirmType: makeStringProp<InputConfirmType>('done'),
|
||||
/**
|
||||
* @description 是否开启 iphone 系列全面屏底部安全区适配
|
||||
*/
|
||||
safeAreaInsetBottom: Boolean,
|
||||
/**
|
||||
* @description 指定的距离的最小值作为光标与键盘的距离
|
||||
*/
|
||||
cursorSpacing: makeNumberProp(0),
|
||||
}
|
||||
|
||||
export type SearchbarProps = ExtractPropTypes<typeof searchbarProps>
|
||||
|
||||
export const searchbarEmits = {
|
||||
[UPDATE_MODEL_EVENT]: (val: string, event: InputOnInputEvent) => (isString(val) || val === undefined) && event instanceof Object,
|
||||
[CHANGE_EVENT]: (val: string, event: InputOnInputEvent) => (isString(val) || val === undefined) && event instanceof Object,
|
||||
[BLUR_EVENT]: (val: string, event: InputOnBlurEvent) => (isString(val) || val === undefined) && event instanceof Object,
|
||||
[FOCUS_EVENT]: (val: string, event: InputOnFocusEvent) => (isString(val) || val === undefined) && event instanceof Object,
|
||||
[CLEAR_EVENT]: (val: string) => (isString(val) || val === undefined),
|
||||
[SEARCH_EVENT]: (val: string) => (isString(val) || val === undefined),
|
||||
clickInput: (val: string, event: Event) => (isString(val) || val === undefined) && event instanceof Object,
|
||||
clickLeftIcon: (val: string, event: Event) => (isString(val) || val === undefined) && event instanceof Object,
|
||||
clickRightIcon: (val: string, event: Event) => (isString(val) || val === undefined) && event instanceof Object,
|
||||
}
|
||||
|
||||
export type SearchbarEmits = typeof searchbarEmits
|
||||
217
uni_modules/nutui-uni/components/searchbar/searchbar.vue
Normal file
217
uni_modules/nutui-uni/components/searchbar/searchbar.vue
Normal file
@@ -0,0 +1,217 @@
|
||||
<script setup lang="ts">
|
||||
import type { InputOnBlurEvent, InputOnFocusEvent, InputOnInputEvent } from '@uni-helper/uni-app-types'
|
||||
import type { CSSProperties } from 'vue'
|
||||
import { computed, defineComponent, reactive, toRef, useSlots } from 'vue'
|
||||
import { BLUR_EVENT, CHANGE_EVENT, CLEAR_EVENT, FOCUS_EVENT, PREFIX, SEARCH_EVENT, UPDATE_MODEL_EVENT } from '../_constants'
|
||||
import { getMainClass, getMainStyle } from '../_utils'
|
||||
import { useTranslate } from '../../locale'
|
||||
import { useFormDisabled } from '../form/form'
|
||||
import NutIcon from '../icon/icon.vue'
|
||||
import { searchbarEmits, searchbarProps } from './searchbar'
|
||||
|
||||
const props = defineProps(searchbarProps)
|
||||
|
||||
const emit = defineEmits(searchbarEmits)
|
||||
|
||||
const slots = useSlots()
|
||||
|
||||
function hasSlot(name: string) {
|
||||
return Boolean(slots[name])
|
||||
}
|
||||
|
||||
const formDisabled = useFormDisabled(toRef(props, 'disabled'))
|
||||
|
||||
const state = reactive({
|
||||
active: false,
|
||||
})
|
||||
|
||||
function stringModelValue() {
|
||||
if (props.modelValue == null)
|
||||
return ''
|
||||
|
||||
return String(props.modelValue)
|
||||
}
|
||||
|
||||
const innerValue = computed<string>(() => {
|
||||
return stringModelValue()
|
||||
})
|
||||
|
||||
const innerMaxLength = computed(() => {
|
||||
if (props.maxLength == null)
|
||||
return -1
|
||||
|
||||
return Number(props.maxLength)
|
||||
})
|
||||
|
||||
const classes = computed(() => {
|
||||
return getMainClass(props, componentName, {
|
||||
'safe-area-inset-bottom': props.safeAreaInsetBottom,
|
||||
})
|
||||
})
|
||||
|
||||
const styles = computed(() => {
|
||||
return getMainStyle(props, {
|
||||
background: props.background,
|
||||
})
|
||||
})
|
||||
|
||||
const inputWrapperStyles = computed(() => {
|
||||
const style: CSSProperties = {
|
||||
background: props.inputBackground,
|
||||
}
|
||||
|
||||
if (state.active)
|
||||
Object.assign(style, props.focusStyle)
|
||||
|
||||
return style
|
||||
})
|
||||
|
||||
const inputStyles = computed(() => {
|
||||
return {
|
||||
textAlign: props.inputAlign,
|
||||
}
|
||||
})
|
||||
|
||||
function handleValue(value: string) {
|
||||
if (innerMaxLength.value > 0 && value.length > innerMaxLength.value)
|
||||
value = value.slice(0, innerMaxLength.value)
|
||||
|
||||
return value
|
||||
}
|
||||
|
||||
function handleInput(event: InputOnInputEvent) {
|
||||
const value = handleValue(event.detail.value)
|
||||
|
||||
emit(UPDATE_MODEL_EVENT, value, event)
|
||||
emit(CHANGE_EVENT, value, event)
|
||||
}
|
||||
|
||||
function handleFocus(event: InputOnFocusEvent) {
|
||||
const value = handleValue(event.detail.value)
|
||||
|
||||
state.active = true
|
||||
|
||||
emit(FOCUS_EVENT, value, event)
|
||||
}
|
||||
|
||||
function handleBlur(event: InputOnBlurEvent) {
|
||||
const value = handleValue(event.detail.value)
|
||||
|
||||
setTimeout(() => {
|
||||
state.active = false
|
||||
}, 200)
|
||||
|
||||
emit(BLUR_EVENT, value, event)
|
||||
}
|
||||
|
||||
function handleClear(event: any) {
|
||||
emit(UPDATE_MODEL_EVENT, '', event)
|
||||
emit(CHANGE_EVENT, '', event)
|
||||
emit(CLEAR_EVENT, '')
|
||||
}
|
||||
|
||||
function handleSubmit() {
|
||||
emit(SEARCH_EVENT, innerValue.value)
|
||||
}
|
||||
|
||||
function handleInputClick(event: any) {
|
||||
emit('clickInput', innerValue.value, event)
|
||||
}
|
||||
|
||||
function handleLeftIconClick(event: any) {
|
||||
emit('clickLeftIcon', innerValue.value, event)
|
||||
}
|
||||
|
||||
function handleRightIconClick(event: any) {
|
||||
emit('clickRightIcon', innerValue.value, event)
|
||||
}
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
const componentName = `${PREFIX}-searchbar`
|
||||
const { translate } = useTranslate(componentName)
|
||||
|
||||
export default defineComponent({
|
||||
name: componentName,
|
||||
options: {
|
||||
virtualHost: true,
|
||||
addGlobalClass: true,
|
||||
styleIsolation: 'shared',
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view :class="classes" :style="styles">
|
||||
<view
|
||||
v-if="hasSlot('leftout')"
|
||||
class="nut-searchbar__search-icon nut-searchbar__left-search-icon"
|
||||
@click="handleLeftIconClick"
|
||||
>
|
||||
<slot name="leftout" />
|
||||
</view>
|
||||
<view class="nut-searchbar__search-input" :class="[props.shape]" :style="inputWrapperStyles">
|
||||
<view v-if="hasSlot('leftin')" class="nut-searchbar__search-icon nut-searchbar__iptleft-search-icon">
|
||||
<slot name="leftin" />
|
||||
</view>
|
||||
<view class="nut-searchbar__input-inner" :class="{ 'nut-searchbar__input-inner-absolute': hasSlot('rightin') }">
|
||||
<form
|
||||
class="nut-searchbar__input-form"
|
||||
action="#"
|
||||
onsubmit="return false"
|
||||
@submit.prevent="handleSubmit"
|
||||
>
|
||||
<input
|
||||
class="nut-searchbar__input-bar"
|
||||
:class="{ 'nut-searchbar__input-bar_clear': props.clearable }"
|
||||
:style="inputStyles"
|
||||
:type="props.inputType as any"
|
||||
:maxlength="innerMaxLength"
|
||||
:placeholder="props.placeholder || translate('placeholder')"
|
||||
:value="innerValue"
|
||||
:focus="props.autofocus"
|
||||
:confirm-type="props.confirmType"
|
||||
:disabled="formDisabled"
|
||||
:readonly="props.readonly"
|
||||
:cursor-spacing="props.cursorSpacing"
|
||||
@click="handleInputClick"
|
||||
@input="handleInput"
|
||||
@focus="handleFocus"
|
||||
@blur="handleBlur"
|
||||
@confirm="handleSubmit"
|
||||
>
|
||||
</form>
|
||||
</view>
|
||||
<view
|
||||
class="nut-searchbar__input-inner-icon"
|
||||
:class="{ 'nut-searchbar__input-inner-icon-absolute': hasSlot('rightin') }"
|
||||
>
|
||||
<view
|
||||
v-if="props.clearable"
|
||||
class="nut-searchbar__search-icon nut-searchbar__input-clear"
|
||||
:class="{ 'nut-hidden': innerValue.length <= 0 }"
|
||||
@click="handleClear"
|
||||
>
|
||||
<template v-if="hasSlot('clear-icon')">
|
||||
<slot name="clear-icon" />
|
||||
</template>
|
||||
<NutIcon v-else :name="props.clearIcon" />
|
||||
</view>
|
||||
<view
|
||||
v-if="hasSlot('rightin')"
|
||||
class="nut-searchbar__search-icon nut-searchbar__iptright-search-icon"
|
||||
@click="handleRightIconClick"
|
||||
>
|
||||
<slot name="rightin" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view v-if="hasSlot('rightout')" class="nut-searchbar__search-icon nut-searchbar__right-search-icon">
|
||||
<slot name="rightout" />
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
@import "./index";
|
||||
</style>
|
||||
2
uni_modules/nutui-uni/components/searchbar/type.ts
Normal file
2
uni_modules/nutui-uni/components/searchbar/type.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export const searchbarShape = ['square', 'round']
|
||||
export type SearchbarShape = (typeof searchbarShape)[number]
|
||||
Reference in New Issue
Block a user