Commit 576155d2bf17ecd5232a532adb5e4748791ca836

Authored by ww
1 parent ae5914ca

wip: data dashboard component

  1 +<script lang="ts" setup>
  2 +import '@simonwep/pickr/dist/themes/monolith.min.css'
  3 +import ColorPicker from '@simonwep/pickr'
  4 +import type { PropType } from 'vue'
  5 +import { computed, onMounted, onUnmounted, ref, unref } from 'vue'
  6 +
  7 +type Format = Exclude<keyof ColorPicker.HSVaColor, 'clone'>
  8 +
  9 +const props = defineProps({
  10 + value: {
  11 + type: String,
  12 + default: '#377dff',
  13 + },
  14 + format: {
  15 + type: String as PropType<ColorPicker.Representation>,
  16 + default: 'HEXA' as ColorPicker.Representation,
  17 + },
  18 + config: {
  19 + type: Object as PropType<ColorPicker.Options>,
  20 + },
  21 +})
  22 +
  23 +const emit = defineEmits(['update:value'])
  24 +
  25 +const prefixCls = 'thingskit'
  26 +
  27 +const colorPickerCls = 'color-picker'
  28 +
  29 +const picker = ref<Nullable<ColorPicker>>(null)
  30 +
  31 +const getFormat = computed<Format>(() => {
  32 + return `to${props.format}`
  33 +})
  34 +
  35 +const getColor = () => {
  36 + const value = unref(picker)?.getColor()
  37 + return value && value[unref(getFormat)]().toString()
  38 +}
  39 +
  40 +const onInit = () => {
  41 + unref(picker)?.setColor(props.value)
  42 +}
  43 +
  44 +const onSave = () => {
  45 + const value = getColor()
  46 + emit('update:value', value)
  47 + unref(picker)?.hide()
  48 +}
  49 +
  50 +const getOption = computed<ColorPicker.Options>(() => {
  51 + const { config = {} } = props
  52 + return {
  53 + theme: 'monolith',
  54 + components: {
  55 + // Main components
  56 + preview: true,
  57 + opacity: true,
  58 + hue: true,
  59 +
  60 + // Input / output Options
  61 + interaction: {
  62 + hex: true,
  63 + rgba: true,
  64 + input: true,
  65 + clear: true,
  66 + save: true,
  67 + },
  68 + },
  69 + i18n: {
  70 + 'ui:dialog': 'color picker dialog',
  71 + 'btn:toggle': 'toggle color picker dialog',
  72 + 'btn:swatch': 'color swatch',
  73 + 'btn:last-color': '使用以前的颜色',
  74 + 'btn:save': '保存',
  75 + 'btn:cancel': '取消',
  76 + 'btn:clear': '清除',
  77 +
  78 + // 用于 aria-labels 的字符串
  79 + 'aria:btn:save': 'save and close',
  80 + 'aria:btn:cancel': 'cancel and close',
  81 + 'aria:btn:clear': 'clear and close',
  82 + 'aria:input': '颜色输入字段',
  83 + 'aria:palette': '颜色选择区域',
  84 + 'aria:hue': '色调选择滑块',
  85 + 'aria:opacity': '选择滑块',
  86 + },
  87 + ...config,
  88 +
  89 + el: `.${prefixCls}-${colorPickerCls}`,
  90 + }
  91 +})
  92 +
  93 +onMounted(() => {
  94 + picker.value = ColorPicker.create(unref(getOption))
  95 + unref(picker)?.on('init', onInit)
  96 + unref(picker)?.on('save', onSave)
  97 +})
  98 +
  99 +onUnmounted(() => {
  100 + unref(picker)?.off('init', onInit)
  101 + unref(picker)?.off('save', onSave)
  102 +
  103 + unref(picker)?.destroyAndRemove()
  104 +})
  105 +</script>
  106 +
  107 +<template>
  108 + <div :class="`${prefixCls}-${colorPickerCls}`" />
  109 +</template>
... ...
  1 +<script lang="ts" setup>
  2 +import icon from '../assets/command.svg'
  3 +
  4 +const props = defineProps<{
  5 + value?: boolean
  6 +}>()
  7 +
  8 +const emit = defineEmits(['update:value', 'change'])
  9 +
  10 +const handleChange = (event: Event) => {
  11 + const _value = (event.target as HTMLInputElement).checked
  12 + emit('update:value', _value)
  13 + emit('change', _value)
  14 +}
  15 +</script>
  16 +
  17 +<template>
  18 + <div class="command-send-button">
  19 + 123
  20 + <a href="javascript:;">
  21 + <img :src="icon" alt="">
  22 + </a>
  23 + </div>
  24 +</template>
  25 +
  26 +<style scoped lang="less">
  27 +.command-send-button {
  28 + width: 80px;
  29 + height: 60px;
  30 + position: relative;
  31 +
  32 + a {
  33 + position: absolute;
  34 + box-sizing: border-box;
  35 + cursor: pointer;
  36 + text-decoration: none;
  37 + color: #fff;
  38 + background: #f2385a;
  39 + top: 0;
  40 + padding: 0 16px;
  41 + height: 60px;
  42 + width: 80px;
  43 + overflow: hidden;
  44 + font-size: 20px;
  45 + line-height: 60px;
  46 + border-radius: 10px;
  47 + box-shadow: 0 15px 0 0 #f02046, 0 0 20px 0 #bbb;
  48 + transition: all 0.2s;
  49 + }
  50 +
  51 +}
  52 +</style>
