mirror of
https://github.com/vuejs/vue-cli.git
synced 2026-04-19 18:51:46 -05:00
* feat: basic fonctionality, welcome and kill port widgets * fix: contrast improvements * feat: plugin/dep/vulnerability widgets design * fix: widget add/remove animation * feat: run task widget * feat: news + wip resizing * feat: nuxt * chore: removed widget example * fix: visual polish for widget transform * feat(widget): overlap detection * fix: news default/max size * feat(dashboard): sidepane transition * chore: dev api server port * fix(widget): configure tooltip * refactor(widget): generic Movable mixin * refactor(widget): resizable mixin * feat(widget): resize transition * feat(widget): resize improvements * refactor(widget): zoom factor * refactor(widget): OnGrid mixin * refactor(widget): resize handler style moved to global * chore: remove console.log * refactor: files structure * feat: improved design and layout * fix: content background vars * fix: status bar / view nav z-indexes * fix: webpack dashboard grid gap * feat(news feed): handle errors * fix(card): dimmed box shadow * fix: view nav & status bar z-index * fix: remove (wip) * feat(widget): style tweaks * feat(widget): details pane (wip) * feat: news feed widget improvements * feat(widget): custom header button * feat(news): item details pane * feat(widget): custom title * fix(news): better cache and misc fixes * feat(widget): resize left and top handles * feat(widget): transparent widget while moving/resizing * feat(news): better "big size" style * fix(news): media sizes in rich content * feat(plugin): local plugins support * fix: scrolling issue in Fx * fix: colors * fix(nav bar): more item overflowing * feat(vuln): frontend * chore: locale update * fix: image in suggestion dropdown (dev) * fix(suggestion): missing custom image * feat(view): user default plugin logo if no provided icon * feat(view): better loading UX * feat(view): button background if view is selected * feat(view): new nav indicator * feat(widget): use plugin logo as default icon * feat(widget): better widget modal * feat(widget): longDescription * fix(widget): news validate url param * feat(widget): filter widgets in add pane * feat(widget): tease upcoming widgets * chore: fix merge dev * chore: yarn install * chore: sync versions * chore: update apollo * docs: widget * fix(progress): graphql error * fix(deps): localPath * perf(plugin): faster local plugin refresh * fix(nav): center active indicator * feat(task): improved header * feat(client addon): custom component load timeout message * feat(suggestion): ping animation to improve discoverability * chore: update vue-apollo * feat(api): requestRoute * fix(suggestion): hide more info link if no link * fix(style): ul padding * test(e2e): fix plugin path * chore: change test scripts * chore(deps): upgrade * fix: build error * fix(widget): removed moving scale transform * fix(widget): resize handles style * chore(deps): unpin apollo-utilities * chore(deps): lock fix * test(e2e): fix server * fix: issue with writeQuery See: https://github.com/apollographql/apollo-client/issues/4031#issuecomment-433668473 * test(e2e): fix tests * test(e2e): missing widgets build * fix: missing widgets dep
335 lines
7.5 KiB
JavaScript
335 lines
7.5 KiB
JavaScript
const shortid = require('shortid')
|
|
// Connectors
|
|
const cwd = require('./cwd')
|
|
const prompts = require('./prompts')
|
|
// Utils
|
|
const { log } = require('../util/logger')
|
|
|
|
function getDefaultWidgets () {
|
|
return [
|
|
{
|
|
id: shortid(),
|
|
definitionId: 'org.vue.widgets.welcome',
|
|
x: 0,
|
|
y: 0,
|
|
width: 3,
|
|
height: 4,
|
|
configured: true,
|
|
config: null
|
|
},
|
|
{
|
|
id: shortid(),
|
|
definitionId: 'org.vue.widgets.kill-port',
|
|
x: 3,
|
|
y: 0,
|
|
width: 2,
|
|
height: 1,
|
|
configured: true,
|
|
config: null
|
|
}
|
|
]
|
|
}
|
|
|
|
let widgetDefs = new Map()
|
|
let widgetCount = new Map()
|
|
let widgets = []
|
|
let currentWidget
|
|
|
|
let loadPromise, loadResolve
|
|
|
|
function reset (context) {
|
|
widgetDefs = new Map()
|
|
widgetCount = new Map()
|
|
widgets = []
|
|
loadPromise = new Promise((resolve) => {
|
|
loadResolve = () => {
|
|
loadPromise = null
|
|
resolve()
|
|
log('Load Promise resolved')
|
|
}
|
|
})
|
|
}
|
|
|
|
async function registerDefinition ({ definition, project }, context) {
|
|
definition.hasConfigPrompts = !!definition.onConfigOpen
|
|
|
|
// Default icon
|
|
if (!definition.icon) {
|
|
const plugins = require('./plugins')
|
|
const plugin = plugins.findOne({ id: definition.pluginId, file: cwd.get() }, context)
|
|
const logo = await plugins.getLogo(plugin, context)
|
|
if (logo) {
|
|
definition.icon = `${logo}?project=${project.id}`
|
|
}
|
|
}
|
|
|
|
widgetDefs.set(definition.id, definition)
|
|
}
|
|
|
|
function listDefinitions (context) {
|
|
return widgetDefs.values()
|
|
}
|
|
|
|
function findDefinition ({ definitionId }, context) {
|
|
const def = widgetDefs.get(definitionId)
|
|
if (!def) {
|
|
throw new Error(`Widget definition ${definitionId} not found`)
|
|
}
|
|
return def
|
|
}
|
|
|
|
async function list (context) {
|
|
log('loadPromise', loadPromise)
|
|
if (loadPromise) {
|
|
await loadPromise
|
|
}
|
|
log('widgets', widgets)
|
|
return widgets
|
|
}
|
|
|
|
function load (context) {
|
|
const projects = require('./projects')
|
|
const id = projects.getCurrent(context).id
|
|
const project = context.db.get('projects').find({ id }).value()
|
|
widgets = project.widgets
|
|
|
|
if (!widgets) {
|
|
widgets = getDefaultWidgets()
|
|
}
|
|
|
|
widgets.forEach(widget => {
|
|
updateCount(widget.definitionId, 1)
|
|
})
|
|
|
|
log('Widgets loaded', widgets.length)
|
|
|
|
loadResolve()
|
|
}
|
|
|
|
function save (context) {
|
|
const projects = require('./projects')
|
|
const id = projects.getCurrent(context).id
|
|
context.db.get('projects').find({ id }).assign({
|
|
widgets
|
|
}).write()
|
|
}
|
|
|
|
function canAddMore (definition, context) {
|
|
if (definition.maxCount) {
|
|
return getCount(definition.id) < definition.maxCount
|
|
}
|
|
return true
|
|
}
|
|
|
|
function add ({ definitionId }, context) {
|
|
const definition = findDefinition({ definitionId }, context)
|
|
|
|
const { x, y, width, height } = findValidPosition(definition)
|
|
|
|
const widget = {
|
|
id: shortid(),
|
|
definitionId,
|
|
x,
|
|
y,
|
|
width,
|
|
height,
|
|
config: null,
|
|
configured: !definition.needsUserConfig
|
|
}
|
|
|
|
// Default config
|
|
if (definition.defaultConfig) {
|
|
widget.config = definition.defaultConfig({
|
|
definition
|
|
})
|
|
}
|
|
|
|
updateCount(definitionId, 1)
|
|
widgets.push(widget)
|
|
save(context)
|
|
|
|
if (definition.onAdded) {
|
|
definition.onAdded({ widget, definition })
|
|
}
|
|
|
|
return widget
|
|
}
|
|
|
|
function getCount (definitionId) {
|
|
if (widgetCount.has(definitionId)) {
|
|
return widgetCount.get(definitionId)
|
|
} else {
|
|
return 0
|
|
}
|
|
}
|
|
|
|
function updateCount (definitionId, mod) {
|
|
widgetCount.set(definitionId, getCount(definitionId) + mod)
|
|
}
|
|
|
|
function findValidPosition (definition, currentWidget = null) {
|
|
// Find next available space
|
|
const width = (currentWidget && currentWidget.width) || definition.defaultWidth || definition.minWidth
|
|
const height = (currentWidget && currentWidget.height) || definition.defaultHeight || definition.minHeight
|
|
// Mark occupied positions on the grid
|
|
const grid = new Map()
|
|
for (const widget of widgets) {
|
|
if (widget !== currentWidget) {
|
|
for (let x = widget.x; x < widget.x + widget.width; x++) {
|
|
for (let y = widget.y; y < widget.y + widget.height; y++) {
|
|
grid.set(`${x}:${y}`, true)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// Go through the possible positions
|
|
let x = 0
|
|
let y = 0
|
|
while (true) {
|
|
// Virtual "line brak"
|
|
if (x !== 0 && x + width >= 7) {
|
|
x = 0
|
|
y++
|
|
}
|
|
const { result, testX } = hasEnoughSpace(grid, x, y, width, height)
|
|
if (!result) {
|
|
x = testX + 1
|
|
continue
|
|
}
|
|
// Found! :)
|
|
break
|
|
}
|
|
|
|
return {
|
|
x,
|
|
y,
|
|
width,
|
|
height
|
|
}
|
|
}
|
|
|
|
function hasEnoughSpace (grid, x, y, width, height) {
|
|
// Test if enough horizontal available space
|
|
for (let testX = x; testX < x + width; testX++) {
|
|
// Test if enough vertical available space
|
|
for (let testY = y; testY < y + height; testY++) {
|
|
if (grid.get(`${testX}:${testY}`)) {
|
|
return { result: false, testX }
|
|
}
|
|
}
|
|
}
|
|
return { result: true }
|
|
}
|
|
|
|
function findById ({ id }, context) {
|
|
return widgets.find(w => w.id === id)
|
|
}
|
|
|
|
function remove ({ id }, context) {
|
|
const index = widgets.findIndex(w => w.id === id)
|
|
if (index !== -1) {
|
|
const widget = widgets[index]
|
|
updateCount(widget.definitionId, -1)
|
|
widgets.splice(index, 1)
|
|
save(context)
|
|
|
|
const definition = findDefinition(widget, context)
|
|
if (definition.onAdded) {
|
|
definition.onAdded({ widget, definition })
|
|
}
|
|
|
|
return widget
|
|
}
|
|
}
|
|
|
|
function move (input, context) {
|
|
const widget = findById(input, context)
|
|
if (widget) {
|
|
widget.x = input.x
|
|
widget.y = input.y
|
|
const definition = findDefinition(widget, context)
|
|
widget.width = input.width
|
|
widget.height = input.height
|
|
if (widget.width < definition.minWidth) widget.width = definition.minWidth
|
|
if (widget.width > definition.maxWidth) widget.width = definition.maxWidth
|
|
if (widget.height < definition.minHeight) widget.height = definition.minHeight
|
|
if (widget.height > definition.maxHeight) widget.height = definition.maxHeight
|
|
|
|
for (const otherWidget of widgets) {
|
|
if (otherWidget !== widget) {
|
|
if (areOverlapping(otherWidget, widget)) {
|
|
const otherDefinition = findDefinition(otherWidget, context)
|
|
Object.assign(otherWidget, findValidPosition(otherDefinition, otherWidget))
|
|
}
|
|
}
|
|
}
|
|
|
|
save(context)
|
|
}
|
|
return widgets
|
|
}
|
|
|
|
function areOverlapping (widgetA, widgetB) {
|
|
return (
|
|
widgetA.x + widgetA.width - 1 >= widgetB.x &&
|
|
widgetA.x <= widgetB.x + widgetB.width - 1 &&
|
|
widgetA.y + widgetA.height - 1 >= widgetB.y &&
|
|
widgetA.y <= widgetB.y + widgetB.height - 1
|
|
)
|
|
}
|
|
|
|
async function openConfig ({ id }, context) {
|
|
const widget = findById({ id }, context)
|
|
const definition = findDefinition(widget, context)
|
|
if (definition.onConfigOpen) {
|
|
const result = await definition.onConfigOpen({
|
|
widget,
|
|
definition,
|
|
context
|
|
})
|
|
await prompts.reset(widget.config || {})
|
|
result.prompts.forEach(prompts.add)
|
|
await prompts.start()
|
|
currentWidget = widget
|
|
}
|
|
return widget
|
|
}
|
|
|
|
function getConfigPrompts ({ id }, context) {
|
|
return currentWidget && currentWidget.id === id ? prompts.list() : []
|
|
}
|
|
|
|
function saveConfig ({ id }, context) {
|
|
const widget = findById({ id }, context)
|
|
widget.config = prompts.getAnswers()
|
|
widget.configured = true
|
|
save(context)
|
|
currentWidget = null
|
|
return widget
|
|
}
|
|
|
|
function resetConfig ({ id }, context) {
|
|
// const widget = findById({ id }, context)
|
|
// TODO
|
|
save(context)
|
|
}
|
|
|
|
module.exports = {
|
|
reset,
|
|
registerDefinition,
|
|
listDefinitions,
|
|
findDefinition,
|
|
list,
|
|
load,
|
|
save,
|
|
canAddMore,
|
|
getCount,
|
|
add,
|
|
remove,
|
|
move,
|
|
openConfig,
|
|
getConfigPrompts,
|
|
saveConfig,
|
|
resetConfig
|
|
}
|