refactor: make components with detached parts work in modal

This commit is contained in:
07akioni 2020-03-06 16:13:45 +08:00
parent 20f3024449
commit 57137d926c
30 changed files with 890 additions and 262 deletions

View File

@ -39,7 +39,7 @@ ajaxUsage
|loading|`boolean`|`false`||
|scroll-x|`number \| string`|`null`|表格内容的横向宽度,如果列被水平固定了,则需要设定它|
|pagination|`false \| object`|`false`|属性参考 [Pagination props](n-pagination#Props)|
|paging|`boolean`|表格是否自动分页数据,在异步的状况下你可呢个要把它设为 `false`|
|paging|`boolean`|表格是否自动分页数据,在异步的状况下你可能需要把它设为 `false`|
|row-class-name|`string \| (rowData: object, index : number) => string \| object`|`null`||
|checked-row-keys|`Array<string \| number> \| null`|`null`||
|default-checked-row-keys|`Array<string \| number>`|`[]`||

View File

@ -0,0 +1,249 @@
# Dark Debug 1
```html
<n-button @click="modalActive = !modalActive">Toggle</n-button>
<n-drawer
v-model="modalActive"
width="800"
>
<n-radio-group v-model="size" name="top-size" style="margin-bottom: 12px;">
<n-radio-button value="small"></n-radio-button>
<n-radio-button value="medium" ></n-radio-button>
<n-radio-button value="large"></n-radio-button>
</n-radio-group>
<n-form
:model="model"
:rules="rules"
:size="size"
ref="form"
label-placement="top"
>
<n-row :gutter="24">
<n-form-item-col :span="12" label="Input" path="inputValue">
<n-input placeholder="Input" v-model="model.inputValue" />
</n-form-item-col :span="12">
<n-form-item-col :span="12" label="Textarea" path="textareaValue">
<n-input placeholder="Textarea" v-model="model.textareaValue" type="textarea"
:autosize="{
minRows: 3,
maxRows: 5
}"
/>
</n-form-item-col>
</n-row>
<n-row :gutter="24">
<n-form-item-col :span="12" label="Select" path="selectValue">
<n-select placeholder="Select" :options="generalOptions" v-model="model.selectValue"/>
</n-form-item-col>
<n-form-item-col :span="12" label="Multiple Select" path="multipleSelectValue">
<n-select placeholder="Select" :options="generalOptions" v-model="model.multipleSelectValue" multiple/>
</n-form-item-col>
</n-row>
<n-row :gutter="24">
<n-form-item-col :span="12" label="Datetime" path="datetimeValue">
<n-date-picker type="datetime" v-model="model.datetimeValue"/>
</n-form-item-col>
<n-form-item-col :span="12" label="Switch" path="switchValue">
<n-switch v-model="model.switchValue" />
</n-form-item-col>
</n-row>
<n-row :gutter="24">
<n-form-item-col :span="12" label="Checkbox Group" path="checkboxGroupValue">
<n-checkbox-group v-model="model.checkboxGroupValue">
<n-checkbox value="Option 1">Option 1</n-checkbox>
<n-checkbox value="Option 2">Option 2</n-checkbox>
<n-checkbox value="Option 3">Option 3</n-checkbox>
</n-checkbox-group>
</n-form-item-col>
<n-form-item-col :span="12" label="Radio Group" path="radioGroupValue">
<n-radio-group v-model="model.radioGroupValue" name="radiogroup1">
<n-radio value="Radio 1">Radio 1</n-radio>
<n-radio value="Radio 2">Radio 2</n-radio>
<n-radio value="Radio 3">Radio 3</n-radio>
</n-radio-group>
</n-form-item-col>
</n-row>
<n-row :gutter="24">
<n-form-item-col :span="12" label="Radio Button Group" path="radioGroupValue">
<n-radio-group v-model="model.radioGroupValue" name="radiogroup2">
<n-radio-button value="Radio 1">Radio 1</n-radio-button>
<n-radio-button value="Radio 2">Radio 2</n-radio-button>
<n-radio-button value="Radio 3">Radio 3</n-radio-button>
</n-radio-group>
</n-form-item-col>
<n-form-item-col :span="12" label="Input Number" path="inputNumberValue">
<n-input-number v-model="model.inputNumberValue"/>
</n-form-item-col>
</n-row>
<n-row :gutter="24">
<n-form-item-col :span="12" label="Time Picker" path="timePickerValue">
<n-time-picker v-model="model.timePickerValue" />
</n-form-item-col>
<n-form-item-col :span="12" label="Slider" path="sliderValue">
<n-slider v-model="model.sliderValue" :step="5"/>
</n-form-item-col>
</n-row>
<n-row :gutter="24">
<n-form-item-col :span="14" label="Transfer" path="transferValue">
<n-transfer
style="width: 100%;"
v-model="model.transferValue"
:options="generalOptions"
/>
</n-form-item-col>
<n-form-item-col :span="5" label="Nested Path" path="nestedValue.path1">
<n-cascader placeholder="Nested Path 1" v-model="model.nestedValue.path1" :options="cascaderOptions"/>
</n-form-item-col>
<n-form-item-col :span="5" path="nestedValue.path2">
<n-select placeholder="Nested Path 2" :options="generalOptions" v-model="model.nestedValue.path2"/>
</n-form-item-col>
</n-row>
<n-row>
<n-col :span="24">
<div style="display: flex; justify-content: flex-end;">
<n-button @click="handleValidateButtonClick" round type="primary">验证</n-button>
</div>
</n-col>
</n-row>
</n-form>
</n-drawer>
```
```js
export default {
data () {
return {
modalActive: false,
size: 'medium',
model: {
inputValue: null,
textareaValue: null,
selectValue: null,
multipleSelectValue: null,
datetimeValue: null,
nestedValue: {
path1: null,
path2: null
},
switchValue: false,
checkboxGroupValue: null,
radioGroupValue: null,
radioButtonGroupValue: null,
inputNumberValue: null,
timePickerValue: null,
sliderValue: 0,
transferValue: null
},
generalOptions: [
'groode',
'veli good',
'emazing',
'lidiculous'
].map(v => ({
label: v,
value: v
})),
cascaderOptions: [
{
label: 'groode',
value: 'groode',
children: [
{
label: 'veli good',
value: 'veli good'
}
]
}
],
rules: {
inputValue: {
required: true,
trigger: ['blur', 'input'],
message: '请输入 inputValue'
},
textareaValue: {
required: true,
trigger: ['blur', 'input'],
message: '请输入 textareaValue'
},
selectValue: {
required: true,
trigger: ['blur', 'change'],
message: '请选择 selectValue'
},
multipleSelectValue: {
type: 'array',
required: true,
trigger: ['blur', 'change'],
message: '请选择 multipleSelectValue'
},
datetimeValue: {
type: 'number',
required: true,
trigger: ['blur', 'change'],
message: '请输入 datetimeValue'
},
nestedValue: {
path1: {
required: true,
trigger: ['blur', 'input'],
message: '请输入 nestedValue.path1'
},
path2: {
required: true,
trigger: ['blur', 'change'],
message: '请输入 nestedValue.path2'
}
},
checkboxGroupValue: {
type: 'array',
required: true,
trigger: 'change',
message: '请选择 checkboxGroupValue'
},
radioGroupValue: {
required: true,
trigger: 'change',
message: '请选择 radioGroupValue'
},
radioButtonGroupValue: {
required: true,
trigger: 'change',
message: '请选择 radioButtonGroupValue'
},
inputNumberValue: {
type: 'number',
required: true,
trigger: ['blur', 'change'],
message: '请输入 inputNumberValue'
},
timePickerValue: {
type: 'number',
required: true,
trigger: ['blur', 'change'],
message: '请输入 timePickerValue'
},
sliderValue: 0,
transferValue: {
type: 'array',
required: true,
trigger: 'change',
message: '请输入 transferValue'
}
}
}
},
methods: {
handleValidateButtonClick (e) {
e.preventDefault()
this.$refs.form.validate(errors => {
if (!errors) {
this.$NMessage.success('验证成功')
} else {
console.log(errors)
this.$NMessage.error('验证失败')
}
})
}
}
}
```

View File

@ -0,0 +1,200 @@
# Dark Debug 2
```html
<n-button @click="modalActive = !modalActive">Toggle</n-button>
<n-drawer
v-model="modalActive"
width="800"
>
<n-table :bordered="false" :single-line="false">
<n-thead>
<n-tr>
<n-th>Abandon</n-th>
<n-th>Abormal</n-th>
<n-th>Abolish</n-th>
<n-th>...</n-th>
<n-th>It's hard to learn words</n-th>
</n-tr>
</n-thead>
<n-tbody>
<n-tr>
<n-td>放弃</n-td>
<n-td>反常的</n-td>
<n-td>彻底废除</n-td>
<n-td>...</n-td>
<n-td>Damn it! I can't remember those words.</n-td>
</n-tr>
<n-tr>
<n-td>...</n-td>
<n-td>...</n-td>
<n-td>...</n-td>
<n-td>...</n-td>
<n-td>...</n-td>
</n-tr>
</n-tbody>
</n-table>
<n-list>
<template v-slot:header>
hhh
</template>
<template v-slot:footer>
fff
</template>
<n-list-item>
<template v-slot:prefix>
<n-button>Prefix</n-button>
</template>
<template v-slot:suffix>
<n-button>Suffix</n-button>
</template>
<n-thing title="Thing" title-extra="extra" description="description">
Biu<br>
Biu<br>
Biu<br>
</n-thing>
</n-list-item>
<n-list-item>
<n-thing title="Thing" title-extra="extra" description="description">
Biu<br>
Biu<br>
Biu<br>
</n-thing>
<template v-slot:suffix>
<n-button>Suffix</n-button>
</template>
</n-list-item>
</n-list>
<n-list bordered>
<template v-slot:header>
hhh
</template>
<template v-slot:footer>
fff
</template>
<n-list-item>
<template v-slot:prefix>
<n-button>Prefix</n-button>
</template>
<template v-slot:suffix>
<n-button>Suffix</n-button>
</template>
<n-thing title="Thing" title-extra="extra" description="description">
Biu<br>
Biu<br>
Biu<br>
</n-thing>
</n-list-item>
<n-list-item>
<n-thing title="Thing" title-extra="extra" description="description">
Biu<br>
Biu<br>
Biu<br>
</n-thing>
<template v-slot:suffix>
<n-button>Suffix</n-button>
</template>
</n-list-item>
</n-list>
<n-descriptions bordered>
<n-descriptions-item>
<template v-slot:label>
Breakfast
</template>
Apple
</n-descriptions-item>
<n-descriptions-item label="Lunch">
Apple
</n-descriptions-item>
<n-descriptions-item label="Supper">
Apple
</n-descriptions-item>
<n-descriptions-item label="Why Long">
Why <br> Long <br> Long <br> Long <br> Long <br> Long
</n-descriptions-item>
<n-descriptions-item label="Why Long">
Why <br> Long <br> Long <br> Long <br> Long <br> Long
</n-descriptions-item>
<n-descriptions-item label="Why Long">
Why <br> Long <br> Long <br> Long <br> Long <br> Long
</n-descriptions-item>
</n-descriptions>
<n-descriptions label-placement="left" bordered>
<n-descriptions-item>
<template v-slot:label>
Breakfast
</template>
Apple
</n-descriptions-item>
<n-descriptions-item label="Lunch">
Apple
</n-descriptions-item>
<n-descriptions-item label="Supper">
Apple
</n-descriptions-item>
<n-descriptions-item label="Why Long">
Why <br> Long <br> Long <br> Long <br> Long <br> Long
</n-descriptions-item>
<n-descriptions-item label="Why Long">
Why <br> Long <br> Long <br> Long <br> Long <br> Long
</n-descriptions-item>
<n-descriptions-item label="Why Long">
Why <br> Long <br> Long <br> Long <br> Long <br> Long
</n-descriptions-item>
</n-descriptions>
<n-descriptions>
<n-descriptions-item>
<template v-slot:label>
Breakfast
</template>
Apple
</n-descriptions-item>
<n-descriptions-item label="Lunch">
Apple
</n-descriptions-item>
<n-descriptions-item label="Supper">
Apple
</n-descriptions-item>
<n-descriptions-item label="Why Long">
Why <br> Long <br> Long <br> Long <br> Long <br> Long
</n-descriptions-item>
<n-descriptions-item label="Why Long">
Why <br> Long <br> Long <br> Long <br> Long <br> Long
</n-descriptions-item>
<n-descriptions-item label="Why Long">
Why <br> Long <br> Long <br> Long <br> Long <br> Long
</n-descriptions-item>
</n-descriptions>
<n-descriptions label-placement="left">
<n-descriptions-item>
<template v-slot:label>
Breakfast
</template>
Apple
</n-descriptions-item>
<n-descriptions-item label="Lunch">
Apple
</n-descriptions-item>
<n-descriptions-item label="Supper">
Apple
</n-descriptions-item>
<n-descriptions-item label="Why Long">
Why <br> Long <br> Long <br> Long <br> Long <br> Long
</n-descriptions-item>
<n-descriptions-item label="Why Long">
Why <br> Long <br> Long <br> Long <br> Long <br> Long
</n-descriptions-item>
<n-descriptions-item label="Why Long">
Why <br> Long <br> Long <br> Long <br> Long <br> Long
</n-descriptions-item>
</n-descriptions>
</n-drawer>
```
```js
export default {
data () {
return {
modalActive: false
}
},
}
```

View File

@ -0,0 +1,106 @@
# Dark Debug 3
```html
<n-button @click="modalActive = !modalActive">Toggle</n-button>
<n-drawer
v-model="modalActive"
width="800"
>
<n-data-table
ref="table"
:columns="columns"
:data="data"
:pagination="pagination"
:max-height="250"
:scroll-x="1800"
/>
</n-drawer>
```
```js
const columns = [
{
title: 'Name',
key: 'name',
width: 200,
fixed: 'left',
filter: 'default',
filterOptions: [
{
label: 'Edward King 0',
value: 'Edward King 0'
},
{
label: 'Edward King 1',
value: 'Edward King 1'
},
{
label: 'Edward King 2',
value: 'Edward King 2'
},
{
label: 'Edward King 3',
value: 'Edward King 3'
},
]
},
{
title: 'Age',
key: 'age',
width: 100
},
{
title: 'Row',
key: 'row',
render (h, row, index) {
return h('span', ['row ', index])
}
},
{
title: 'Row1',
key: 'row1',
render(h, row, index) {
return h('span', ['row ', index])
}
},
{
title: 'Row2',
key: 'row2',
render(h, row, index) {
return h('span', ['row ', index])
}
},
{
title: 'Address',
key: 'address',
width: 200,
fixed: 'right'
}
]
const data = Array.apply(null, { length: 46 }).map((_, index) => ({
key: index,
name: `Edward King ${index % 4}`,
age: 32 + index % 3,
address: `London, Park Lane no. ${index}`
}))
export default {
data() {
return {
modalActive: false,
data,
columns
}
},
computed: {
pagination () {
return { pageSize: 10 }
}
},
methods: {
sendMail(rowData) {
this.$NMessage.info('send mail to ' + rowData.name)
}
}
}
```

View File

@ -0,0 +1,26 @@
# Dark Debug 4
```html
<n-button @click="modalActive = !modalActive">Toggle</n-button>
<n-drawer
v-model="modalActive"
width="800"
>
<n-popover>
<template v-slot:activator>
<n-button style="margin:0;">
悬浮
</n-button>
</template>
<span>或许不想知道你的花园长得咋样</span>
</n-popover>
</n-drawer>
```
```js
export default {
data() {
return {
modalActive: false
}
}
}
```

View File

@ -4,6 +4,10 @@
```demo
basic
multiple
dark-1-debug
dark-2-debug
dark-3-debug
dark-4-debug
```
## V-model
|prop|event|

View File

@ -23,7 +23,26 @@ const columns = [
title: 'Name',
key: 'name',
width: 200,
fixed: 'left'
fixed: 'left',
filter: 'default',
filterOptions: [
{
label: 'Edward King 0',
value: 'Edward King 0'
},
{
label: 'Edward King 1',
value: 'Edward King 1'
},
{
label: 'Edward King 2',
value: 'Edward King 2'
},
{
label: 'Edward King 3',
value: 'Edward King 3'
},
]
},
{
title: 'Age',
@ -61,8 +80,8 @@ const columns = [
const data = Array.apply(null, { length: 46 }).map((_, index) => ({
key: index,
name: `Edward King ${index}`,
age: 32,
name: `Edward King ${index % 4}`,
age: 32 + index % 3,
address: `London, Park Lane no. ${index}`
}))

View File

@ -0,0 +1,28 @@
# Dark Debug 4
```html
<n-button @click="modalActive = !modalActive">Toggle</n-button>
<n-modal
title="Dark Modal Debug"
preset="card"
v-model="modalActive"
:overlay-style="{ marginTop: '24px', marginBottom: '24px', width: '800px' }"
>
<n-popover>
<template v-slot:activator>
<n-button style="margin:0;">
悬浮
</n-button>
</template>
<span>或许不想知道你的花园长得咋样</span>
</n-popover>
</n-modal>
```
```js
export default {
data() {
return {
modalActive: false
}
}
}
```

View File

@ -12,6 +12,7 @@ preset-confirm-slot
dark1-debug
dark2-debug
dark3-debug
dark4-debug
```
## V-model
|Prop|Event|

View File

@ -3,7 +3,7 @@
"zh-CN": {
"dark": "深色",
"light": "浅色",
"searchPlaceholder": "搜索组件",
"searchPlaceholder": "搜索",
"home": "首页",
"doc": "文档",
"common": "常规",
@ -13,7 +13,7 @@
"en-US": {
"dark": "Dark",
"light": "Light",
"searchPlaceholder": "Search Components",
"searchPlaceholder": "Search",
"home": "Home",
"doc": "Documentation",
"common": "Common",

View File

@ -1,5 +1,6 @@
<template>
<div
ref="contentContainer"
class="n-positioning-container"
>
<div ref="content" class="n-positioning-content">

View File

@ -1,5 +1,8 @@
<template>
<div class="n-positioning-container">
<div
ref="contentContainer"
class="n-positioning-container"
>
<div ref="content" class="n-positioning-content">
<transition name="n-cascader-menu-transition">
<n-base-select-menu

View File

@ -270,13 +270,16 @@ export default {
/**
* When async, filter won't be set, so data won't be filtered
*/
if (columnToFilter && typeof columnToFilter.filter === 'function') {
const filter = columnToFilter.filter === 'default'
? (filterOptionValue, row) => ~String(row[columnKey]).indexOf(String(filterOptionValue))
: columnToFilter.filter
if (columnToFilter && typeof filter === 'function') {
if (columnToFilter.filterMode === 'and') {
if (activeFilterOptionValues.some(filterOptionValue => !columnToFilter.filter(filterOptionValue, row))) {
if (activeFilterOptionValues.some(filterOptionValue => !filter(filterOptionValue, row))) {
return false
}
} else {
if (activeFilterOptionValues.some(filterOptionValue => columnToFilter.filter(filterOptionValue, row))) {
if (activeFilterOptionValues.some(filterOptionValue => filter(filterOptionValue, row))) {
return true
} else {
return false

View File

@ -37,7 +37,6 @@
:class="{
'n-data-table-th--filterable': isColumnFilterable(column),
'n-data-table-th--sortable': isColumnSortable(column),
'n-data-table-th--ellipsis': column.ellipsis,
[`n-data-table-th--fixed-${column.fixed}`]: column.fixed,
'n-data-table-th--shadow-after': leftActiveFixedColumn[column.key],
'n-data-table-th--shadow-before': rightActiveFixedColumn[column.key]
@ -51,12 +50,22 @@
:indeterminate="checkboxIndererminate"
@change="handleCheckboxInput(column)"
/>
<render
:render="typeof column.title === 'function'
? h => (column.title)(h, column, index)
: column.title
"
/>
<div v-if="column.ellipsis" class="n-data-table-th__ellipsis">
<render
:render="typeof column.title === 'function'
? h => (column.title)(h, column, index)
: column.title
"
/>
</div>
<template v-else>
<render
:render="typeof column.title === 'function'
? h => (column.title)(h, column, index)
: column.title
"
/>
</template>
<sort-button
v-if="isColumnSortable(column)"
:column="column"

View File

@ -0,0 +1,19 @@
<template>
<div><slot /></div>
</template>
<script>
export default {
name: 'NDrawerContent',
provide () {
return {
NDrawer: this
}
},
methods: {
getDetachTarget () {
return this.$el
}
}
}
</script>

View File

@ -15,7 +15,7 @@
:name="transitionName"
@after-leave="handleAfterLeave"
>
<div
<n-drawer-content
v-if="active"
class="n-drawer"
:style="{
@ -29,7 +29,7 @@
}"
>
<slot />
</div>
</n-drawer-content>
</transition>
</div>
</div>
@ -42,11 +42,13 @@ import withapp from '../../_mixins/withapp'
import themeable from '../../_mixins/themeable'
import zindexable from '../../_mixins/zindexable'
import formatLength from '../../_utils/css/formatLength'
import NDrawerContent from './DrawerContent'
export default {
name: 'NDrawer',
components: {
NBasePortal
NBasePortal,
NDrawerContent
},
mixins: [withapp, themeable, zindexable],
model: {

View File

@ -101,8 +101,10 @@ export default {
NConfirm,
NCard
},
provide: {
NModal: true
provide () {
return {
NModal: this
}
},
mixins: [themeable],
props: {
@ -212,6 +214,9 @@ export default {
},
handleScroll (e) {
this.scrollTop = e.target.scrollTop
},
getDetachTarget () {
return this.$refs.contentInner
}
}
}

View File

@ -222,6 +222,9 @@ export default {
getTrackedElement () {
return this.activator().$el
},
getAbsoluteOffsetContainer () {
return this.$refs.contentContainer
},
getZindexableContent () {
return this.$el
}

View File

@ -567,23 +567,6 @@ export default {
}
})
}
},
getPositionInAbsoluteMode (placement) {
let value = null
if (this.firstHandleActive) {
value = this.firstHandleValue
} else if (this.secondHandleValue) {
value = this.secondHandleActive
}
if (!(typeof value === 'number')) {
console.error('[naive-ui/slider]: try showing tooltip with no number value')
value = 0
}
return {
left: ((value - this.min) / (this.max - this.min) * 100) + '%',
bottom: 'calc(100% - 4px)',
transform: 'translateX(-50%)'
}
}
}
}

View File

@ -3,6 +3,9 @@ export default {
inject: {
NModal: {
default: null
},
NDrawer: {
default: null
}
},
props: {
@ -10,22 +13,30 @@ export default {
type: Function,
default: null
},
target: {
transferTarget: {
type: Function,
default: () => document.body
default: function () {
const NModal = this.NModal
if (NModal) {
return NModal.getDetachTarget()
}
const NDrawer = this.NDrawer
if (NDrawer) {
return NDrawer.getDetachTarget()
}
return document.body
}
}
},
mounted () {
if (this.onMounted) this.onMounted()
if (!this.transferable) return
if (this.$el.parentElement && !this.elementTransferred) {
this.$el.parentElement.removeChild(this.$el)
}
},
beforeDestroy () {
if (!this.transferable) return
const target = this.target()
if (target.contains(this.$el)) {
const target = this.transferTarget()
if (target && target.contains(this.$el)) {
target.removeChild(this.$el)
}
},
@ -34,16 +45,10 @@ export default {
elementTransferred: false
}
},
computed: {
transferable () {
return !this.NModal
}
},
methods: {
transferElement () {
if (!this.transferable) return
if (!this.elementTransferred) {
this.target().appendChild(this.$el)
this.transferTarget().appendChild(this.$el)
this.elementTransferred = true
}
}

View File

@ -6,21 +6,32 @@ import withapp from './withapp'
* Dependency:
* $refs.contentContainer
*
* @prop {HTMLElement} detachTarget determine where should $refs.contentContainer to be detached
* @prop {() => HTMLElement} detachTarget determine where should $refs.contentContainer to be detached
*/
export default {
mixins: [ withapp ],
inject: {
NModal: {
default: null
},
NDrawer: {
default: null
}
},
props: {
detachTarget: {
validator () {
return true
},
default: () => document.body
type: Function,
default: function () {
const NModal = this.NModal
if (NModal) {
return NModal.getDetachTarget()
}
const NDrawer = this.NDrawer
if (NDrawer) {
return NDrawer.getDetachTarget()
}
return document.body
}
},
detachable: {
type: Boolean,
@ -41,7 +52,6 @@ export default {
syntheticDetachable () {
const detachable = this.detachable
if (detachable !== null) return detachable
if (this.NModal) return false
return true
}
},
@ -52,20 +62,22 @@ export default {
}
},
methods: {
getDetachTarget () {
return this.detachTarget()
},
getDetachContent () {
return this.$refs.contentContainer
},
detachContent () {
this.contentContainer = this.$refs.contentContainer
this.$refs.contentContainer.parentNode.removeChild(this.$refs.contentContainer)
const content = this.getDetachContent()
if (content) {
content.parentNode.removeChild(content)
} else {
console.error('[naive-ui/detachable]: fail to detach content')
}
},
appendContent () {
this.detachTarget.append(this.$refs.contentContainer)
}
},
beforeMount () {
if (!this.detachTarget) {
console.error(
'[naive-ui/mixins/detachable]: %s has no `detach-target`.',
this.$options.name
)
this.getDetachTarget().append(this.$refs.contentContainer)
}
},
mounted () {
@ -79,8 +91,10 @@ export default {
},
beforeDestroy () {
if (this.syntheticDetachable) {
if (this.detachTarget.contains(this.$refs.contentContainer)) {
this.detachTarget.removeChild(this.$refs.contentContainer)
const content = this.getDetachContent()
const target = this.getDetachTarget()
if (content && target && target.contains(content)) {
this.getDetachTarget().removeChild(content)
}
}
}

View File

@ -27,13 +27,11 @@ if (!viewMeasurerInitialized && !document.getElementById('n-view-measurer')) {
document.body.appendChild(viewMeasurer)
}
function getActivatorEl (componentInstance) {
const refs = componentInstance.$refs
function getActivatorEl (refs) {
return refs.activator.$el || refs.activator
}
function getContentEl (componentInstance) {
const refs = componentInstance.$refs
function getContentEl (refs) {
return refs.content.$el || refs.content
}
@ -77,63 +75,6 @@ function getActivatorRect (manuallyPositioned, x, y, trackedElement, viewBoundin
}
}
function _getPositionInAbsoluteMode (placement) {
const position = {
top: null,
bottom: null,
left: null,
right: null,
transform: null
}
if (placement === 'bottom-start') {
position.top = '100%'
position.left = '0'
} else if (placement === 'bottom-end') {
position.top = '100%'
position.right = '0'
} else if (placement === 'top-start') {
position.bottom = '100%'
position.left = '0'
} else if (placement === 'top-end') {
position.bottom = '100%'
position.right = '0'
} else if (placement === 'right-start') {
position.top = '0'
position.left = '100%'
} else if (placement === 'right-end') {
position.bottom = '0'
position.left = '100%'
} else if (placement === 'left-start') {
position.top = '0'
position.right = '100%'
} else if (placement === 'left-end') {
position.bottom = '0'
position.right = '100%'
} else if (placement === 'top') {
position.bottom = '100%'
position.left = '50%'
position.transform = 'translateX(-50%)'
} else if (placement === 'right') {
position.left = '100%'
position.top = '50%'
position.transform = 'translateY(-50%)'
} else if (placement === 'bottom') {
position.top = '100%'
position.left = '50%'
position.transform = 'translateX(-50%)'
} else if (placement === 'left') {
position.right = '100%'
position.top = '50%'
position.transform = 'translateY(-50%)'
} else {
console.error(
'[naive-ui/mixins/placeable]: Placement %s is not supported.',
placement
)
}
return position
}
/**
* Make $refs.content trace $refs.activator, set $refs.contentInner width by the way
*
@ -150,6 +91,9 @@ export default {
inject: {
NModal: {
default: null
},
NDrawer: {
default: null
}
},
props: {
@ -210,6 +154,9 @@ export default {
if (this.NModal) {
return 'absolute'
}
if (this.NDrawer) {
return 'absolute'
}
return 'fixed'
},
positionModeisAbsolute () {
@ -250,84 +197,103 @@ export default {
_getTrackingElement () {
const refs = this.$refs
if (refs && refs.content) {
this.trackingElement = getContentEl(this)
} else if (this.getTrackingElement) {
this.trackingElement = this.getTrackingElement()
return getContentEl(refs)
}
const getTrackingElement = this.getTrackingElement
if (getTrackingElement) {
return getTrackingElement()
}
},
_getTrackedElement () {
const refs = this.$refs
if (refs && refs.activator) {
this.trackedElement = getActivatorEl(this)
} else if (this.getTrackedElement) {
this.trackedElement = this.getTrackedElement()
return getActivatorEl(refs)
}
const getTrackedElement = this.getTrackedElement
if (getTrackedElement) {
return getTrackedElement()
}
},
_getAbsoluteOffsetContainer () {
const getAbsoluteOffsetContainer = this.getAbsoluteOffsetContainer
if (getAbsoluteOffsetContainer) {
return getAbsoluteOffsetContainer()
} else {
const container = this.$refs.contentContainer
if (container) {
return container
} else {
console.error('[naive-ui/placeable]: absolute offset container not found')
}
}
},
setOffsetOfTrackingElement (position, transformOrigin) {
this.trackingElement.style.position = 'absolute'
this.trackingElement.style.top = position.top
this.trackingElement.style.left = position.left
this.trackingElement.style.right = position.right
this.trackingElement.style.bottom = position.bottom
this.trackingElement.style.transform = position.transform
this.trackingElement.style.transformOrigin = transformOrigin
this.trackingElement.setAttribute('n-suggested-transform-origin', transformOrigin)
const trackingElement = this._getTrackingElement()
trackingElement.style.position = 'absolute'
trackingElement.style.top = position.top
trackingElement.style.left = position.left
trackingElement.style.right = position.right
trackingElement.style.bottom = position.bottom
trackingElement.style.transform = position.transform
trackingElement.style.transformOrigin = transformOrigin
trackingElement.setAttribute('n-suggested-transform-origin', transformOrigin)
},
updatePosition (el, cb) {
updatePosition () {
if (!this.active) {
if (!this.keepPlaceableTracingWhenInactive) return
}
if (this.active) {
if (this.disablePlaceableTracingWhenActive) return
}
this._getTrackingElement()
this.trackingElement.style.position = 'absolute'
const trackingElement = this._getTrackingElement()
let trackedElement = null
trackingElement.style.position = 'absolute'
if (this.manuallyPositioned) {
if (!this.trackingElement) {
if (!trackingElement) {
console.error('[naive-ui/mixins/placeable]: trackingElement not found.')
return
}
} else {
this._getTrackedElement()
if (!this.trackedElement || !this.trackingElement) {
trackedElement = this._getTrackedElement()
if (!trackedElement || !trackingElement) {
console.error('[naive-ui/mixins/placeable]: trakedElement or trackingElement not found.')
return
}
}
// debugger
const viewBoundingRect = getViewBoundingRect()
const activatorRect = getActivatorRect(this.manuallyPositioned, this.x, this.y, this.trackedElement, viewBoundingRect)
const activatorRect = getActivatorRect(this.manuallyPositioned, this.x, this.y, trackedElement, viewBoundingRect)
const contentInner = getContentInner(this)
if (this.widthMode === 'activator' && contentInner) {
contentInner.style.minWidth = activatorRect.width + 'px'
}
const contentBoundingClientRect = {
width: this.trackingElement.offsetWidth,
height: this.trackingElement.offsetHeight
let adjustedPlacement = this.placement
let contentBoundingClientRect = null
if (this.flip) {
contentBoundingClientRect = {
width: trackingElement.offsetWidth,
height: trackingElement.offsetHeight
}
adjustedPlacement = getAdjustedPlacementOfTrackingElement(this.placement, activatorRect, contentBoundingClientRect, true)
}
const adjustedPlacement = getAdjustedPlacementOfTrackingElement(this.placement, activatorRect, contentBoundingClientRect, this.flip)
const suggestedTransformOrigin = getTransformOriginByPlacement(adjustedPlacement)
let offset = getPosition(adjustedPlacement, activatorRect, contentBoundingClientRect)
let offset = null
this.adjustedPlacement = adjustedPlacement
if (this.positionModeisAbsolute) {
const getPositionInAbsoluteMode = this.getPositionInAbsoluteMode
if (getPositionInAbsoluteMode) {
offset = getPositionInAbsoluteMode(adjustedPlacement)
} else {
offset = _getPositionInAbsoluteMode(adjustedPlacement)
const absoluteOffsetContainer = this._getAbsoluteOffsetContainer()
if (absoluteOffsetContainer) {
offset = getPosition(
adjustedPlacement,
absoluteOffsetContainer.getBoundingClientRect(),
activatorRect
)
}
} else {
offset = getPosition(adjustedPlacement, viewBoundingRect, activatorRect)
}
this.setOffsetOfTrackingElement(offset, suggestedTransformOrigin)
if (el && cb) {
cb(el, activatorRect, contentBoundingClientRect)
}
},
registerResizeListener () {
resizeDelegate.registerHandler(this.updatePosition)
},
registerScrollListeners () {
this._getTrackedElement()
let currentElement = getParentNode(this.trackedElement)
let currentElement = getParentNode(this._getTrackedElement())
while (true) {
currentElement = getScrollParent(currentElement)
if (currentElement === null) break

View File

@ -5,13 +5,6 @@ const oppositeDirection = {
right: 'left'
}
// const adjacentDirections = {
// top: ['left', 'right'],
// right: ['top', 'bottom'],
// bottom: ['left', 'right'],
// left: ['top', 'bottom']
// }
const lengthToCompare = {
top: 'height',
bottom: 'height',
@ -74,86 +67,71 @@ export function getAdjustedPlacementOfTrackingElement (placement, trackedRect, t
} else {
return adjustedPosition ? (direction + '-' + adjustedPosition) : direction
}
// else {
// const [direction1, direction2] = adjacentDirections[direction]
// let adjacentDirectionWithMoreSpace = direction1
// if (trackedRect[direction1] < trackedRect[direction2]) {
// adjacentDirectionWithMoreSpace = direction2
// }
// if (trackedRect[adjacentDirectionWithMoreSpace] < trackingRect[lengthToCompare[adjacentDirections]]) {
// /**
// * If no direction has required space, simply not flip tracking element to any side.
// */
// return placement
// }
// return adjacentDirectionWithMoreSpace
// }
}
export function getTransformOriginByPlacement (placement) {
return placementToTransformOrigin[placement] || null
}
export function getPosition (placement, trackedRect, trackingRect) {
const position = {
export function getPosition (placement, offsetContainerRect, activatorRect) {
const offset = {
top: null,
bottom: null,
left: null,
right: null,
top: null,
bottom: null
transform: null
}
switch (placement) {
case 'bottom-start':
position.top = trackedRect.top + trackedRect.height
position.left = trackedRect.left
break
case 'bottom':
position.top = trackedRect.top + trackedRect.height
position.left = trackedRect.left + trackedRect.width / 2 - trackingRect.width / 2
break
case 'bottom-end':
position.top = trackedRect.top + trackedRect.height
position.left = trackedRect.left + trackedRect.width - trackingRect.width
break
case 'top-start':
position.top = trackedRect.top - trackingRect.height
position.left = trackedRect.left
break
case 'top':
position.top = trackedRect.top - trackingRect.height
position.left = trackedRect.left + trackedRect.width / 2 - trackingRect.width / 2
break
case 'top-end':
position.top = trackedRect.top - trackingRect.height
position.left = trackedRect.left + trackedRect.width - trackingRect.width
break
case 'left-start':
position.top = trackedRect.top
position.left = trackedRect.left - trackingRect.width
break
case 'left':
position.top = trackedRect.top + trackedRect.height / 2 - trackingRect.height / 2
position.left = trackedRect.left - trackingRect.width
break
case 'left-end':
position.top = trackedRect.top + trackedRect.height - trackingRect.height
position.left = trackedRect.left - trackingRect.width
break
case 'right-start':
position.top = trackedRect.top
position.left = trackedRect.left + trackedRect.width
break
case 'right':
position.top = trackedRect.top + trackedRect.height / 2 - trackingRect.height / 2
position.left = trackedRect.left + trackedRect.width
break
case 'right-end':
position.top = trackedRect.top + trackedRect.height - trackingRect.height
position.left = trackedRect.left + trackedRect.width
break
if (placement === 'bottom-start') {
offset.top = (activatorRect.top - offsetContainerRect.top + activatorRect.height) + 'px'
offset.left = (activatorRect.left - offsetContainerRect.left) + 'px'
} else if (placement === 'bottom-end') {
offset.top = (activatorRect.top - offsetContainerRect.top + activatorRect.height) + 'px'
offset.left = (activatorRect.left - offsetContainerRect.left + activatorRect.width) + 'px'
offset.transform = 'translateX(-100%)'
} else if (placement === 'top-start') {
offset.top = (activatorRect.top - offsetContainerRect.top) + 'px'
offset.left = (activatorRect.left - offsetContainerRect.left) + 'px'
offset.transform = 'translateY(-100%)'
} else if (placement === 'top-end') {
offset.top = (activatorRect.top - offsetContainerRect.top) + 'px'
offset.left = (activatorRect.left - offsetContainerRect.left + activatorRect.width) + 'px'
offset.transform = 'translateY(-100%) translateX(-100%)'
} else if (placement === 'right-start') {
offset.top = (activatorRect.top - offsetContainerRect.top) + 'px'
offset.left = (activatorRect.left - offsetContainerRect.left + activatorRect.width) + 'px'
} else if (placement === 'right-end') {
offset.top = (activatorRect.top - offsetContainerRect.top + activatorRect.height) + 'px'
offset.left = (activatorRect.left - offsetContainerRect.left + activatorRect.width) + 'px'
offset.transform = 'translateY(-100%)'
} else if (placement === 'left-start') {
offset.top = (activatorRect.top - offsetContainerRect.top) + 'px'
offset.left = (activatorRect.left - offsetContainerRect.left) + 'px'
offset.transform = 'translateX(-100%)'
} else if (placement === 'left-end') {
offset.top = (activatorRect.top - offsetContainerRect.top + activatorRect.height) + 'px'
offset.left = (activatorRect.left - offsetContainerRect.left) + 'px'
offset.transform = 'translateX(-100%) translateY(-100%)'
} else if (placement === 'top') {
offset.top = (activatorRect.top - offsetContainerRect.top) + 'px'
offset.left = (activatorRect.left - offsetContainerRect.left + activatorRect.width / 2) + 'px'
offset.transform = 'translateX(-50%) translateY(-100%)'
} else if (placement === 'right') {
offset.top = (activatorRect.top - offsetContainerRect.top + activatorRect.height / 2) + 'px'
offset.left = (activatorRect.left - offsetContainerRect.left + activatorRect.width) + 'px'
offset.transform = 'translateY(-50%)'
} else if (placement === 'bottom') {
offset.top = (activatorRect.top - offsetContainerRect.top + activatorRect.height) + 'px'
offset.left = (activatorRect.left - offsetContainerRect.left + activatorRect.width / 2) + 'px'
offset.transform = 'translateX(-50%)'
} else if (placement === 'left') {
offset.top = (activatorRect.top - offsetContainerRect.top + activatorRect.height / 2) + 'px'
offset.left = (activatorRect.left - offsetContainerRect.left) + 'px'
offset.transform = 'translateX(-100%) translateY(-50%)'
} else {
console.error(
'[naive-ui/mixins/placeable]: Placement %s is not supported.',
placement
)
}
if (position.left !== null) position.left = position.left + 'px'
if (position.right !== null) position.right = position.right + 'px'
if (position.top !== null) position.top = position.top + 'px'
if (position.bottom !== null) position.bottom = position.bottom + 'px'
return position
return offset
}

View File

@ -164,7 +164,7 @@
@include m(filterable) {
padding-right: 36px;
}
@include m(ellipsis) {
@include e(ellipsis) {
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
@ -308,6 +308,7 @@
@include b(data-table-filter-button) {
@include once {
position: absolute;
z-index: auto;
right: 0;
width: 36px;
top: 0;
@ -317,6 +318,7 @@
@include e(icon-wrapper) {
@include once {
position: absolute;
z-index: auto;
right: 0;
left: 0;
top: 0;

View File

@ -69,6 +69,7 @@
}
@include b(modal-content-slot) {
position: relative;
&#{&}-transition-enter-active {
opacity: 1;
transition: opacity .3s cubic-bezier(.4, 0, .2, 1), transform .3s cubic-bezier(0.0, 0.0, 0.2, 1);

View File

@ -32,10 +32,11 @@ input {
@include b(modal-content) {
@include b(positioning-container) {
position: absolute;
z-index: 2;
left: 0;
right: 0;
top: 0;
bottom: 0;
}
}
@include b(drawer) {
@include b(positioning-container) {
position: absolute;
}
}

View File

@ -123,7 +123,7 @@ $default-theme: 'light';
}
@mixin as-modal-content {
&.#{$namespace}-modal-content {
&.#{$namespace}-modal-content, &.#{$namespace}-drawer {
@content;
}
}

View File

@ -1,4 +1,4 @@
$--n-font-family: 'FiraSans';
$--n-font-family: 'FiraSans', '-apple-system';
$--n-mono-font-family: 'FiraCode', monospace;
$--n-strong-weight: 600;

View File

@ -2,8 +2,8 @@
@mixin setup-light-data-table {
$--data-table-border-color: (
'default': mix-color-with-alpha-color($--n-card-background-color, $--n-table-header-overlay-background-color),
'modal': mix-color-with-alpha-color($--n-modal-background-color, $--n-table-header-overlay-background-color),
'default': mix-color-with-alpha-color($--n-card-background-color, $--n-divider-color),
'modal': mix-color-with-alpha-color($--n-modal-background-color, $--n-divider-color),
) !global;
$--data-table-header-text-color: $--n-primary-text-color !global;
$--data-table-fixed-column-box-shadow-color: rgba(0, 0, 0, .18) !global;

View File

@ -107,7 +107,7 @@
$--n-icon-color: $--overlay-text-4 !global;
$--n-rail-color: $--overlay-rail !global;
$--n-divider-color: $--neutral-divider !global;
$--n-divider-color: $--overlay-divider !global;
$--n-border-color: $--neutral-border !global;
$--n-scrollbar-background-color: $--overlay-scrollbar !global;