@featherds/dialog
Advanced tools
Comparing version 0.9.6 to 0.10.0
@@ -17,3 +17,4 @@ var __defProp = Object.defineProperty; | ||
}; | ||
import { openBlock, createElementBlock, createElementVNode, renderSlot, resolveComponent, normalizeClass, withModifiers, createVNode, toRef, ref, computed, watch, withDirectives, Transition, withCtx, vShow, toDisplayString, createCommentVNode, createBlock } from "vue"; | ||
import { defineComponent, ref, toRef, computed, nextTick, openBlock, createElementBlock, createElementVNode, renderSlot, resolveComponent, normalizeClass, withModifiers, createVNode, watch, withDirectives, Transition, withCtx, vShow, toDisplayString, createCommentVNode, createBlock } from "vue"; | ||
import { getElements, addLayer, removeLayer } from "@featherds/composables/modal/Layers"; | ||
import { FeatherIcon } from "@featherds/icon"; | ||
@@ -33,3 +34,3 @@ import Close from "@featherds/icon/navigation/Cancel"; | ||
}; | ||
const _sfc_main$2 = { | ||
const _sfc_main$2 = defineComponent({ | ||
props: { | ||
@@ -39,2 +40,5 @@ enable: { | ||
required: true | ||
}, | ||
layer: { | ||
type: Object | ||
} | ||
@@ -44,56 +48,32 @@ }, | ||
return { | ||
lastFocus: void 0, | ||
ignoreUtilFocusChanges: false, | ||
rendered: false | ||
}; | ||
}, | ||
computed: { | ||
ready() { | ||
return this.rendered && this.enable; | ||
} | ||
}, | ||
watch: { | ||
ready: { | ||
immediate: true, | ||
handler: "enableTrap" | ||
} | ||
}, | ||
methods: { | ||
enableTrap(nv) { | ||
if (nv) { | ||
this.addFocusTrapEvents(); | ||
} else { | ||
this.removeFocusTrapEvents(); | ||
setup(props) { | ||
const content = ref(); | ||
const ignoreUtilFocusChanges = ref(false); | ||
const layer = toRef(props, "layer"); | ||
const contentAndLayers = computed(() => { | ||
const result = []; | ||
if (content.value) { | ||
result.push(content.value); | ||
} | ||
}, | ||
addFocusTrapEvents() { | ||
document.addEventListener("blur", this.trapFocus, true); | ||
if (this.$refs.content) { | ||
this.attemptToFocusFirst(this.$refs.content); | ||
} else { | ||
this.$nextTick(() => { | ||
if (this.$refs.content) { | ||
this.attemptToFocusFirst(this.$refs.content); | ||
} | ||
}); | ||
if (layer.value) { | ||
result.push(...getElements(layer.value).value); | ||
} | ||
}, | ||
attemptToFocusFirst(el) { | ||
const firstFocus = el.querySelector("[first-focus]"); | ||
if (firstFocus && firstFocus.focus) { | ||
this.$nextTick(() => { | ||
firstFocus.focus(); | ||
}); | ||
} else { | ||
this.focusFirstDescendant(el); | ||
} | ||
}, | ||
removeFocusTrapEvents() { | ||
if (typeof document !== "undefined") | ||
document.removeEventListener("blur", this.trapFocus, true); | ||
}, | ||
focusFirstDescendant(element) { | ||
return result; | ||
}); | ||
const comparePositionInDOM = (a, b) => { | ||
let result = a.compareDocumentPosition ? a.compareDocumentPosition(b) : a.contains ? (a != b && a.contains(b) && 16) + (a != b && b.contains(a) && 8) + (a.sourceIndex >= 0 && b.sourceIndex >= 0 ? (a.sourceIndex < b.sourceIndex && 4) + (a.sourceIndex > b.sourceIndex && 2) : 1) + 0 : 0; | ||
if (result === 2) | ||
return "before"; | ||
if (result === 4) | ||
return "after"; | ||
if (result === 10 || result === 12) | ||
return "parent"; | ||
}; | ||
const focusFirstDescendant = (element) => { | ||
for (var i = 0; i < element.childNodes.length; i++) { | ||
var child = element.childNodes[i]; | ||
if (this.attemptFocus(child) || this.focusFirstDescendant(child)) { | ||
if (attemptFocus(child) || focusFirstDescendant(child)) { | ||
return true; | ||
@@ -103,7 +83,7 @@ } | ||
return false; | ||
}, | ||
focusLastDescendant(element) { | ||
}; | ||
const focusLastDescendant = (element) => { | ||
for (var i = element.childNodes.length - 1; i >= 0; i--) { | ||
var child = element.childNodes[i]; | ||
if (this.attemptFocus(child) || this.focusLastDescendant(child)) { | ||
if (attemptFocus(child) || focusLastDescendant(child)) { | ||
return true; | ||
@@ -113,49 +93,15 @@ } | ||
return false; | ||
}, | ||
attemptFocus(element) { | ||
if (!this.isFocusable(element)) { | ||
}; | ||
const attemptFocus = (element) => { | ||
if (!isFocusable(element)) { | ||
return false; | ||
} | ||
this.ignoreUtilFocusChanges = true; | ||
ignoreUtilFocusChanges.value = true; | ||
if (element.focus) { | ||
element.focus(); | ||
} | ||
this.ignoreUtilFocusChanges = false; | ||
ignoreUtilFocusChanges.value = false; | ||
return document.activeElement === element; | ||
}, | ||
trapFocus() { | ||
if (this.ignoreUtilFocusChanges) { | ||
return; | ||
} | ||
setTimeout(() => { | ||
var target = document.activeElement; | ||
if (this.$refs.content.contains(target)) { | ||
this.lastFocus = target; | ||
} else { | ||
let position = this.comparePositionInDOM(this.$refs.content, target); | ||
switch (position) { | ||
case "before": | ||
this.focusLastDescendant(this.$refs.content); | ||
break; | ||
case "after": | ||
this.focusFirstDescendant(this.$refs.content); | ||
break; | ||
case "parent": | ||
this.attemptToFocusFirst(this.$refs.content); | ||
break; | ||
} | ||
this.lastFocus = document.activeElement; | ||
} | ||
}, 0); | ||
}, | ||
comparePositionInDOM(a, b) { | ||
let result = a.compareDocumentPosition ? a.compareDocumentPosition(b) : a.contains ? (a != b && a.contains(b) && 16) + (a != b && b.contains(a) && 8) + (a.sourceIndex >= 0 && b.sourceIndex >= 0 ? (a.sourceIndex < b.sourceIndex && 4) + (a.sourceIndex > b.sourceIndex && 2) : 1) + 0 : 0; | ||
if (result === 2) | ||
return "before"; | ||
if (result === 4) | ||
return "after"; | ||
if (result === 10 || result === 12) | ||
return "parent"; | ||
}, | ||
isFocusable(element) { | ||
}; | ||
const isFocusable = (element) => { | ||
if (element.tabIndex > 0 || element.tabIndex === 0 && element.getAttribute("tabIndex") !== null) { | ||
@@ -180,4 +126,87 @@ return true; | ||
} | ||
}; | ||
const attemptToFocusFirst = (el) => { | ||
const firstFocus = el.querySelector("[first-focus]"); | ||
if (firstFocus && firstFocus.focus) { | ||
nextTick(() => { | ||
firstFocus.focus(); | ||
}); | ||
} else { | ||
focusFirstDescendant(el); | ||
} | ||
}; | ||
const lastFocus = ref(); | ||
const trapFocus = () => { | ||
if (ignoreUtilFocusChanges.value) { | ||
return; | ||
} | ||
setTimeout(() => { | ||
var target = document.activeElement; | ||
if (contentAndLayers.value.some((el) => el.contains(target))) { | ||
lastFocus.value = target; | ||
return; | ||
} else { | ||
let position = comparePositionInDOM(content.value, target); | ||
switch (position) { | ||
case "before": | ||
focusLastDescendant(content.value); | ||
break; | ||
case "after": | ||
focusFirstDescendant(content.value); | ||
break; | ||
case "parent": | ||
attemptToFocusFirst(content.value); | ||
break; | ||
} | ||
lastFocus.value = document.activeElement; | ||
} | ||
}, 0); | ||
}; | ||
return { | ||
trapFocus, | ||
content, | ||
ignoreUtilFocusChanges, | ||
attemptToFocusFirst, | ||
focusLastDescendant, | ||
focusFirstDescendant, | ||
isFocusable, | ||
lastFocus | ||
}; | ||
}, | ||
computed: { | ||
ready() { | ||
return this.rendered && this.enable; | ||
} | ||
}, | ||
watch: { | ||
ready: { | ||
immediate: true, | ||
handler: "enableTrap" | ||
} | ||
}, | ||
methods: { | ||
enableTrap(nv) { | ||
if (nv) { | ||
this.addFocusTrapEvents(); | ||
} else { | ||
this.removeFocusTrapEvents(); | ||
} | ||
}, | ||
addFocusTrapEvents() { | ||
document.addEventListener("blur", this.trapFocus, true); | ||
if (this.content) { | ||
this.attemptToFocusFirst(this.content); | ||
} else { | ||
this.$nextTick(() => { | ||
if (this.content) { | ||
this.attemptToFocusFirst(this.content); | ||
} | ||
}); | ||
} | ||
}, | ||
removeFocusTrapEvents() { | ||
if (typeof document !== "undefined") | ||
document.removeEventListener("blur", this.trapFocus, true); | ||
} | ||
}, | ||
mounted() { | ||
@@ -189,3 +218,3 @@ this.rendered = true; | ||
} | ||
}; | ||
}); | ||
const _hoisted_1$2 = /* @__PURE__ */ createElementVNode("div", { tabindex: "0" }, null, -1); | ||
@@ -245,3 +274,3 @@ const _hoisted_2$1 = { | ||
} | ||
var DialogClose = /* @__PURE__ */ _export_sfc(_sfc_main$1, [["render", _sfc_render$1], ["__scopeId", "data-v-3998156b"]]); | ||
var DialogClose = /* @__PURE__ */ _export_sfc(_sfc_main$1, [["render", _sfc_render$1], ["__scopeId", "data-v-36f0af5f"]]); | ||
var FeatherDialog_vue_vue_type_style_index_0_scoped_true_lang = ""; | ||
@@ -307,10 +336,13 @@ const LABELS = { | ||
const shown = ref(props.modelValue); | ||
const layer = ref(); | ||
watch(shown, (v) => { | ||
if (v) { | ||
context.emit("shown"); | ||
layer.value = addLayer(element, "modal"); | ||
} else { | ||
context.emit("hidden"); | ||
removeLayer(layer.value); | ||
} | ||
}); | ||
return __spreadValues({ close, hasFooter, headerId, element, shown }, labels); | ||
return __spreadValues({ close, hasFooter, headerId, element, shown, layer }, labels); | ||
}, | ||
@@ -341,4 +373,3 @@ components: { | ||
return withDirectives((openBlock(), createElementBlock("div", { | ||
class: normalizeClass(["feather-dialog", { relative: $props.relative }]), | ||
ref: "element" | ||
class: normalizeClass(["feather-dialog", { relative: $props.relative }]) | ||
}, [ | ||
@@ -361,3 +392,4 @@ createVNode(Transition, { name: "fade" }, { | ||
enable: $setup.shown && $props.modelValue, | ||
class: "trap" | ||
class: "trap", | ||
layer: $setup.layer | ||
}, { | ||
@@ -370,3 +402,4 @@ default: withCtx(() => [ | ||
"aria-labelledby": $setup.headerId, | ||
"data-ref-id": "feather-dialog" | ||
"data-ref-id": "feather-dialog", | ||
ref: "element" | ||
}, [ | ||
@@ -397,3 +430,3 @@ createElementVNode("div", _hoisted_3, [ | ||
_: 3 | ||
}, 8, ["enable"]), [ | ||
}, 8, ["enable", "layer"]), [ | ||
[vShow, $props.modelValue] | ||
@@ -408,3 +441,3 @@ ]) | ||
} | ||
var FeatherDialog = /* @__PURE__ */ _export_sfc(_sfc_main, [["render", _sfc_render], ["__scopeId", "data-v-16089cce"]]); | ||
var FeatherDialog = /* @__PURE__ */ _export_sfc(_sfc_main, [["render", _sfc_render], ["__scopeId", "data-v-9253336a"]]); | ||
export { DialogClose, FeatherDialog, FocusTrap }; |
{ | ||
"name": "@featherds/dialog", | ||
"version": "0.9.6", | ||
"version": "0.10.0", | ||
"publishConfig": { | ||
@@ -12,6 +12,6 @@ "access": "public" | ||
"dependencies": { | ||
"@featherds/composables": "^0.9.6", | ||
"@featherds/icon": "^0.9.6", | ||
"@featherds/styles": "^0.9.6", | ||
"@featherds/utils": "^0.9.6", | ||
"@featherds/composables": "^0.10.0", | ||
"@featherds/icon": "^0.10.0", | ||
"@featherds/styles": "^0.10.0", | ||
"@featherds/utils": "^0.10.0", | ||
"vue": "^3.1.0-0" | ||
@@ -24,3 +24,3 @@ }, | ||
"types": "./src/index.d.ts", | ||
"gitHead": "04a74207e8bfa8d39acc470365e30dd6c90b2e8e" | ||
"gitHead": "9871b17eaedcfc90174b78b21acb0cc06afd594c" | ||
} |
@@ -1,6 +0,4 @@ | ||
import { mount, config } from "@vue/test-utils"; | ||
import { mount } from "@vue/test-utils"; | ||
import FocusTrap from "./FocusTrap.vue"; | ||
config.renderStubDefaultSlot = true; | ||
const slots = { | ||
@@ -40,3 +38,3 @@ default: { | ||
wrapper = getWrapper({ props: { enable: false }, slots }); | ||
const focusFirst = jest.spyOn(wrapper.vm, "focusFirstDescendant"); | ||
const focusFirst = jest.spyOn(wrapper.vm, "attemptToFocusFirst"); | ||
await wrapper.setProps({ enable: true }); | ||
@@ -43,0 +41,0 @@ expect(focusFirst).toHaveBeenCalled(); |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
54890
931
+ Added@featherds/composables@0.10.17(transitive)
+ Added@featherds/icon@0.10.17(transitive)
+ Added@featherds/styles@0.10.17(transitive)
+ Added@featherds/utils@0.10.17(transitive)
- Removed@featherds/composables@0.9.6(transitive)
- Removed@featherds/icon@0.9.6(transitive)
- Removed@featherds/styles@0.9.6(transitive)
- Removed@featherds/utils@0.9.6(transitive)
Updated@featherds/icon@^0.10.0
Updated@featherds/styles@^0.10.0
Updated@featherds/utils@^0.10.0