... ...
  1 +<script lang="ts" setup>
  2 + import type { ECharts, EChartsOption } from 'echarts';
  3 + import type { PropType } from 'vue';
  4 + import { nextTick, onMounted, onUnmounted, ref, unref } from 'vue';
  5 + import { init } from 'echarts';
  6 +
  7 + interface DataSource {
  8 + id: string | number;
  9 + }
  10 +
  11 + const props = defineProps({
  12 + dataSource: {
  13 + type: Object as PropType<DataSource>,
  14 + required: true,
  15 + },
  16 + chartOption: {
  17 + type: Object as PropType<EChartsOption>,
  18 + // required: true,
  19 + },
  20 + add: {
  21 + type: Function,
  22 + required: true,
  23 + },
  24 + });
  25 +
  26 + const getControlsWidgetId = () => `widget-chart-${props.dataSource.id}`;
  27 +
  28 + const chartRef = ref<Nullable<ECharts>>(null);
  29 +
  30 + function initChart() {
  31 + const chartDom = document.getElementById(getControlsWidgetId())!;
  32 + chartRef.value = init(chartDom);
  33 + const option: EChartsOption = props.chartOption || {
  34 + tooltip: {
  35 + trigger: 'item',
  36 + // confine: true,
  37 + extraCssText: 'position: fixed;',
  38 + position: (point, params, dom, rect, size) => {
  39 + const parentEl = (dom as HTMLDivElement).parentElement!;
  40 +
  41 + const { top = 0, left = 0 } = parentEl.getBoundingClientRect()!;
  42 + return [left, top];
  43 + },
  44 + },
  45 + series: [
  46 + {
  47 + name: 'Access From',
  48 + type: 'pie',
  49 + radius: '50%',
  50 + data: [
  51 + { value: 1048, name: 'Search Engine' },
  52 + { value: 735, name: 'Direct' },
  53 + { value: 580, name: 'Email' },
  54 + { value: 484, name: 'Union Ads' },
  55 + { value: 300, name: 'Video Ads' },
  56 + ],
  57 + emphasis: {
  58 + itemStyle: {
  59 + shadowBlur: 10,
  60 + shadowOffsetX: 0,
  61 + shadowColor: 'rgba(0, 0, 0, 0.5)',
  62 + },
  63 + },
  64 + },
  65 + ],
  66 + };
  67 +
  68 + nextTick(() => {
  69 + option && unref(chartRef)?.setOption(option);
  70 + });
  71 + }
  72 +
  73 + function update() {
  74 + unref(chartRef)?.resize();
  75 + }
  76 +
  77 + onMounted(() => {
  78 + initChart();
  79 + props.add(props.dataSource.id, update);
  80 + });
  81 +
  82 + onUnmounted(() => {
  83 + unref(chartRef)?.clear();
  84 + });
  85 +
  86 + defineExpose({ update });
  87 +</script>
  88 +
  89 +<template>
  90 + <div :id="getControlsWidgetId()" class="widget-charts"></div>
  91 +</template>
  92 +
  93 +<style scoped>
  94 + .widget-charts {
  95 + min-width: 10px;
  96 + min-height: 10px;
  97 + width: 100%;
  98 + height: 100%;
  99 + }
  100 +
  101 + .widget-charts > div {
  102 + width: 100%;
  103 + height: 100%;
  104 + }
  105 +</style>
... ...
  1 +<script lang="ts">
  2 +import { defineComponent } from 'vue'
  3 +
  4 +export default defineComponent({
  5 + props: {
  6 + msg: String,
  7 + },
  8 + setup(props) {
  9 +
  10 + },
  11 +})
  12 +</script>
  13 +
  14 +<template>
  15 + <div>{{ msg }}</div>
  16 +</template>
