Skip to content
Permalink
Browse files
feat(b-form-file): improved drag and drop handling (closes #3673) (#5727
)

* feat(b-form-file): improved drag and drop handling

* Update form-file.js

* Update form-file.js

* Further implementation

* Update form-file.spec.js

* add global `stopEvent()` util

* Improve tests

* Update form-file.spec.js

* Update form-file.js

* Update form-file.js

* Update form-file.js

* Update form-file.js

* Update common-props.json

* Update form-file.js

* Update package.json

* Update form-file.js

* Update README.md

* Update form-file.js

* Update index.d.ts

* Update README.md

* Keep current event prevent behavior

* Update form-spinbutton.js

Co-authored-by: Hiws <hiws@live.dk>
  • Loading branch information
jacobmllr95 and Hiws committed Sep 10, 2020
1 parent 7e18c61 commit 3b12a73d3856a0b14f630d45d236570698b75e50
Show file tree
Hide file tree
Showing 51 changed files with 1,106 additions and 552 deletions.
@@ -22,6 +22,7 @@ module.exports = {
ignoreRestSiblings: false
}
],
'object-shorthand': ['error', 'properties'],
'spaced-comment': 'off', // needed to ignore `/*#__PURE__*/` comments
'vue/html-self-closing': [
'error',
@@ -1,6 +1,6 @@
{
"id": {
"description": "Used to set the 'id' attribute on the rendered content, and used as the base to generate any additional element IDs as needed"
"description": "Used to set the `id` attribute on the rendered content, and used as the base to generate any additional element IDs as needed"
},
"variant": {
"description": "Applies one of the Bootstrap theme color variants to the component"
@@ -114,22 +114,22 @@
"description": "Set the size of the component's appearance. 'sm', 'md' (default), or 'lg'"
},
"required": {
"description": "Adds the 'required' attribute to the form control"
"description": "Adds the `required` attribute to the form control"
},
"form": {
"description": "ID of the form that the form control belongs to. Sets the 'form' attribute on the control"
"description": "ID of the form that the form control belongs to. Sets the `form` attribute on the control"
},
"name": {
"description": "Sets the value of the 'name' attribute on the form control"
"description": "Sets the value of the `name` attribute on the form control"
},
"placeholder": {
"description": "Sets the 'placeholder' attribute value on the form control"
"description": "Sets the `placeholder` attribute value on the form control"
},
"disabled": {
"description": "When set to 'true', disables the component's functionality and places it in a disabled state"
},
"readonly": {
"description": "Sets the 'readonly' attribute on the form control"
"description": "Sets the `readonly` attribute on the form control"
},
"plaintext": {
"description": "Set the form control as readonly and renders the control to look like plain text (no borders)"
@@ -138,25 +138,25 @@
"description": "Sets the 'autocomplete' attribute value on the form control"
},
"autofocus": {
"description": "When set to 'true', attempts to auto-focus the control when it is mounted, or re-activated when in a keep-alive. Does not set the 'autofocus' attribute on the control"
"description": "When set to `true`, attempts to auto-focus the control when it is mounted, or re-activated when in a keep-alive. Does not set the `autofocus` attribute on the control"
},
"state": {
"description": "Controls the validation state appearance of the component. 'true' for valid, 'false' for invalid', or 'null' for no validation state"
"description": "Controls the validation state appearance of the component. `true` for valid, `false` for invalid, or `null` for no validation state"
},
"options": {
"description": "Array of items to render in the component"
},
"valueField": {
"description": "Field name in the 'options' array that should be used for the value"
"description": "Field name in the `options` array that should be used for the value"
},
"textField": {
"description": "Field name in the 'options' array that should be used for the text label"
"description": "Field name in the `options` array that should be used for the text label"
},
"htmlField": {
"description": "Field name in the 'options' array that should be used for the html label instead of text field. Use with caution."
"description": "Field name in the `options` array that should be used for the html label instead of text field. Use with caution"
},
"disabledField": {
"description": "Field name in the 'options' array that should be used for the disabled state"
"description": "Field name in the `options` array that should be used for the disabled state"
},
"plain": {
"description": "Render the form control in plain mode, rather than custom styled mode"
@@ -165,52 +165,52 @@
"description": "Renders the content of the component in-place in the DOM, rather than portalling it to be appended to the body element"
},
"src": {
"description": "URL to set for the 'src' attribute"
"description": "URL to set for the `src` attribute"
},
"alt": {
"description": "Value to set for the 'alt' attribute"
"description": "Value to set for the `alt` attribute"
},
"role": {
"description": "Sets the ARIA attribute 'role' to a specific value"
"description": "Sets the ARIA attribute `role` to a specific value"
},
"ariaRole": {
"description": "Sets the ARIA attribute 'role' to a specific value"
"description": "Sets the ARIA attribute `role` to a specific value"
},
"ariaLabel": {
"description": "Sets the value of 'aria-label' attribute on the rendered element"
"description": "Sets the value of `aria-label` attribute on the rendered element"
},
"ariaLabelledby": {
"description": "The ID of the element that provides a label for this component. Used as the value for the 'aria-labelledby' attribute"
"description": "The ID of the element that provides a label for this component. Used as the value for the `aria-labelledby` attribute"
},
"ariaDescribedby": {
"description": "The ID of the element that provides additional context for this component. Used as the value for the 'aria-describedby' attribute"
"description": "The ID of the element that provides additional context for this component. Used as the value for the `aria-describedby` attribute"
},
"ariaLive": {
"description": "When the rendered element is an aria-live region (for screen reader users), set to either 'polite' or 'assertive'"
"description": "When the rendered element is an `aria-live` region (for screen reader users), set to either 'polite' or 'assertive'"
},
"fade": {
"description": "When set to 'true', enables the fade animation/transition on the component"
"description": "When set to `true`, enables the fade animation/transition on the component"
},
"noFade": {
"description": "When set to 'true', disables the fade animation/transition on the component"
"description": "When set to `true`, disables the fade animation/transition on the component"
},
"active": {
"description": "When set to 'true', places the component in the active state with active styling"
"description": "When set to `true`, places the component in the active state with active styling"
},
"href": {
"description": "<b-link> prop: Denotes the target URL of the link for standard a links"
},
"rel": {
"description": "<b-link> prop: Sets the 'rel' attribute on the rendered link"
"description": "<b-link> prop: Sets the `rel` attribute on the rendered link"
},
"target": {
"description": "<b-link> prop: Sets the 'target' attribute on the rendered link"
"description": "<b-link> prop: Sets the `target` attribute on the rendered link"
},
"to": {
"description": "<router-link> prop: Denotes the target route of the link. When clicked, the value of the to prop will be passed to router.push() internally, so the value can be either a string or a Location descriptor object"
"description": "<router-link> prop: Denotes the target route of the link. When clicked, the value of the to prop will be passed to `router.push()` internally, so the value can be either a string or a Location descriptor object"
},
"replace": {
"description": "<router-link> prop: Setting the replace prop will call 'router.replace()' instead of 'router.push()' when clicked, so the navigation will not leave a history record"
"description": "<router-link> prop: Setting the replace prop will call `router.replace()` instead of `router.push()` when clicked, so the navigation will not leave a history record"
},
"append": {
"description": "<router-link> prop: Setting append prop always appends the relative path to the current path"
@@ -225,17 +225,17 @@
"description": "<router-link> prop: Configure the active CSS class applied when the link is active with exact match. Typically you will want to set this to class name 'active'"
},
"routerTag": {
"description": "<router-link> prop: Specify which tag to render, and it will still listen to click events for navigation. 'router-tag' translates to the tag prop on the final rendered router-link. Typically you should use the default value"
"description": "<router-link> prop: Specify which tag to render, and it will still listen to click events for navigation. `router-tag` translates to the tag prop on the final rendered `<router-link>`. Typically you should use the default value"
},
"event": {
"description": "<router-link> prop: Specify the event that triggers the link. In most cases you should leave this as the default"
},
"prefetch": {
"description": "<nuxt-link> prop: To improve the responsiveness of your Nuxt.js applications, when the link will be displayed within the viewport, Nuxt.js will automatically prefetch the code splitted page. Setting 'prefetch' to 'true' or 'false' will overwrite the default value of 'router.prefetchLinks'",
"description": "<nuxt-link> prop: To improve the responsiveness of your Nuxt.js applications, when the link will be displayed within the viewport, Nuxt.js will automatically prefetch the code splitted page. Setting `prefetch` to `true` or `false` will overwrite the default value of `router.prefetchLinks`",
"version": "2.15.0"
},
"noPrefetch": {
"description": "<nuxt-link> prop: To improve the responsiveness of your Nuxt.js applications, when the link will be displayed within the viewport, Nuxt.js will automatically prefetch the code splitted page. Setting 'no-prefetch' will disabled this feature for the specific link"
"description": "<nuxt-link> prop: To improve the responsiveness of your Nuxt.js applications, when the link will be displayed within the viewport, Nuxt.js will automatically prefetch the code splitted page. Setting `no-prefetch` will disabled this feature for the specific link"
},
"routerComponentName": {
"description": "<b-link> prop: BootstrapVue auto detects between `<router-link>` and `<nuxt-link>`. In cases where you want to use a 3rd party link component based on `<router-link>`, set this prop to the component name. e.g. set it to 'g-link' if you are using Gridsome (note only `<router-link>` specific props are passed to the component)",
@@ -229,7 +229,7 @@ export default {
const fallbackUrl = slug ? `https://opencollective.com/${slug}` : null
// Return the normalized result
return {
slug: slug,
slug,
name: entry.fromAccount.name,
// type: 'ORGANIZATION', 'INDIVIDUAL'
type: entry.fromAccount.type,
@@ -245,10 +245,10 @@ export default {
status: entry.status,
// For recurring donations, this is the installment amount
// For one time donations, this is the donation amount (most recent)
amount: amount,
amount,
// For recurring donations, this is the total amount donated
// For users that donate multiple times, this will be the total of all one time donations
totalAmount: totalAmount,
totalAmount,
// For recurring donations, this is how often the donation is received
frequency: entry.frequency,
// We now have sponsor tiers, but some appear as
@@ -524,37 +524,37 @@ export default {
oWarn = window.console.warn
oError = window.console.error
oClear = window.console.clear
} catch (e) {}
} catch {}
return {
info: function() {
info() {
try {
logger('info', ...arguments)
oInfo.apply(oConsole, arguments)
} catch (e) {}
} catch {}
},
log: function() {
log() {
try {
logger('info', ...arguments)
oLog.apply(oConsole, arguments)
} catch (e) {}
} catch {}
},
warn: function() {
warn() {
try {
logger('warning', ...arguments)
oWarn.apply(oConsole, arguments)
} catch (e) {}
} catch {}
},
error: function() {
error() {
try {
logger('danger', ...arguments)
oError.apply(oConsole, arguments)
} catch (e) {}
} catch {}
},
clear: function() {
clear() {
try {
clear()
oClear.apply(oConsole)
} catch (e) {}
} catch {}
}
}
}
@@ -651,10 +651,10 @@ export default {
vm.$destroy()
removeNode(vm.$el)
vm.$el.innerHTML = ''
} catch (err) {}
} catch {}
try {
parent.$destroy()
} catch (err) {}
} catch {}
}
this.playVM = vm = null
this.$refs.result.innerHTML = ''
@@ -151,7 +151,6 @@
"nuxt": "^2.14.4",
"postcss-cli": "^7.1.2",
"prettier": "1.14.3",
"regenerator-runtime": "^0.13.7",
"require-context": "^1.1.0",
"rollup": "^2.26.11",
"rollup-plugin-babel": "^4.4.0",
@@ -176,7 +176,7 @@ const processFile = (file, data) =>
// Remove leading/trailing whitespace
.trim()
// Add to the iconsData object
data.icons[componentName] = { name: name, content: content }
data.icons[componentName] = { name, content }
data.componentNames.push(componentName)
// Resolve
resolve()
@@ -1,5 +1,4 @@
// Creates a web-types.json, tags.json and attributes.json files and places them in /dist
require('regenerator-runtime/runtime')
const path = require('path')
const fs = require('fs')
const requireContext = require('require-context')
@@ -166,7 +165,7 @@ const processComponentMeta = (meta, groupRef, groupDescription, docUrl) => {
name: propName,
value: {
kind: 'expression',
type: type
type
},
default: computePropDefault($prop),
'doc-url': docUrl
@@ -212,7 +211,7 @@ const processComponentMeta = (meta, groupRef, groupDescription, docUrl) => {
}
if (Array.isArray(eventObj.args)) {
event.arguments = eventObj.args.map((arg, index) => {
arg = typeof arg === 'object' ? arg : { arg: arg }
arg = typeof arg === 'object' ? arg : { arg }
const name = arg.arg || (arg.type ? computePropType(arg) : undefined) || 'arg' + index
const argument = {
name: name.charAt(0).toLowerCase() + name.slice(1),
@@ -448,7 +447,7 @@ try {
const type = (attrObj.value || { type: 'any' }).type
veturAttributes[`${tag}/${kebabCase(attrObj.name)}`] = {
description: attrObj.description || `One of: ${type.split('|').join(' or ')}`,
type: type
type
}
})
})
@@ -1,6 +1,7 @@
import Vue from '../../utils/vue'
import KeyCodes from '../../utils/key-codes'
import { attemptFocus, contains, isVisible, selectAll } from '../../utils/dom'
import { stopEvent } from '../../utils/events'
import normalizeSlotMixin from '../../mixins/normalize-slot'

// --- Constants ---
@@ -13,13 +14,6 @@ const ITEM_SELECTOR = [
'input[type="radio"]:not(.disabled)'
].join(',')

// --- Utility methods ---

const stopEvent = evt => {
evt.preventDefault()
evt.stopPropagation()
}

// --- Main component ---

// @vue/component
@@ -1,6 +1,7 @@
import Vue from '../../utils/vue'
import { mergeData } from 'vue-functional-data-merge'
import { getComponentConfig } from '../../utils/config'
import { stopEvent } from '../../utils/events'
import { isEvent } from '../../utils/inspect'
import { hasNormalizedSlot, normalizeSlot } from '../../utils/normalize-slot'

@@ -49,8 +50,7 @@ export const BButtonClose = /*#__PURE__*/ Vue.extend({
// Ensure click on button HTML content is also disabled
/* istanbul ignore if: bug in JSDOM still emits click on inner element */
if (props.disabled && isEvent(evt)) {
evt.stopPropagation()
evt.preventDefault()
stopEvent(evt)
}
}
}
@@ -4,6 +4,7 @@ import KeyCodes from '../../utils/key-codes'
import { concat } from '../../utils/array'
import { getComponentConfig } from '../../utils/config'
import { addClass, isTag, removeClass } from '../../utils/dom'
import { stopEvent } from '../../utils/events'
import { isBoolean, isEvent, isFunction } from '../../utils/inspect'
import { omit } from '../../utils/object'
import { pluckProps } from '../../utils/props'
@@ -163,15 +164,14 @@ export const BButton = /*#__PURE__*/ Vue.extend({
// Add SPACE handler for `href="#"` and ENTER handler for non-standard tags
if (keyCode === KeyCodes.SPACE || (keyCode === KeyCodes.ENTER && nonStandardTag)) {
const target = evt.currentTarget || evt.target
evt.preventDefault()
stopEvent(evt, { propagation: false })
target.click()
}
},
click(evt) {
/* istanbul ignore if: blink/button disabled should handle this */
if (props.disabled && isEvent(evt)) {
evt.stopPropagation()
evt.preventDefault()
stopEvent(evt)
} else if (toggle && listeners && listeners['update:pressed']) {
// Send `.sync` updates to any "pressed" prop (if `.sync` listeners)
// `concat()` will normalize the value to an array without

0 comments on commit 3b12a73

Please sign in to comment.