提交 bc879172 编写于 作者: M Marcin Luczak

Release 1.0.0-beta1

上级 71f6f9af
MDB 5 Vue
Version: FREE 1.0.0-alpha4
Version: FREE 1.0.0-beta1
Documentation:
https://mdbootstrap.com/docs/b5/vue/
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
{
"name": "mdb-vue-ui-kit",
"version": "1.0.0-alpha4",
"version": "1.0.0-beta1",
"main": "js/mdb.umd.min.js",
"repository": "https://github.com/mdbootstrap/mdb-vue-ui-kit.git",
"author": "MDBootstrap",
......
......@@ -86,8 +86,10 @@ export default {
// controls if DropdownMenu has been mounted into DOM and its element
// can be targeted by the Popper setup function
const isMenuMounted = ref(false);
const setMenuMountedState = boolean => {
const dropdownMenu = ref(null);
const setMenuMountedState = (boolean, menuRef) => {
isMenuMounted.value = boolean;
dropdownMenu.value = menuRef;
};
provide("setMenuMountedState", setMenuMountedState);
......@@ -124,6 +126,7 @@ export default {
});
provide("isPopperActive", isPopperActive);
provide("externalTarget", props.target);
// ------------------- handleEscAndOutsideClick -------------------
// mimics toggling modelValue when user click outside the toggle button
......@@ -147,7 +150,7 @@ export default {
triggerEl.value = props.target
? document.querySelector(props.target)
: root.value.querySelector("[data-trigger]");
popperEl.value = root.value.querySelector("[data-popper]");
popperEl.value = dropdownMenu.value;
if (typeof props.align === "string") {
menuAlignClasses.value = `dropdown-menu-${props.align}`;
......@@ -174,7 +177,9 @@ export default {
modifiers: {
offset: {
offset: props.offset || "0"
}
},
preventOverflow: { enabled: true },
flip: { enabled: true }
}
};
......
<template>
<transition>
<transition v-if="!shouldTeleport">
<component
v-if="isMounted"
:is="tag"
......@@ -12,10 +12,25 @@
<slot />
</component>
</transition>
<teleport v-else :to="externalTarget">
<transition>
<component
v-if="isMounted"
:is="tag"
:class="className"
:style="staticStyle"
v-bind="attrs"
:data-popper="externalTarget"
ref="root"
>
<slot />
</component>
</transition>
</teleport>
</template>
<script>
import { computed, inject, ref, watch } from "vue";
import { computed, inject, onMounted, ref, watch } from "vue";
import { on, off } from "../../utils/MDBEventHandlers.js";
export default {
......@@ -86,8 +101,8 @@ export default {
cur => {
if (cur) {
setTimeout(() => {
setMenuMountedState(true);
}, 10);
setMenuMountedState(true, root.value);
}, 100);
} else if (!cur && isPopperActive) {
setInactive();
......@@ -123,6 +138,18 @@ export default {
}
});
const externalTarget = inject("externalTarget", false);
const shouldTeleport = ref(false);
onMounted(() => {
if (externalTarget) {
const target = document.body.querySelector(externalTarget);
if (target) {
shouldTeleport.value = true;
}
}
});
// ------------------- isPopperActive -------------------
// controls if DropdownMenu is visible for user or not
// controls show class and animation
......@@ -205,6 +232,8 @@ export default {
showClass,
className,
isMounted,
shouldTeleport,
externalTarget,
root,
attrs,
props
......
......@@ -23,7 +23,6 @@ import mdbClickOutside from "@/directives/free/mdbClickOutside.js";
export default {
name: "MDBDropdownToggle",
components: { MDBBtn },
inheritAttrs: false,
emits: ["toggle-dropdown"],
......@@ -33,10 +32,6 @@ export default {
type: String,
default: "button"
},
color: {
type: String,
default: "primary"
},
href: [String, null],
split: {
type: Boolean,
......@@ -52,15 +47,20 @@ export default {
btnClass.value,
"dropdown-toggle",
props.split && "dropdown-toggle-split",
props.size && `btn-${props.size}`
props.size && `btn-${props.size}`,
props.outline && `btn-outline-${props.outline}`
];
});
const btnClass = computed(() => {
if (props.tag === "button") {
const color = props.color ? `btn-${props.color}` : "";
return `btn ${color}`;
}
if (props.tag !== "button") return;
const color =
props.color && !props.outline
? `btn-${props.color}`
: props.outline
? ""
: "btn-primary";
return `btn ${color}`;
});
const expanded = ref(false);
......
......@@ -14,10 +14,10 @@
<label v-if="label" :class="labelClassName" :for="uid">
{{ label }}
</label>
<div :class="validFeedbackClassName">
<div v-if="validFeedback" :class="validFeedbackClassName">
{{ validFeedback }}
</div>
<div :class="invalidFeedbackClassName">
<div v-if="invalidFeedback" :class="invalidFeedbackClassName">
{{ invalidFeedback }}
</div>
</component>
......@@ -36,10 +36,10 @@
<label v-if="!wrap && label" :class="labelClassName" :for="uid">
{{ label }}
</label>
<div v-if="!wrap" :class="validFeedbackClassName">
<div v-if="!wrap && validFeedback" :class="validFeedbackClassName">
{{ validFeedback }}
</div>
<div v-if="!wrap" :class="invalidFeedbackClassName">
<div v-if="!wrap && invalidFeedback" :class="invalidFeedbackClassName">
{{ invalidFeedback }}
</div>
</template>
......@@ -64,7 +64,6 @@ export default {
validateOnChange: Boolean,
isValidated: Boolean,
isValid: Boolean,
isInvalid: Boolean,
validFeedback: String,
invalidFeedback: String,
tooltipFeedback: {
......@@ -101,10 +100,8 @@ export default {
return [
props.btnCheck ? "btn-check" : "form-check-input",
props.inputClass && props.inputClass,
((isInputValidated.value && isInputValid.value) || props.isValid) &&
"is-valid",
((isInputValidated.value && !isInputValid.value) || props.isInvalid) &&
"is-invalid"
isInputValidated.value && isInputValid.value && "is-valid",
isInputValidated.value && !isInputValid.value && "is-invalid"
];
});
const labelClassName = computed(() => {
......
<template>
<label v-if="label" :class="labelClassName" :for="uid">{{ label }}</label>
<input
type="file"
:class="inputClassName"
v-bind="attrs"
:id="uid"
@change="handleChange"
/>
<div v-if="validFeedback" :class="validFeedbackClassName">
{{ validFeedback }}
</div>
<div v-if="invalidFeedback" :class="invalidFeedbackClassName">
{{ invalidFeedback }}
</div>
</template>
<script>
import { computed, ref, watch } from "vue";
import { getUID } from "../../utils/getUID";
export default {
name: "MDBFile",
props: {
id: String,
inputClass: String,
invalidFeedback: String,
isInvalid: Boolean,
isValid: Boolean,
isValidated: Boolean,
label: String,
labelClass: String,
modelValue: {
type: [FileList, Array],
default: () => []
},
size: String,
tooltipFeedback: Boolean,
validFeedback: String,
validateOnChange: Boolean
},
emits: ["update:modelValue"],
setup(props, { attrs, emit }) {
const uid = props.id || getUID("MDBFile-");
const inputValue = ref(props.modelValue);
const inputClassName = computed(() => {
return [
"form-control",
props.size && `form-control-${props.size}`,
isInputValidated.value && isInputValid.value && "is-valid",
isInputValidated.value && !isInputValid.value && "is-invalid",
props.inputClass
];
});
const labelClassName = computed(() => {
return ["form-label", props.labelClass];
});
const validFeedbackClassName = computed(() => {
return props.tooltipFeedback ? "valid-tooltip" : "valid-feedback";
});
const invalidFeedbackClassName = computed(() => {
return props.tooltipFeedback ? "invalid-tooltip" : "invalid-feedback";
});
// Validation ------------------------
const isInputValidated = ref(props.isValidated);
const isInputValid = ref(props.isValid);
const handleValidation = event => {
isInputValid.value = event.target.files.length > 0;
isInputValidated.value = true;
};
const handleChange = event => {
inputValue.value = event.target.files;
emit("update:modelValue", inputValue.value);
if (props.validateOnChange) {
handleValidation(event);
}
};
watch(
() => props.modelValue,
value => (inputValue.value = value)
);
watch(
() => props.isValidated,
value => (isInputValidated.value = value)
);
watch(
() => props.isValid,
value => {
isInputValid.value = value;
}
);
return {
attrs,
uid,
inputClassName,
labelClassName,
validFeedbackClassName,
invalidFeedbackClassName,
handleChange
};
}
};
</script>
<template>
<component :is="tag" :class="wrapperClassName">
<input
v-if="!wrap"
:class="inputClassName"
v-bind="attrs"
:id="uid"
:value="inputValue"
@input="handleInput"
ref="inputRef"
/>
<label
v-if="label && !wrap"
ref="labelRef"
:class="labelClassName"
:for="uid"
>
{{ label }}
</label>
<slot v-if="!wrap" />
<div v-if="!wrap && validFeedback" :class="validFeedbackClassName">
{{ validFeedback }}
</div>
<div v-if="!wrap && customInvalidFeedback" :class="invalidFeedbackClassName">
{{ customInvalidFeedback }}
</div>
<component
v-if="wrap"
:is="tag"
:class="wrapperClassName"
:style="validationStyle"
>
<slot name="prepend" />
<input
:class="inputClassName"
v-bind="attrs"
......@@ -12,13 +42,13 @@
{{ label }}
</label>
<slot></slot>
<div :class="validFeedbackClassName">
<div v-if="validFeedback" :class="validFeedbackClassName">
{{ validFeedback }}
</div>
<div :class="invalidFeedbackClassName">
<div v-if="customInvalidFeedback" :class="invalidFeedbackClassName">
{{ customInvalidFeedback }}
</div>
<div class="form-notch">
<div v-if="formOutline" class="form-notch">
<div
class="form-notch-leading"
:style="{ width: `${notchLeadingWidth}px` }"
......@@ -59,12 +89,19 @@ export default {
default: true
},
wrapperClass: String,
inputGroup: {
type: [Boolean, String],
default: false
},
wrap: {
type: Boolean,
default: true
},
formText: String,
white: Boolean,
validationEvent: String,
isValidated: Boolean,
isValid: Boolean,
isInvalid: Boolean,
validFeedback: String,
invalidFeedback: String,
tooltipFeedback: {
......@@ -89,6 +126,7 @@ export default {
const wrapperClassName = computed(() => {
return [
props.formOutline && "form-outline",
inputGroupClassName.value,
props.white && "form-white",
props.wrapperClass
];
......@@ -99,16 +137,29 @@ export default {
props.size && `form-control-${props.size}`,
inputValue.value && "active",
showPlaceholder.value && "placeholder-active",
((isInputValidated.value && isInputValid.value) || props.isValid) &&
"is-valid",
((isInputValidated.value && !isInputValid.value) || props.isInvalid) &&
"is-invalid"
isInputValidated.value && isInputValid.value && "is-valid",
isInputValidated.value && !isInputValid.value && "is-invalid"
];
});
const labelClassName = computed(() => {
return ["form-label", props.labelClass];
});
const inputGroupClassName = computed(() => {
if (!props.inputGroup) {
return;
}
return props.inputGroup !== true
? `input-group input-group-${props.inputGroup}`
: "input-group";
});
const validationStyle = computed(() => {
return props.inputGroup && isInputValidated.value
? { marginBottom: "1rem" }
: "";
});
const validFeedbackClassName = computed(() => {
return props.tooltipFeedback ? "valid-tooltip" : "valid-feedback";
});
......@@ -205,6 +256,7 @@ export default {
labelClassName,
validFeedbackClassName,
invalidFeedbackClassName,
validationStyle,
customInvalidFeedback,
notchLeadingWidth,
notchMiddleWidth,
......
......@@ -14,10 +14,10 @@
<label v-if="label" :class="labelClassName" :for="uid">
{{ label }}
</label>
<div :class="validFeedbackClassName">
<div v-if="validFeedback" :class="validFeedbackClassName">
{{ validFeedback }}
</div>
<div :class="invalidFeedbackClassName">
<div v-if="invalidFeedback" :class="invalidFeedbackClassName">
{{ invalidFeedback }}
</div>
</component>
......@@ -36,10 +36,10 @@
<label v-if="!wrap && label" :class="labelClassName" :for="uid">
{{ label }}
</label>
<div v-if="!wrap" :class="validFeedbackClassName">
<div v-if="!wrap && validFeedback" :class="validFeedbackClassName">
{{ validFeedback }}
</div>
<div v-if="!wrap" :class="invalidFeedbackClassName">
<div v-if="!wrap && invalidFeedback" :class="invalidFeedbackClassName">
{{ invalidFeedback }}
</div>
</template>
......@@ -63,7 +63,6 @@ export default {
validateOnChange: Boolean,
isValidated: Boolean,
isValid: Boolean,
isInvalid: Boolean,
validFeedback: String,
invalidFeedback: String,
tooltipFeedback: {
......@@ -87,7 +86,7 @@ export default {
setup(props, { attrs, emit }) {
const inputRef = ref("inputRef");
const inputValue = ref(props.modelValue || false);
const uid = props.id || getUID("MDBCheckbox-");
const uid = props.id || getUID("MDBRadio-");
const wrapperClassName = computed(() => {
return [
......@@ -99,10 +98,8 @@ export default {
const inputClassName = computed(() => {
return [
props.btnCheck ? "btn-check" : "form-check-input",
((isInputValidated.value && isInputValid.value) || props.isValid) &&
"is-valid",
((isInputValidated.value && !isInputValid.value) || props.isInvalid) &&
"is-invalid"
isInputValidated.value && isInputValid.value && "is-valid",
isInputValidated.value && !isInputValid.value && "is-invalid"
];
});
const labelClassName = computed(() => {
......
<template>
<label v-if="label" :class="labelClassName" :for="uid">{{ label }}</label>
<component :is="tag" :class="wrapperClassName">
<input
type="range"
:class="inputClassName"
:id="uid"
:value="inputValue"
:min="minValue"
:max="maxValue"
v-bind="attrs"
@input="handleInput"
@mousedown="toggleThumb(true)"
@touchstart="toggleThumb(true)"
@mouseup="toggleThumb(false)"
@touchend="toggleThumb(false)"
/>
<span
v-if="thumb"
:class="thumbClassName"
:style="{ left: thumbLeftPosition }"
>
<span class="thumb-value">{{ inputValue }}</span>
</span>
</component>
</template>
<script>
import { computed, ref, watch, nextTick } from "vue";
import { getUID } from "@/components/utils/getUID";
export default {
name: "MDBRange",
props: {
id: String,
inputClass: String,
label: String,
labelClass: String,
max: {
type: Number,
default: 100
},
min: {
type: Number,
default: 0
},
modelValue: {
type: Number,
default: 50
},
tag: {
type: String,
default: "div"
},
thumb: {
type: Boolean,
default: true
},
thumbClass: String,
wrapperClass: String
},
emits: ["update:modelValue"],
setup(props, { attrs, emit }) {
const inputValue = ref(props.modelValue);
const minValue = ref(props.min);
const maxValue = ref(props.max);
const uid = props.id || getUID("MDBRange-");
const isThumbActive = ref(false);
const wrapperClassName = computed(() => {
return ["range", props.wrapperClass];
});
const inputClassName = computed(() => {
return ["form-range", props.inputClass];
});
const labelClassName = computed(() => {
return ["form-label", props.labelClass];
});
const thumbClassName = computed(() => {
return ["thumb", isThumbActive.value && "thumb-active", props.thumbClass];
});
const thumbLeftPosition = ref(0);
const handleInput = e => {
inputValue.value = parseFloat(e.target.value);
emit("update:modelValue", inputValue.value);
setThumbPosition();
};
const toggleThumb = isActive => {
isThumbActive.value = isActive;
};
const setThumbPosition = () => {
const left = parseFloat(
((inputValue.value - minValue.value) * 100) /
(maxValue.value - minValue.value)
);
thumbLeftPosition.value = `calc(${left}% + (${8 - left * 0.15}px))`;
};
nextTick(() => {
setThumbPosition();
});
watch(
() => props.modelValue,
value => {
inputValue.value = value;
setThumbPosition();
}
);
return {
inputValue,
minValue,
maxValue,
uid,
wrapperClassName,
inputClassName,
labelClassName,
thumbClassName,
thumbLeftPosition,
attrs,
props,
handleInput,
toggleThumb
};
}
};
</script>
<template>
<component :is="tag" :class="wrapperClassName">
<input
type="checkbox"
:class="inputClassName"
:id="uid"
v-bind="attrs"
:checked="inputValue"
@change="handleChange"
/>
<label :class="labelClassName" :for="uid">{{ label }}</label>
</component>
</template>
<script>
import { ref, computed, watch } from "vue";
import { getUID } from "../../utils/getUID";
export default {
name: "MDBSwitch",
props: {
id: String,
inputClass: String,
label: String,
labelClass: String,
modelValue: {
type: Boolean,
default: false
},
tag: {
type: String,
default: "div"
},
wrapperClass: String
},
emits: ["update:modelValue"],
setup(props, { attrs, emit }) {
const inputValue = ref(props.modelValue);
const uid = props.id || getUID("MDBSwitch-");
const wrapperClassName = computed(() => {
return ["form-check form-switch", props.wrapperClass];
});
const inputClassName = computed(() => {
return ["form-check-input", props.inputClass];
});
const labelClassName = computed(() => {
return ["form-check-label", props.labelClass];
});
function handleChange() {
inputValue.value = !inputValue.value;
emit("update:modelValue", inputValue.value);
}
watch(
() => props.modelValue,
value => (inputValue.value = value)
);
return {
inputValue,
uid,
wrapperClassName,
inputClassName,
labelClassName,
handleChange,
attrs
};
}
};
</script>
......@@ -38,7 +38,6 @@ export default {
type: [String, Array]
}
},
setup(props, { attrs }) {
const className = computed(() => {
return [
......
<template>
<keep-alive>
<div v-if="isVertical" :class="columnClassName">
<component :is="tag" :class="className" v-bind="attrs">
<slot />
</component>
</div>
<component v-else :is="tag" :class="className" v-bind="attrs">
<slot />
</component>
</keep-alive>
</template>
<script>
import { computed, inject } from "vue";
export default {
name: "MDBTabContent",
props: {
tag: {
type: String,
default: "div"
},
col: {
type: String,
default: "9"
},
contentClasses: String
},
setup(props, { attrs }) {
const className = computed(() => {
return ["tab-content", props.contentClasses && props.contentClasses];
});
const columnClassName = computed(() => {
return [`col-${props.col}`];
});
const isVertical = inject("isVertical", false);
return {
isVertical,
className,
columnClassName,
props,
attrs
};
}
};
</script>
<template>
<li v-if="href" class="nav-item" role="presentation">
<a
:class="className"
role="tab"
:aria-controls="controls"
:id="uid"
:href="href"
@click.prevent="handleClick(tabId)"
ref="item"
>
<slot />
</a>
</li>
<component
v-else
:is="tag"
:class="className"
role="tab"
:aria-controls="controls"
:id="uid"
@click.prevent="handleClick(tabId)"
ref="item"
>
<slot />
</component>
</template>
<script>
import { computed, inject, onMounted, ref, watchEffect } from "vue";
export default {
name: "MDBTabItem",
props: {
tag: {
type: String,
default: "a"
},
tabId: {
type: String,
required: true
},
href: String
},
setup(props, { attrs }) {
const item = ref("item");
const className = computed(() => {
return ["nav-link", isActive.value && "active"];
});
const uid = computed(() => {
return `tab-${props.tabId}`;
});
const controls = computed(() => {
return `${props.tabId}`;
});
const activeTabId = inject("activeTab", false);
const isActive = ref(
activeTabId &&
(activeTabId.value === props.tabId ||
(activeTabId && activeTabId === props.tabId))
);
const updateActiveTab = inject("updateActiveTab", false);
watchEffect(
() =>
(isActive.value =
activeTabId &&
(activeTabId.value === props.tabId ||
(activeTabId && activeTabId === props.tabId)))
);
const handleClick = () => {
updateActiveTab(item.value, props.tabId);
};
onMounted(() => {
if (isActive.value && updateActiveTab) {
updateActiveTab(item.value, props.tabId);
}
});
return {
item,
uid,
controls,
className,
handleClick,
props,
attrs
};
}
};
</script>
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
.btn {
margin: 0 0.12rem;
}
.input-group .btn {
margin: 0;
}
\ No newline at end of file
此差异已折叠。
.nav-tabs {
.nav-link {
cursor: pointer;
}
}
.nav-pills {
.nav-link {
cursor: pointer;
}
}
......@@ -9,3 +9,5 @@
@import './free/tooltips';
@import './free/popovers';
@import './free/validation';
@import './free/tabs';
@import './free/input-group';
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册