... ...
  1 +<script lang="ts" setup>
  2 +const props = defineProps<{
  3 + value?: boolean
  4 +}>()
  5 +
  6 +const emit = defineEmits(['update:value', 'change'])
  7 +
  8 +const handleChange = (event: Event) => {
  9 + const _value = (event.target as HTMLInputElement).checked
  10 + emit('update:value', _value)
  11 + emit('change', _value)
  12 +}
  13 +</script>
  14 +
  15 +<template>
  16 + <label class="indicator-light-switch">
  17 + <input :value="props.value" :checked="props.value" type="checkbox" @change="handleChange">
  18 + <div class="panel" />
  19 + </label>
  20 +</template>
  21 +
  22 +<style scoped lang="less">
  23 +.indicator-light-switch {
  24 + input[type='checkbox'] {
  25 + display: none;
  26 + }
  27 + .panel {
  28 + position: relative;
  29 + width: 60px;
  30 + height: 60px;
  31 + cursor: pointer;
  32 + border-radius: 50%;
  33 + background: linear-gradient(#dedede, #fdfdfd);
  34 + box-shadow: 0 3px 5px rgb(0 0 0 / 25%), inset 0 1px 0 hsl(0deg 0% 100% / 30%), inset 0 -5px 5px hsl(0deg 0% 39% / 10%), inset 0 5px 5px hsl(0deg 0% 100% / 30%);
  35 + }
  36 +
  37 + .panel::after,
  38 + .panel::before {
  39 + content: '';
  40 + position: absolute;
  41 + width: 20%;
  42 + height: 20%;
  43 + border-radius: 50%;
  44 + left: 40%;
  45 + top: 40%;
  46 + }
  47 +
  48 + input:checked ~ .panel::after {
  49 + display: none;
  50 + }
  51 +
  52 + input:not(:checked) ~ .panel::before {
  53 + display: none;
  54 + }
  55 +
  56 + input:not(:checked) ~ .panel {
  57 + background: linear-gradient(#EAEAEA, #eaeaea);
  58 + }
  59 +
  60 + .panel::after {
  61 + background-color: #ddd;
  62 + background: radial-gradient(40% 35%, #ccc, #969696 60%);
  63 + box-shadow: inset 0 2px 1px rgb(0 0 0 / 15%), 0 2px 5px hsl(0deg 0% 78% / 10%);
  64 + }
  65 +
  66 + .panel::before {
  67 + background-color: #25d025;
  68 + box-shadow: inset 0 3px 5px 1px rgb(0 0 0 / 10%), 0 1px 0 hsl(0deg 0% 100% / 40%), 0 0 10px 2px rgb(0 210 0 / 50%);
  69 + }
  70 +}
  71 +</style>
... ...
  1 +<script lang="ts" setup>
  2 +import on from '../assets/light-bulb-on.svg'
  3 +import off from '../assets/light-bulb-off.svg'
  4 +
  5 +const props = defineProps<{
  6 + value?: boolean
  7 +}>()
  8 +
  9 +const emit = defineEmits(['update:value', 'change'])
  10 +
  11 +const handleChange = (event: Event) => {
  12 + const _value = (event.target as HTMLInputElement).checked
  13 + emit('update:value', _value)
  14 + emit('change', _value)
  15 +}
  16 +</script>
  17 +
  18 +<template>
  19 + <label class="light-bulb-switch">
  20 + <input :value="props.value" :checked="props.value" type="checkbox" @change="handleChange">
  21 + <div class="light-bulb__on">
  22 + <img :src="on" alt="开">
  23 + </div>
  24 + <div class="light-bulb__off">
  25 + <img :src="off" alt="关">
  26 + </div>
  27 + </label>
  28 +</template>
  29 +
  30 +<style scoped lang="less">
  31 +.light-bulb-switch {
  32 + input[type='checkbox'] {
  33 + display: none;
  34 + }
  35 +
  36 + input:checked ~ .light-bulb__off {
  37 + display: none;
  38 + }
  39 +
  40 + input:not(:checked) ~ .light-bulb__on {
  41 + display: none;
  42 + }
  43 +
  44 + .light-bulb__on > img,
  45 + .light-bulb__off > img {
  46 + width: 48px;
  47 + height: 48px;
  48 + cursor: pointer;
  49 + }
  50 +}
  51 +</style>
... ...
  1 +<script lang="ts" setup>
  2 +const props = defineProps<{
  3 + value?: boolean
  4 +}>()
  5 +
  6 +const emit = defineEmits(['update:value', 'change'])
  7 +
  8 +const handleChange = (event: Event) => {
  9 + const _value = (event.target as HTMLInputElement).checked
  10 + emit('update:value', _value)
  11 + emit('change', _value)
  12 +}
  13 +</script>
  14 +
  15 +<template>
  16 + <label class="rocker-switch">
  17 + <input :value="props.value" type="checkbox" :checked="props.value" @change="handleChange">
  18 + <span class="switch-left">ON</span>
  19 + <span class="switch-right">OFF</span>
  20 + </label>
  21 +</template>
  22 +
  23 +<style scoped lang="less">
  24 +.rocker-switch {
  25 + display: flex;
  26 + width: 72px;
  27 + font-size: 14px;
  28 + color: #fff;
  29 + font-weight: 700;
  30 + letter-spacing: 1px;
  31 + border: 0.5em solid #eee;
  32 + background-color: #999;
  33 + user-select: none;
  34 +
  35 + input[type='checkbox'] {
  36 + display: none;
  37 + }
  38 +
  39 + input:checked + .switch-left + .switch-right {
  40 + transform: rotate(-15deg) skew(-15deg) translate(-1px, -5px);
  41 + }
  42 + input:checked + .switch-left + .switch-right::before {
  43 + background-color: #ccc;
  44 + content: '';
  45 + position: absolute;
  46 + width: 3px;
  47 + height: 30px;
  48 + transform: skewY(75deg) skewX(4deg) rotate(5deg) translateX(1px);
  49 + top: 1.5px;
  50 + right: -1.5px;
  51 + }
  52 +
  53 + input + .switch-left {
  54 + transform: rotate(15deg) skew(17deg) translate(1px, -5px);
  55 + }
  56 +
  57 + input:checked + .switch-left {
  58 + background-color: #0084d0;
  59 + transform: none;
  60 + }
  61 +
  62 + input:not(:checked) + .switch-left {
  63 + background-color: #ccc;
  64 + }
  65 +
  66 + input:not(:checked) + .switch-left + .switch-right {
  67 + background-color: #bd5757;
  68 + color: #fff;
  69 + }
  70 +
  71 + input + .switch-left::before {
  72 + background-color: #ccc;
  73 + content: '';
  74 + position: absolute;
  75 + width: 3px;
  76 + height: 30px;
  77 + background-color: #ccc;
  78 + transform: skewY(-75deg) skewX(-4deg) rotate(-5deg) translateX(-1.5px);
  79 + top: -1.5px;
  80 + left: -1.5px;
  81 + }
  82 +
  83 + .switch-left {
  84 + width: 36px;
  85 + height: 30px;
  86 + text-align: center;
  87 + line-height: 30px;
  88 + cursor: pointer;
  89 + }
  90 +
  91 + .switch-right {
  92 + color: #888;
  93 + width: 36px;
  94 + height: 30px;
  95 + text-align: center;
  96 + line-height: 30px;
  97 + background-color: #ddd;
  98 + cursor: pointer;
  99 + }
  100 +}
  101 +</style>
... ...
  1 +<script lang="ts" setup>
  2 +const props = defineProps<{
  3 + value?: boolean
  4 +}>()
  5 +
  6 +const emit = defineEmits(['update:value', 'change'])
  7 +
  8 +const handleChange = (event: Event) => {
  9 + const _value = (event.target as HTMLInputElement).checked
  10 + emit('update:value', _value)
  11 + emit('change', _value)
  12 +}
  13 +</script>
  14 +
  15 +<template>
  16 + <label class="sliding-switch">
  17 + <input :value="props.value" type="checkbox" :checked="props.value" @change="handleChange">
  18 + <span class="slider" />
  19 + <span class="on">ON</span>
  20 + <span class="off">OFF</span>
  21 + </label>
  22 +</template>
  23 +
  24 +<style scoped lang="less">
  25 +.sliding-switch {
  26 + position: relative;
  27 + display: block;
  28 + font-weight: 700;
  29 + line-height: 40px;
  30 + width: 80px;
  31 + height: 40px;
  32 + font-size: 14px;
  33 + cursor: pointer;
  34 + user-select: none;
  35 +
  36 + input[type='checkbox'] {
  37 + display: none;
  38 + }
  39 +
  40 + .slider {
  41 + width: 80px;
  42 + height: 40px;
  43 + box-sizing: border-box;
  44 + position: absolute;
  45 + top: 0;
  46 + left: 0;
  47 + right: 0;
  48 + bottom: 0;
  49 + border: 2px solid #ecf0f3;
  50 + border-radius: 20px;
  51 + box-shadow: -2px -2px 8px #fff, -2px -2px 12px hsl(0deg 0% 100% / 50%), inset -2px -2px 8px #fff, inset -2px -2px 12px hsl(0deg 0% 100% / 50%), inset 2px 2px 4px hsl(0deg 0% 100% / 10%),
  52 + inset 2px 2px 8px rgb(0 0 0 / 30%), 2px 2px 8px rgb(0 0 0 / 30%);
  53 + background-color: #ecf0f3;
  54 + z-index: -1;
  55 + }
  56 +
  57 + .slider::after {
  58 + position: absolute;
  59 + cursor: pointer;
  60 + display: block;
  61 + content: '';
  62 + width: 24px;
  63 + height: 24px;
  64 + border-radius: 50%;
  65 + top: 6px;
  66 + left: 6px;
  67 + background-color: #ecf0f3;
  68 + box-shadow: -2px -2px 8px #fff, -2px -2px 12px hsl(0deg 0% 100% / 50%), inset 2px 2px 4px hsl(0deg 0% 100% / 10%), 2px 2px 8px rgb(0 0 0 / 30%);
  69 + z-index: 999;
  70 + transition: 0.5s;
  71 + }
  72 +
  73 + input:checked ~ .off {
  74 + opacity: 0;
  75 + }
  76 +
  77 + input:checked ~ .slider::after {
  78 + transform: translateX(35px);
  79 + }
  80 + input:not(:checked) ~ .on {
  81 + opacity: 0;
  82 + transform: translateX(0px);
  83 + }
  84 +
  85 + .on,
  86 + .off {
  87 + display: inline-block;
  88 + margin-left: 3px;
  89 + width: 34px;
  90 + text-align: center;
  91 + transition: 0.2s;
  92 + }
  93 +
  94 + .on {
  95 + color: #039be5;
  96 + }
  97 + .off {
  98 + color: #999;
  99 + }
  100 +}
  101 +</style>
... ...
  1 +<script lang="ts" setup>
  2 +const props = defineProps<{
  3 + value?: boolean
  4 +}>()
  5 +
  6 +const emit = defineEmits(['update:value', 'change'])
  7 +
  8 +const handleChange = (event: Event) => {
  9 + const _value = (event.target as HTMLInputElement).checked
  10 + emit('update:value', _value)
  11 + emit('change', _value)
  12 +}
  13 +</script>
  14 +
  15 +<template>
  16 + <div class="toggle-switch">
  17 + <label class="switch">
  18 + <input :value="props.value" type="checkbox" :checked="props.value" @change="handleChange">
  19 + <div class="button">
  20 + <div class="light" />
  21 + <div class="dots" />
  22 + <div class="characters" />
  23 + <div class="shine" />
  24 + <div class="shadow" />
  25 + </div>
  26 + </label>
  27 + </div>
  28 +</template>
  29 +
  30 +<style scoped>
  31 +.toggle-switch {
  32 + flex: 1 1 auto;
  33 + max-width: 75px;
  34 + height: 97.5px;
  35 + display: flex;
  36 +}
  37 +
  38 +.switch {
  39 + background-color: black;
  40 + box-sizing: border-box;
  41 + width: 100%;
  42 + height: 100%;
  43 + box-shadow: 0 0 10px 2px rgba(0, 0, 0, 0.2), 0 0 1px 2px black, inset 0 2px 2px -2px white, inset 0 0 2px 15px #47434c, inset 0 0 2px 22px black;
  44 + border-radius: 5px;
  45 + padding: 10px;
  46 + perspective: 700px;
  47 +}
  48 +
  49 +.switch input {
  50 + display: none;
  51 +}
  52 +
  53 +.switch input:checked + .button {
  54 + transform: translateZ(20px) rotateX(25deg);
  55 + box-shadow: 0 -5px 10px #ff1818;
  56 +}
  57 +
  58 +.switch input:checked + .button .light {
  59 + animation: flicker 0.2s infinite 0.3s;
  60 +}
  61 +
  62 +.switch input:checked + .button .shine {
  63 + opacity: 1;
  64 +}
  65 +
  66 +.switch input:checked + .button .shadow {
  67 + opacity: 0;
  68 +}
  69 +
  70 +.switch .button {
  71 + display: flex;
  72 + justify-content: center;
  73 + align-items: center;
  74 + transition: all 0.3s cubic-bezier(1, 0, 1, 1);
  75 + transform-origin: center center -20px;
  76 + transform: translateZ(20px) rotateX(-25deg);
  77 + transform-style: preserve-3d;
  78 + background-color: #9b0621;
  79 + width: 100%;
  80 + height: 100%;
  81 + position: relative;
  82 + cursor: pointer;
  83 + background: linear-gradient(#980000 0%, #6f0000 30%, #6f0000 70%, #980000 100%);
  84 + background-repeat: no-repeat;
  85 +}
  86 +
  87 +.switch .button::before {
  88 + content: '';
  89 + background: linear-gradient(rgba(255, 255, 255, 0.8) 10%, rgba(255, 255, 255, 0.3) 30%, #650000 75%, #320000) 50% 50%/97% 97%, #b10000;
  90 + background-repeat: no-repeat;
  91 + width: 100%;
  92 + height: 30px;
  93 + transform-origin: top;
  94 + transform: rotateX(-90deg);
  95 + position: absolute;
  96 + top: 0;
  97 +}
  98 +
  99 +.switch .button::after {
  100 + content: '';
  101 + background-image: linear-gradient(#650000, #320000);
  102 + width: 100%;
  103 + height: 30px;
  104 + transform-origin: top;
  105 + transform: translateY(30px) rotateX(-90deg);
  106 + position: absolute;
  107 + bottom: 0;
  108 + box-shadow: 0 30px 8px 0px black, 0 60px 20px 0px rgb(0 0 0 / 50%);
  109 +}
  110 +
  111 +.switch .light {
  112 + opacity: 0;
  113 + animation: light-off 1s;
  114 + position: absolute;
  115 + width: 80%;
  116 + height: 80%;
  117 + background-image: radial-gradient(#ffc97e, transparent 40%), radial-gradient(circle, #ff1818 50%, transparent 80%);
  118 +}
  119 +
  120 +.switch .dots {
  121 + position: absolute;
  122 + width: 100%;
  123 + height: 100%;
  124 + background-image: radial-gradient(transparent 30%, rgba(101, 0, 0, 0.7) 70%);
  125 + background-size: 10px 10px;
  126 +}
  127 +
  128 +.switch .characters {
  129 + position: absolute;
  130 + width: 100%;
  131 + height: 100%;
  132 + background: linear-gradient(white, white) 50% 20%/5% 20%, radial-gradient(circle, transparent 50%, white 52%, white 70%, transparent 72%) 50% 80%/33% 25%;
  133 + background-repeat: no-repeat;
  134 +}
  135 +
  136 +.switch .shine {
  137 + transition: all 0.3s cubic-bezier(1, 0, 1, 1);
  138 + opacity: 0.3;
  139 + position: absolute;
  140 + width: 100%;
  141 + height: 100%;
  142 + background: linear-gradient(white, transparent 3%) 50% 50%/97% 97%, linear-gradient(rgba(255, 255, 255, 0.5), transparent 50%, transparent 80%, rgba(255, 255, 255, 0.5)) 50% 50%/97% 97%;
  143 + background-repeat: no-repeat;
  144 +}
  145 +
  146 +.switch .shadow {
  147 + transition: all 0.3s cubic-bezier(1, 0, 1, 1);
  148 + opacity: 1;
  149 + position: absolute;
  150 + width: 100%;
  151 + height: 100%;
  152 + background: linear-gradient(transparent 70%, rgba(0, 0, 0, 0.8));
  153 + background-repeat: no-repeat;
  154 +}
  155 +
  156 +@keyframes flicker {
  157 + 0% {
  158 + opacity: 1;
  159 + }
  160 +
  161 + 80% {
  162 + opacity: 0.8;
  163 + }
  164 +
  165 + 100% {
  166 + opacity: 1;
  167 + }
  168 +}
  169 +
  170 +@keyframes light-off {
  171 + 0% {
  172 + opacity: 1;
  173 + }
  174 +
  175 + 80% {
  176 + opacity: 0;
  177 + }
  178 +}
  179 +</style>
... ...
  1 +<script lang="ts" setup>
  2 + import { MoreOutlined, LineChartOutlined } from '@ant-design/icons-vue';
  3 + import { DropMenu } from '/@/components/Dropdown';
  4 + import Dropdown from '/@/components/Dropdown/src/Dropdown.vue';
  5 + import { Tooltip } from 'ant-design-vue';
  6 + import Icon from '/@/components/Icon/src/Icon.vue';
  7 + enum MoreEvent {
  8 + EDIT = 'edit',
  9 + COPY = 'copy',
  10 + DELETE = 'delete',
  11 + }
  12 +
  13 + const dropMenuList: DropMenu[] = [
  14 + {
  15 + text: '编辑组件',
  16 + event: MoreEvent.EDIT,
  17 + icon: 'ant-design:edit-outlined',
  18 + },
  19 + {
  20 + text: '复制组件',
  21 + event: MoreEvent.COPY,
  22 + icon: 'ant-design:copy-outlined',
  23 + },
  24 + {
  25 + text: '删除组件',
  26 + event: MoreEvent.DELETE,
  27 + icon: 'ant-design:delete-outlined',
  28 + },
  29 + ];
  30 +
  31 + const handleMenuEvent = (event: DropMenu) => {
  32 + console.log(event);
  33 + };
  34 +</script>
  35 +
  36 +<template>
  37 + <div class="flex justify-between">
  38 + <div class="flex flex-auto">
  39 + <div v-for="item in 3" class="flex mx-2" :key="item">
  40 + <div class="flex items-center">
  41 + <Tooltip>
  42 + <Icon icon="ant-design:edit-outlined" />
  43 + </Tooltip>
  44 + <span class="truncate max-w-25">设备名称{{ item }}</span>
  45 + </div>
  46 + </div>
  47 + </div>
  48 + <div class="flex items-center wx-9">
  49 + <Tooltip title="趋势">
  50 + <LineChartOutlined class="cursor-pointer mx-2" />
  51 + </Tooltip>
  52 + <Dropdown :drop-menu-list="dropMenuList" :trigger="['click']" @menu-event="handleMenuEvent">
  53 + <Tooltip title="更多">
  54 + <MoreOutlined class="transform rotate-90 cursor-pointer" />
  55 + </Tooltip>
  56 + </Dropdown>
  57 + </div>
  58 + </div>
  59 +</template>
... ...
  1 +<script lang="ts" setup>
  2 + import { onMounted } from 'vue';
  3 + import { useUpdateCenter } from '../../hook/useUpdateCenter';
  4 + import type { DataSource, WidgetWrapperRegister } from './type';
  5 +
  6 + const props = defineProps<{
  7 + dataSource: DataSource[];
  8 + register?: WidgetWrapperRegister;
  9 + }>();
  10 +
  11 + const { update, add, remove } = useUpdateCenter();
  12 +
  13 + onMounted(() => {
  14 + props.register && props.register(props.dataSource);
  15 + });
  16 +
  17 + defineExpose({ update });
  18 +</script>
  19 +
  20 +<template>
  21 + <section class="widget">
  22 + <slot name="header"></slot>
  23 +
  24 + <div class="widget-content">
  25 + <div
  26 + v-for="item in props.dataSource"
  27 + :key="item.id"
  28 + :style="{ width: `${item.width}%`, height: `${item.height}%` }"
  29 + class="widget-item"
  30 + >
  31 + <div class="widget-box">
  32 + <div class="widget-controls-container">
  33 + <slot
  34 + name="controls"
  35 + :record="item"
  36 + :add="add"
  37 + :remove="remove"
  38 + :update="update"
  39 + ></slot>
  40 + </div>
  41 + <div class="widget-value">
  42 + <slot name="value" :record="item"></slot>
  43 + </div>
  44 + <div class="widget-label">
  45 + <slot name="label" :record="item"></slot>
  46 + </div>
  47 + </div>
  48 + </div>
  49 + </div>
  50 + <slot name="footer"></slot>
  51 + </section>
  52 +</template>
  53 +
  54 +<style scoped>
  55 + .widget {
  56 + display: flex;
  57 + flex-direction: column;
  58 + width: 100%;
  59 + height: 100%;
  60 + }
  61 + .widget-content {
  62 + display: flex;
  63 + flex-wrap: wrap;
  64 + justify-content: center;
  65 + align-items: center;
  66 +
  67 + width: 100%;
  68 + height: 100%;
  69 + }
  70 +
  71 + .widget-box .widget-charts {
  72 + display: flex;
  73 + }
  74 +
  75 + .widget-item {
  76 + display: flex;
  77 + flex-direction: column;
  78 + overflow: hidden;
  79 + justify-content: center;
  80 + align-items: center;
  81 + }
  82 +
  83 + .widget-box {
  84 + position: relative;
  85 + width: 100%;
  86 + height: 100%;
  87 + }
  88 +
  89 + .widget-controls-container {
  90 + flex: 1 1 auto;
  91 + width: 100%;
  92 + height: calc(100% - 20px);
  93 + display: flex;
  94 + align-items: center;
  95 + justify-content: center;
  96 + }
  97 +
  98 + .widget-controls-container > div {
  99 + width: 100%;
  100 + height: 100%;
  101 + background-color: red;
  102 + }
  103 +
  104 + .widget-value {
  105 + font-size: 14px;
  106 + position: absolute;
  107 + width: 100%;
  108 + top: 0;
  109 + text-align: center;
  110 + overflow: hidden;
  111 + text-overflow: ellipsis;
  112 + }
  113 +
  114 + .widget-label {
  115 + font-size: 14px;
  116 + line-height: 1.2;
  117 + text-align: center;
  118 + overflow: hidden;
  119 + text-overflow: ellipsis;
  120 + }
  121 +</style>
... ...
  1 +export interface DataSource {
  2 + id: number | string
  3 + width: number
  4 + height: number
  5 +
  6 + [key: string]: any
  7 +}
  8 +
  9 +export type WidgetWrapperRegister = (dataSource: DataSource[]) => any
... ...
1 1 <script lang="ts" setup>
2   - import { PageWrapper } from '/@/components/Page';
3   - import { Button } from 'ant-design-vue';
  2 + import { Button, PageHeader } from 'ant-design-vue';
  3 + import { GridItem, GridLayout, Layout } from 'vue3-grid-layout';
  4 + import { nextTick, ref } from 'vue';
  5 + import BaseDashboard from '../cpns/Dashboard/BaseDashboard.vue';
  6 + import WidgetWrapper from '../cpns/WidgetWrapper/WidgetWrapper.vue';
  7 + import BaseWidgetHeader from '../cpns/WidgetHeader/BaseWidgetHeader.vue';
4 8 const handleBack = () => {};
  9 +
  10 + interface ChartAttr {
  11 + id: string | number;
  12 + width: number;
  13 + height: number;
  14 + }
  15 +
  16 + interface ChartSetting extends Layout {
  17 + chart: ChartAttr[];
  18 + }
  19 +
  20 + const widgetEl = new Map<string, Fn>();
  21 +
  22 + const id = '296Charts';
  23 + // GridItem.
  24 + const layout = ref<ChartSetting[]>([
  25 + {
  26 + x: 0,
  27 + y: 0,
  28 + w: 6,
  29 + h: 6,
  30 + i: id,
  31 + static: false,
  32 + chart: [
  33 + { id: 'a', width: 100, height: 100 },
  34 + { id: 'b', width: 100, height: 100 },
  35 + { id: 'c', width: 100, height: 100 },
  36 + { id: 'd', width: 100, height: 100 },
  37 + { id: 'e', width: 100, height: 100 },
  38 + { id: 'f', width: 100, height: 100 },
  39 + { id: 'g', width: 100, height: 100 },
  40 + { id: 'h', width: 100, height: 100 },
  41 + { id: 'i', width: 100, height: 100 },
  42 + ],
  43 + },
  44 + {
  45 + x: 0,
  46 + y: 0,
  47 + w: 6,
  48 + h: 6,
  49 + i: 'sdasdf',
  50 + static: false,
  51 + chart: [
  52 + { id: 'j', width: 100, height: 100 },
  53 + { id: 'k', width: 100, height: 100 },
  54 + { id: 'l', width: 100, height: 100 },
  55 + { id: 'm', width: 100, height: 100 },
  56 + { id: 'n', width: 100, height: 100 },
  57 + { id: 'o', width: 100, height: 100 },
  58 + { id: 'p', width: 100, height: 100 },
  59 + { id: 'q', width: 100, height: 100 },
  60 + { id: 'r', width: 100, height: 100 },
  61 + ],
  62 + },
  63 + ]);
  64 + const draggable = ref(true);
  65 + const resizable = ref(true);
  66 +
  67 + const GirdLayoutColNum = 24;
  68 +
  69 + const GridLayoutMargin = 10;
  70 +
  71 + function updateSize(i: string, newH: number, newW: number, newHPx: number, newWPx: number) {
  72 + newWPx = Number(newWPx);
  73 + newHPx = Number(newHPx);
  74 +
  75 + const data = layout.value.find((item) => item.i === i)!;
  76 + const border = 2;
  77 + const length = data.chart.length || 0;
  78 +
  79 + const row = Math.floor(Math.pow(length, 0.5));
  80 + const col = Math.floor(length / row);
  81 + let width = Math.floor(100 / col);
  82 + let height = Math.floor(100 / row);
  83 +
  84 + const WHRatio = newWPx / newHPx;
  85 + const HWRatio = newHPx / newWPx;
  86 +
  87 + if (WHRatio > 1.6) {
  88 + width = Math.floor(100 / length);
  89 + height = 100;
  90 + }
  91 +
  92 + if (HWRatio > 1.6) {
  93 + height = Math.floor(100 / length);
  94 + width = 100;
  95 + }
  96 +
  97 + data.chart = data?.chart.map((item) => {
  98 + return {
  99 + ...item,
  100 + width,
  101 + height,
  102 + };
  103 + });
  104 + nextTick(() => {
  105 + const updateFn = widgetEl.get(i);
  106 + if (updateFn) updateFn();
  107 + });
  108 + }
  109 +
  110 + const itemResized = (i: string, newH: number, newW: number, newHPx: number, newWPx: number) => {
  111 + updateSize(i, newH, newW, newHPx, newWPx);
  112 + };
  113 +
  114 + const itemContainerResized = (
  115 + i: string,
  116 + newH: number,
  117 + newW: number,
  118 + newHPx: number,
  119 + newWPx: number
  120 + ) => {
  121 + updateSize(i, newH, newW, newHPx, newWPx);
  122 + };
  123 +
  124 + const updateCharts = (i: string) => {
  125 + nextTick(() => {
  126 + const updateFn = widgetEl.get(i);
  127 + if (updateFn) updateFn();
  128 + });
  129 + };
  130 +
  131 + const setComponentRef = (el: Element, record: ChartSetting) => {
  132 + if (widgetEl.has(record.i)) widgetEl.delete(record.i);
  133 + if (el && (el as unknown as { update: Fn }).update)
  134 + widgetEl.set(record.i, (el as unknown as { update: Fn }).update);
  135 + };
5 136 </script>
6 137
7 138 <template>
8   - <PageWrapper title="水电表看板" @back="handleBack" content="已创建组件: 3个">
  139 + <!-- <PageWrapper title="水电表看板" @back="handleBack" content="已创建组件: 3个">
9 140 <template #extra>
10 141 <Button type="primary">创建组件</Button>
11 142 </template>
12   - <section class="bg-light-50 h-full w-full"> </section>
13   - </PageWrapper>
  143 +
  144 + </PageWrapper> -->
  145 + <section class="bg-light-50 flex flex-col overflow-hidden h-full w-full">
  146 + <PageHeader title="水电表看板" @back="handleBack">
  147 + <template #extra>
  148 + <Button type="primary">创建组件</Button>
  149 + </template>
  150 + <div>已创建组件: 3个</div>
  151 + </PageHeader>
  152 + <section class="flex-1">
  153 + <GridLayout
  154 + v-model:layout="layout"
  155 + :col-num="GirdLayoutColNum"
  156 + :row-height="30"
  157 + :margin="[GridLayoutMargin, GridLayoutMargin]"
  158 + :is-draggable="draggable"
  159 + :is-resizable="resizable"
  160 + :vertical-compact="true"
  161 + :use-css-transforms="true"
  162 + style="width: 100%"
  163 + >
  164 + <GridItem
  165 + v-for="item in layout"
  166 + :key="item.i"
  167 + :static="item.static"
  168 + :x="item.x"
  169 + :y="item.y"
  170 + :w="item.w"
  171 + :h="item.h"
  172 + :i="item.i"
  173 + style="display: flex; flex-wrap: wrap"
  174 + class="grid-item-layout"
  175 + @resized="itemResized"
  176 + @resize="itemResized"
  177 + @moved="updateCharts"
  178 + @container-resized="itemContainerResized"
  179 + >
  180 + <WidgetWrapper
  181 + :key="item.i"
  182 + :ref="(el) => setComponentRef(el, item)"
  183 + :data-source="item.chart"
  184 + >
  185 + <template #header>
  186 + <!-- <div>header</div> -->
  187 + <BaseWidgetHeader />
  188 + </template>
  189 + <template #controls="{ record, add }">
  190 + <!-- <ToggleSwitch /> -->
  191 + <!-- <SlidingSwitch @change="handleChange" /> -->
  192 + <!-- <LightBulbSwitch @change="handleChange" /> -->
  193 + <!-- <div :id="getControlsWidgetId(record.id)" class="widget-charts" /> -->
  194 + <BaseDashboard :data-source="record" :add="add" />
  195 + </template>
  196 + <template #value="{ record }">
  197 + <span>{{ record.id }}</span>
  198 + </template>
  199 + <template #label="{ record }">
  200 + <span>{{ record.id }}</span>
  201 + </template>
  202 + </WidgetWrapper>
  203 + </GridItem>
  204 + </GridLayout>
  205 + </section>
  206 + </section>
14 207 </template>
  208 +
  209 +<style>
  210 + .vue-grid-item:not(.vue-grid-placeholder) {
  211 + background: #fcfcfc;
  212 + border: 1px solid black;
  213 + }
  214 + .vue-grid-item .resizing {
  215 + opacity: 0.9;
  216 + }
  217 + .vue-grid-item .static {
  218 + background: #cce;
  219 + }
  220 + .vue-grid-item .text {
  221 + font-size: 24px;
  222 + text-align: center;
  223 + position: absolute;
  224 + top: 0;
  225 + bottom: 0;
  226 + left: 0;
  227 + right: 0;
  228 + margin: auto;
  229 + height: 100%;
  230 + width: 100%;
  231 + }
  232 + .vue-grid-item .no-drag {
  233 + height: 100%;
  234 + width: 100%;
  235 + }
  236 + .vue-grid-item .minMax {
  237 + font-size: 12px;
  238 + }
  239 + .vue-grid-item .add {
  240 + cursor: pointer;
  241 + }
  242 + .vue-draggable-handle {
  243 + position: absolute;
  244 + width: 20px;
  245 + height: 20px;
  246 + top: 0;
  247 + left: 0;
  248 + background: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='10' height='10'><circle cx='5' cy='5' r='5' fill='#999999'/></svg>")
  249 + no-repeat;
  250 + background-position: bottom right;
  251 + padding: 0 8px 8px 0;
  252 + background-repeat: no-repeat;
  253 + background-origin: content-box;
  254 + box-sizing: border-box;
  255 + cursor: pointer;
  256 + }
  257 +
  258 + .container {
  259 + display: grid;
  260 + grid-template-columns: 3;
  261 + grid-row: 3;
  262 + }
  263 +
  264 + .grid-item-layout {
  265 + overflow: hidden;
  266 + border: 1px solid #eee !important;
  267 + background-color: #fcfcfc !important;
  268 + }
  269 +</style>
... ...
  1 +export function useUpdateCenter() {
  2 + const eventCenter = new Map<string, Fn>();
  3 +
  4 + const update = () => {
  5 + eventCenter.forEach((method) => {
  6 + method();
  7 + });
  8 + };
  9 +
  10 + const add = (key: string, method: Fn) => {
  11 + if (eventCenter.has(key)) {
  12 + window.console.log(`Update Center Has Exist This Update Method(${key})`);
  13 + return;
  14 + }
  15 + eventCenter.set(key, method);
  16 + };
  17 +
  18 + const remove = (key: string) => {
  19 + if (eventCenter.has(key)) eventCenter.delete(key);
  20 + };
  21 +
  22 + return { update, add, remove };
  23 +}
... ...
1 1 <script lang="ts" setup>
2   - import { List, Card, Statistic, Dropdown, Menu } from 'ant-design-vue';
  2 + import { List, Card, Statistic } from 'ant-design-vue';
3 3 import { ref, unref } from 'vue';
4 4 import { PageWrapper } from '/@/components/Page';
5 5 import {
... ... @@ -10,9 +10,9 @@
10 10 } from '@ant-design/icons-vue';
11 11 import { useCopyToClipboard } from '/@/hooks/web/useCopyToClipboard';
12 12 import { useMessage } from '/@/hooks/web/useMessage';
13   - import { useRoute, useRouter } from 'vue-router';
14   - const ListItem = List.Item;
15   - const MenuItem = Menu.Item;
  13 + import { useRouter } from 'vue-router';
  14 + import Dropdown from '/@/components/Dropdown/src/Dropdown.vue';
  15 + import { DropMenu } from '/@/components/Dropdown';
16 16
17 17 const router = useRouter();
18 18 const { createMessage } = useMessage();
... ... @@ -46,6 +46,25 @@
46 46 clipboardRef.value = '123';
47 47 unref(clipboardRef) && createMessage.success('复制成功');
48 48 };
  49 + enum MoreEvent {
  50 + EDIT = 'edit',
  51 + DELETE = 'DELETE',
  52 + }
  53 +
  54 + const dropMenuList: DropMenu[] = [
  55 + {
  56 + text: '编辑',
  57 + event: MoreEvent.EDIT,
  58 + icon: 'ant-design:edit-outlined',
  59 + },
  60 + {
  61 + text: '删除',
  62 + event: MoreEvent.DELETE,
  63 + icon: 'ant-design:delete-outlined',
  64 + },
  65 + ];
  66 +
  67 + const handleMenuEvent = (evetn: DropMenu) => {};
49 68
50 69 const handleEdit = () => {
51 70 router.push('/data/board/detail');
... ... @@ -66,19 +85,11 @@
66 85 <ListItem>
67 86 <Card class="data-card cursor-pointer">
68 87 <template #extra>
69   - <Dropdown>
70   - <template #overlay>
71   - <Menu>
72   - <MenuItem key="edit" @click="handleEdit">
73   - <EditOutlined />
74   - <span>编辑</span>
75   - </MenuItem>
76   - <MenuItem key="remove" @click="handleRemove">
77   - <DeleteOutlined />
78   - <span>删除</span>
79   - </MenuItem>
80   - </Menu>
81   - </template>
  88 + <Dropdown
  89 + :trigger="['click']"
  90 + @menu-event="handleMenuEvent"
  91 + :drop-menu-list="dropMenuList"
  92 + >
82 93 <MoreOutlined class="rotate-90 transform cursor-pointer" />
83 94 </Dropdown>
84 95 </template>
... ...
  1 +/**
  2 + * @descriptin Api docs https://jbaysolutions.github.io/vue-grid-layout/zh/guide/properties.html#griditem
  3 + */
  4 +declare module 'vue3-grid-layout' {
  5 + import type { CSSProperties } from 'vue';
  6 +
  7 + interface Layout {
  8 + i: string;
  9 + x: number;
  10 + y: number;
  11 + w: number;
  12 + h: number;
  13 + static?: boolean;
  14 + }
  15 +
  16 + interface BreakPoints {
  17 + lg?: number;
  18 + md?: number;
  19 + sm?: number;
  20 + xs?: number;
  21 + xxs?: number;
  22 + }
  23 +
  24 + interface GridLayoutProps {
  25 + layout: Layout[];
  26 +
  27 + responsiveLayouts?: any;
  28 +
  29 + /**
  30 + * @description Define Col Number, Need A Integer Number
  31 + * @type number
  32 + * @default 12
  33 + */
  34 + colNum?: number;
  35 +
  36 + /**
  37 + * @description Define Row Height
  38 + * @type number
  39 + * @default 150
  40 + */
  41 + rowHeight?: number;
  42 +
  43 + /**
  44 + * @description Define Max Rows
  45 + * @type number
  46 + * @default infinity
  47 + */
  48 + maxRows?: number;
  49 +
  50 + /**
  51 + * @description Define Marign Distance In Grid Layout
  52 + * @type [number, number]
  53 + * @default [10, 10]
  54 + */
  55 + margin?: [number, number];
  56 +
  57 + /**
  58 + * @description Define Element Can Draggable In Grid Layout
  59 + * @type boolean
  60 + * @default true
  61 + */
  62 + isDraggable?: boolean;
  63 +
  64 + /**
  65 + * @description Define Element Can Resizeable In Grid Layout
  66 + * @type boolean
  67 + * @default true
  68 + */
  69 + isResizable?: boolean;
  70 +
  71 + /**
  72 + * @description Define Element Can Mirrored In Grid Layout
  73 + * @type boolean
  74 + * @default false
  75 + */
  76 + isMirrored?: boolean;
  77 +
  78 + /**
  79 + * @descripion Define Element Can Auto Size In Grid Layout
  80 + * @type boolean
  81 + * @default true
  82 + */
  83 + autoSize?: boolean;
  84 +
  85 + /**
  86 + * @description Define Element Can Compact On Vertical Direction
  87 + * @type boolean
  88 + * @default false
  89 + */
  90 + verticalCompact?: boolean;
  91 +
  92 + /**
  93 + * @description Define Element Prevent Collision
  94 + * @type {boolean}
  95 + * @default false
  96 + */
  97 + preventCollision?: boolean;
  98 +
  99 + /**
  100 + * @description Define Element Use Css Transforms
  101 + * @type {boolean}
  102 + * @default true
  103 + */
  104 + useCssTransforms?: boolean;
  105 +
  106 + /**
  107 + * @description Define Whether Is Responsive Layout
  108 + * @type {boolean}
  109 + * @default false
  110 + */
  111 + responsive?: boolean;
  112 +
  113 + /**
  114 + * @description Setting Screen Break Points
  115 + * @type {BreakPoints}
  116 + * @default lg: 1200, md: 996, sm: 768, xs: 480, xxs: 0
  117 + */
  118 + breakPoints?: BreakPoints;
  119 +
  120 + /**
  121 + * @description Setting Column Break Points
  122 + * @type {BreakPoints}
  123 + * @default lg: 12, md: 10, sm: 6, xs: 4, xxs: 2
  124 + */
  125 + cols?: BreakPoints;
  126 +
  127 + /**
  128 + * @descritpion Use Dynamic Cursor Style
  129 + * @type {boolean}
  130 + * @default false
  131 + * @deprecated
  132 + */
  133 + useStyleCursor?: boolean;
  134 +
  135 + [key: string]: any;
  136 + }
  137 +
  138 + interface GridItemProps extends Layout {
  139 + /**
  140 + * @description Min Width
  141 + * @type {number}
  142 + * @default 1
  143 + */
  144 + minW?: number;
  145 +
  146 + /**
  147 + * @description Min Height
  148 + * @type {number}
  149 + * @default 1
  150 + */
  151 + minH?: number;
  152 +
  153 + /**
  154 + * @description Max Width
  155 + * @type {number}
  156 + * @default 1
  157 + */
  158 + maxW?: number;
  159 +
  160 + /**
  161 + * @description Min Height
  162 + * @type {number}
  163 + * @default 1
  164 + */
  165 + maxH?: number;
  166 +
  167 + /**
  168 + * @description Flag Grid Element Can Draggable
  169 + * @type {boolean | null}
  170 + * @default null
  171 + */
  172 + isDraggable?: boolean;
  173 +
  174 + /**
  175 + * @descrition Flag Grid Element Can Resizeable
  176 + * @type {boolean | null}
  177 + * @default null
  178 + */
  179 + isResizeable?: boolean;
  180 +
  181 + /**
  182 + * @description Flag Grid Element Is Static , Can't Draggable Resizeable Move
  183 + * @type {boolean}
  184 + * @default false
  185 + */
  186 + static?: boolean;
  187 +
  188 + /**
  189 + * @description Flag What Are The Element Can't Draggable, Usage Css Picker
  190 + * @type {string}
  191 + * @default 'a button'
  192 + */
  193 + dragIgnoreFrom?: string;
  194 +
  195 + /**
  196 + * @description Flag What Are The Element Allow Draggable, Usage Css Picker
  197 + * @type {string}
  198 + * @default null
  199 + */
  200 + dragAllowFrom?: string;
  201 +
  202 + /**
  203 + * @description Flag What Are The Element Can't Trigger Resize, Usage Css Picker
  204 + * @type {string}
  205 + * @deafult 'a button'
  206 + */
  207 + resizeIgnoreFrom?: string;
  208 +
  209 + style?: CSSProperties;
  210 +
  211 + onLayoutCreate?: (newLayout: Layout[]) => void;
  212 +
  213 + [key: string]: any;
  214 + }
  215 + type GirdLayoutEvent =
  216 + | 'layoutCreate'
  217 + | 'layoutBeforeMount'
  218 + | 'layoutMounted'
  219 + | 'layoutReady'
  220 + | 'layoutUpdate'
  221 + | 'breakpointChanged';
  222 +
  223 + interface LayoutBaseParams {
  224 + newLayout: Layout[];
  225 + }
  226 +
  227 + interface GridLayoutEmit {
  228 + layoutCreate: LayoutBaseParams;
  229 + layoutBeforeMount: LayoutBeforeMountParams;
  230 + layoutMounted: LayoutMountedParams;
  231 + layoutReady: layoutUpdateParams;
  232 + layoutUpdate: layoutUpdateParams;
  233 + breakpointChanged: breakpointChangedParams;
  234 + }
  235 +
  236 + /**
  237 + * @description Grid Item Event Name
  238 + */
  239 + type GridItemEvent = 'move' | 'moved' | 'resize' | 'resized' | 'containerResized';
  240 +
  241 + interface MoveParams {
  242 + i: number;
  243 + newX?: number;
  244 + newY?: number;
  245 + }
  246 +
  247 + interface ResizeParams {
  248 + i: number;
  249 + newH: number;
  250 + newW: number;
  251 + newHPx: number;
  252 + newWPx: number;
  253 + }
  254 +
  255 + interface GridItemEmit {
  256 + move: MoveParams;
  257 + moved: MoveParams;
  258 + resize: ResizeParams;
  259 + resized: ResizeParams;
  260 + containerResized: MoveParams;
  261 + }
  262 +
  263 + // const GridLayout: DefineComponent<GridLayoutProps, void, unknown>
  264 + // const GridItem: DefineComponent<GridItemProps, void, unknown>
  265 +
  266 + const GridLayout: {
  267 + new (): {
  268 + $props: GridLayoutProps;
  269 + $emit: <T extends GirdLayoutEvent>(event: T, ...args: GridLayoutEmit[T]) => void;
  270 + };
  271 + };
  272 +
  273 + const GridItem: {
  274 + new (): {
  275 + $props: GridItemProps;
  276 + $emit: <T extends GridItemEvent>(event: T, ...args: GridItemEmit[T]) => void;
  277 + };
  278 + };
  279 +
  280 + export { GridLayout, GridItem, BreakPoints, Layout, LayoutBaseParams, ResizeParams };
  281 +}
... ...