-
Notifications
You must be signed in to change notification settings - Fork 89
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support for Vue 3.0 #55
Comments
Any news on this? |
Anyone can help? |
? |
Try to use this as VueClamp.vue component file <script>
import { addListener, removeListener } from "resize-detector";
import { defineComponent } from "vue";
import { h } from "vue";
export default defineComponent({
name: "vue-clamp",
props: {
tag: {
type: String,
default: "div",
},
autoresize: {
type: Boolean,
default: false,
},
maxLines: Number,
maxHeight: [String, Number],
ellipsis: {
type: String,
default: "…",
},
location: {
type: String,
default: "end",
validator(value) {
return ["start", "middle", "end"].indexOf(value) !== -1;
},
},
expanded: Boolean,
},
data() {
return {
offset: null,
text: this.getText(),
localExpanded: !!this.expanded,
};
},
computed: {
clampedText() {
if (this.location === "start") {
return this.ellipsis + (this.text.slice(0, this.offset) || "").trim();
} else if (this.location === "middle") {
const split = Math.floor(this.offset / 2);
return (
(this.text.slice(0, split) || "").trim() +
this.ellipsis +
(this.text.slice(-split) || "").trim()
);
}
return (this.text.slice(0, this.offset) || "").trim() + this.ellipsis;
},
isClamped() {
if (!this.text) {
return false;
}
return this.offset !== this.text.length;
},
realText() {
return this.isClamped ? this.clampedText : this.text;
},
realMaxHeight() {
if (this.localExpanded) {
return null;
}
const { maxHeight } = this;
if (!maxHeight) {
return null;
}
return typeof maxHeight === "number" ? `${maxHeight}px` : maxHeight;
},
},
watch: {
expanded(val) {
this.localExpanded = val;
},
localExpanded(val) {
if (val) {
this.clampAt(this.text.length);
} else {
this.update();
}
if (this.expanded !== val) {
this.$emit("update:expanded", val);
}
},
isClamped: {
handler(val) {
this.$nextTick(() => this.$emit("clampchange", val));
},
immediate: true,
},
},
mounted() {
this.init();
this.$watch(
(vm) => [vm.maxLines, vm.maxHeight, vm.ellipsis, vm.isClamped].join(),
this.update
);
this.$watch((vm) => [vm.tag, vm.text, vm.autoresize].join(), this.init);
},
updated() {
this.text = this.getText();
this.applyChange();
},
beforeUnmount() {
this.cleanUp();
},
methods: {
init() {
const contents = this.$slots.default();
if (!contents) {
return;
}
this.offset = this.text.length;
this.cleanUp();
if (this.autoresize) {
addListener(this.$el, this.update);
this.unregisterResizeCallback = () => {
removeListener(this.$el, this.update);
};
}
this.update();
},
update() {
if (this.localExpanded) {
return;
}
this.applyChange();
if (this.isOverflow() || this.isClamped) {
this.search();
}
},
expand() {
this.localExpanded = true;
},
collapse() {
this.localExpanded = false;
},
toggle() {
this.localExpanded = !this.localExpanded;
},
getLines() {
return Object.keys(
Array.prototype.slice
.call(this.$refs.content.getClientRects())
.reduce((prev, { top, bottom }) => {
const key = `${top}/${bottom}`;
if (!prev[key]) {
prev[key] = true;
}
return prev;
}, {})
).length;
},
isOverflow() {
if (!this.maxLines && !this.maxHeight) {
return false;
}
if (this.maxLines) {
if (this.getLines() > this.maxLines) {
return true;
}
}
if (this.maxHeight) {
if (this.$el.scrollHeight > this.$el.offsetHeight) {
return true;
}
}
return false;
},
getText() {
// Look for the first non-empty text node
const [content] = (this.$slots.default() || []).filter(
(node) => !node.tag && !node.isComment
);
return content ? content.children : "";
},
moveEdge(steps) {
this.clampAt(this.offset + steps);
},
clampAt(offset) {
this.offset = offset;
this.applyChange();
},
applyChange() {
this.$refs.text.textContent = this.realText;
},
stepToFit() {
this.fill();
this.clamp();
},
fill() {
while (
(!this.isOverflow() || this.getLines() < 2) &&
this.offset < this.text.length
) {
this.moveEdge(1);
}
},
clamp() {
while (this.isOverflow() && this.getLines() > 1 && this.offset > 0) {
this.moveEdge(-1);
}
},
search(...range) {
const [from = 0, to = this.offset] = range;
if (to - from <= 3) {
this.stepToFit();
return;
}
const target = Math.floor((to + from) / 2);
this.clampAt(target);
if (this.isOverflow()) {
this.search(from, target);
} else {
this.search(target, to);
}
},
cleanUp() {
if (this.unregisterResizeCallback) {
this.unregisterResizeCallback();
}
},
},
render() {
const contents = [
h(
"span",
{
ref: "text",
attrs: {
"aria-label": this.text?.trim(),
},
},
this.realText
),
];
const { expand, collapse, toggle } = this;
const scope = {
expand,
collapse,
toggle,
clamped: this.isClamped,
expanded: this.localExpanded,
};
const before = this.$slots.before
? this.$slots.before(scope)
: this.$slots.before;
if (before) {
contents.unshift(...(Array.isArray(before) ? before : [before]));
}
const after = this.$slots.after
? this.$slots.after(scope)
: this.$slots.after;
if (after) {
contents.push(...(Array.isArray(after) ? after : [after]));
}
const lines = [
h(
"span",
{
style: {
boxShadow: "transparent 0 0",
},
ref: "content",
},
contents
),
];
return h(
this.tag,
{
style: {
maxHeight: this.realMaxHeight,
overflow: "hidden",
},
},
lines
);
},
});
</script>
|
@eladcandroid Thx for your code since we would like to https://gist.github.com/voratham/4f77d49182a82d6ea84fb244fde857e1 |
When the text change the clamp has been not updated. So i come back to use the scrip from eladcandroid and it works. Thank you both, |
vue3 version here 👉 https://github.com/sherwinshen/vue3-text-clamp |
When can vue3 be supported |
Thank you~ |
Is there any plan to support for vue 3.0?
The text was updated successfully, but these errors were encountered: