Initial commit
This commit is contained in:
@@ -0,0 +1,11 @@
|
||||
# 공통으로 적용하는 env 파일, .env.환경이름 과 merge
|
||||
VITE_DESC=COMMON
|
||||
# To set default Language for I18n (ko_KR, en_US, zh_CN, etc..)
|
||||
VITE_DEFAULT_LANG=ko_KR
|
||||
|
||||
# default env
|
||||
VITE_NODE_ENV=development
|
||||
VITE_API_URL=http://sdldev.misdev.sdspaas.io/
|
||||
VITE_DIST_PATH=../src/main/webapp/dd
|
||||
# CONTEXT_PATH value must be end with '/'
|
||||
VITE_WEB_CONTEXT_PATH=/
|
||||
@@ -0,0 +1,11 @@
|
||||
# 공통으로 적용하는 env 파일, .env.환경이름 과 merge
|
||||
VITE_DESC=COMMON
|
||||
# To set default Language for I18n (ko_KR, en_US, zh_CN, etc..)
|
||||
VITE_DEFAULT_LANG=ko_KR
|
||||
|
||||
# default env
|
||||
VITE_NODE_ENV=production
|
||||
VITE_API_URL=http://sdl.sec.samsung.net/administrator
|
||||
VITE_DIST_PATH=../src/main/webapp/pp
|
||||
# CONTEXT_PATH value must be end with '/'
|
||||
VITE_WEB_CONTEXT_PATH=/
|
||||
@@ -0,0 +1,11 @@
|
||||
# 공통으로 적용하는 env 파일, .env.환경이름 과 merge
|
||||
VITE_DESC=COMMON
|
||||
# To set default Language for I18n (ko_KR, en_US, zh_CN, etc..)
|
||||
VITE_DEFAULT_LANG=ko_KR
|
||||
|
||||
# default env
|
||||
VITE_NODE_ENV=local
|
||||
VITE_API_URL=http://localhost:8080
|
||||
VITE_DIST_PATH=../src/main/resources/public
|
||||
# CONTEXT_PATH value must be end with '/'
|
||||
VITE_WEB_CONTEXT_PATH=/
|
||||
@@ -0,0 +1,28 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
.DS_Store
|
||||
dist
|
||||
dist-ssr
|
||||
coverage
|
||||
*.local
|
||||
|
||||
/cypress/videos/
|
||||
/cypress/screenshots/
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"endOfLine": "crlf",
|
||||
"printWidth": 240,
|
||||
"proseWrap": "never",
|
||||
"arrowParens": "avoid",
|
||||
"bracketSpacing": true,
|
||||
"singleQuote": true,
|
||||
"tabWidth": 2,
|
||||
"trailingComma": "all",
|
||||
"semi": true
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
# frontend
|
||||
|
||||
This template should help get you started developing with Vue 3 in Vite.
|
||||
|
||||
## Recommended IDE Setup
|
||||
|
||||
[VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur) + [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin).
|
||||
|
||||
## Customize configuration
|
||||
|
||||
See [Vite Configuration Reference](https://vitejs.dev/config/).
|
||||
|
||||
## Project Setup
|
||||
|
||||
```sh
|
||||
npm install
|
||||
```
|
||||
|
||||
### Compile and Hot-Reload for Development
|
||||
|
||||
```sh
|
||||
npm run dev
|
||||
```
|
||||
|
||||
### Compile and Minify for Production
|
||||
|
||||
```sh
|
||||
npm run build
|
||||
```
|
||||
|
||||
### Lint with [ESLint](https://eslint.org/)
|
||||
|
||||
```sh
|
||||
npm run lint
|
||||
```
|
||||
@@ -0,0 +1,17 @@
|
||||
import pluginVue from 'eslint-plugin-vue';
|
||||
import eslintConfigPrettier from 'eslint-config-prettier';
|
||||
|
||||
export default [
|
||||
// add more generic rulesets here, such as:
|
||||
// js.configs.recommended,
|
||||
...pluginVue.configs['flat/strongly-recommended'],
|
||||
eslintConfigPrettier,
|
||||
// ...pluginVue.configs['flat/vue2-recommended'], // Use this if you are using Vue.js 2.x.
|
||||
{
|
||||
files: ['src/**/*.js'],
|
||||
rules: {
|
||||
// override/add rules settings here, such as:
|
||||
// 'vue/no-unused-vars': 'error'
|
||||
},
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,16 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" href="%VITE_WEB_CONTEXT_PATH%static/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<script src="https://code.jquery.com/jquery-3.6.1.min.js" integrity="sha256-o88AwQnZB+VDvE9tvIXrMQaPlFFSUTR+nldQm1LuPXQ=" crossorigin="anonymous"></script>
|
||||
<link href="//cdn.jsdelivr.net/npm/jquery.fancytree@2.27/dist/skin-win8/ui.fancytree.min.css" rel="stylesheet" />
|
||||
<script src="//cdn.jsdelivr.net/npm/jquery.fancytree@2.27/dist/jquery.fancytree-all-deps.min.js"></script>
|
||||
<script src="/static/js/jszip.min.js"></script>
|
||||
<title>SDL DEMO</title>
|
||||
</head>
|
||||
<body id="app">
|
||||
<script type="module" src="/src/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
Generated
+3692
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,52 @@
|
||||
{
|
||||
"type": "module",
|
||||
"name": "frontend",
|
||||
"version": "6.0.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"local": "vite --mode test",
|
||||
"dev": "vite --mode development",
|
||||
"prod": "vite --mode production",
|
||||
"build": "vite build --mode production",
|
||||
"build-test": "vite build --mode test",
|
||||
"build-dev": "vite build --mode development",
|
||||
"build-prod": "vite build --mode production",
|
||||
"preview": "vite preview --mode production",
|
||||
"preview-test": "vite preview --mode test",
|
||||
"preview-dev": "vite preview --mode development",
|
||||
"preview-prod": "vite preview --mode production",
|
||||
"lint": "eslint src/**/*.{vue,js}"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 20.14.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^1.7.7",
|
||||
"jquery": "^3.7.1",
|
||||
"jquery-ui": "^1.14.1",
|
||||
"jquery.fancytree": "^2.38.4",
|
||||
"lodash": "^4.17.21",
|
||||
"moment": "^2.30.1",
|
||||
"realgrid": "^2.8.5",
|
||||
"tiny-emitter": "^2.1.0",
|
||||
"vue": "^3.4.27",
|
||||
"vue-flatpickr-component": "^11.0.5",
|
||||
"vue-i18n": "^9.13.0",
|
||||
"vue-multiselect": "^3.1.0",
|
||||
"vue-router": "^4.3.1",
|
||||
"vuedraggable": "^4.1.0",
|
||||
"vuex": "^4.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@rushstack/eslint-patch": "^1.10.0",
|
||||
"@vitejs/plugin-vue": "^5.0.4",
|
||||
"@vitejs/plugin-vue-jsx": "^3.1.0",
|
||||
"@vue/eslint-config-prettier": "^9.0.0",
|
||||
"dotenv": "^16.0.3",
|
||||
"eslint": "^9.3.0",
|
||||
"eslint-plugin-vue": "^9.26.0",
|
||||
"less": "^4.1.3",
|
||||
"prettier": "^3.2.5",
|
||||
"vite": "^5.2.12"
|
||||
}
|
||||
}
|
||||
+2020
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,94 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ko">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
|
||||
<link rel="icon" href="/static/favicon.ico">
|
||||
<title>SDL DEMO</title>
|
||||
<link rel="stylesheet" type="text/css" href="/static/css/bootstrap-icons.css" />
|
||||
<style>
|
||||
*, *::before, *::after {
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
html, body {
|
||||
height: 100%;
|
||||
}
|
||||
body {
|
||||
display: flex;
|
||||
margin: 0;
|
||||
-webkit-box-align: center;
|
||||
-ms-flex-align: center;
|
||||
align-items: center;
|
||||
font-size: 14px;
|
||||
font-family: SamsungOneKorean, 돋움;
|
||||
color: #444;
|
||||
background-color: #f8f9fa;
|
||||
}
|
||||
div.div-center {
|
||||
width: 320px;
|
||||
height: 40%;
|
||||
overflow: auto;
|
||||
margin: auto;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
}
|
||||
.msg-icon {
|
||||
padding: 30px 10px;
|
||||
width: 80px;
|
||||
float: left;
|
||||
color:#dc3545;
|
||||
}
|
||||
.icon-size-48 {
|
||||
font-size: 48px;
|
||||
}
|
||||
.msg-text {
|
||||
float: left;
|
||||
padding-top: 10px;
|
||||
width: calc(100% - 80px);
|
||||
}
|
||||
h3 {
|
||||
font-size: 1.4rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
h4 {
|
||||
font-size: 1.2rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
footer {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
padding: 20px 50px;
|
||||
display: flex;
|
||||
border-top: 1px solid #e4eaec;
|
||||
}
|
||||
footer div {
|
||||
padding: 0 50px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="div-center">
|
||||
<div class="msg-box">
|
||||
<div class="msg-icon text-center">
|
||||
<i class="bi bi-x-circle icon-size-48"></i>
|
||||
</div>
|
||||
<div class="msg-text">
|
||||
<h3>No response from server</h3>
|
||||
<h4>try again</h4>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<footer>
|
||||
<div>
|
||||
<span>© Samsung</span>
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 2.3 MiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.7 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 77 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 3.0 KiB |
+13
File diff suppressed because one or more lines are too long
@@ -0,0 +1,7 @@
|
||||
<template>
|
||||
<sdl-modal-container />
|
||||
<sdl-loadingbar />
|
||||
<sdl-dialog :click-to-close="false" />
|
||||
<router-view></router-view>
|
||||
<sdl-scroll-to-top></sdl-scroll-to-top>
|
||||
</template>
|
||||
@@ -0,0 +1,859 @@
|
||||
<!--
|
||||
Program: ApprovalPath.vue
|
||||
Author: SDL
|
||||
Description: Approval path common component
|
||||
-->
|
||||
<template>
|
||||
<div class="ui--approval-container">
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control" :placeholder="$t('sdl.common.label.payer')" aria-label="add User" v-model="searchUserNm" @keyup.esc="searchUserNm = ''" @keyup.enter.stop="openSearchUser()" />
|
||||
<button class="btn btn-outline-secondary" type="button" @click="openSearchUser">
|
||||
<i class="bi bi-search"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="ui--approval-list">
|
||||
<!-- file-button -->
|
||||
<div class="ui--approval-button">
|
||||
<div class="ui--apv-flex">
|
||||
<button class="btn ui--btn-apv-outline" type="button" :class="{ on: approvalType === 'APPROVAL' }" @click="changeApprovalType('APPROVAL')">
|
||||
{{ $t('sdl.approval.label.APPROVE') }}
|
||||
</button>
|
||||
<button class="btn ui--btn-apv-outline" type="button" :class="{ on: approvalType === 'AGREEMENT' }" @click="changeApprovalType('AGREEMENT')">
|
||||
{{ $t('sdl.approval.label.AGREEMENT') }}
|
||||
</button>
|
||||
<button class="btn ui--btn-apv-outline" type="button" :class="{ on: approvalType === 'NOTIFICATION' }" @click="changeApprovalType('NOTIFICATION')">
|
||||
{{ $t('sdl.approval.label.NOTIFY') }}
|
||||
</button>
|
||||
</div>
|
||||
<span class="ui--slide ms-2 me-2">|</span>
|
||||
<button class="btn ui--btn-xs" type="button" @click="changeApprovalType('EX_POST_APPROVAL')">
|
||||
{{ $t('sdl.approval.label.SKIP') }}
|
||||
</button>
|
||||
<span class="ui--slide ms-2 me-2">|</span>
|
||||
<button class="btn ui--btn-xs me-1" type="button" @click="setApprovalParallel(true)"><i class="bi bi-view-stacked"></i>{{ $t('sdl.common.label.parallel') }}</button>
|
||||
<button class="btn ui--btn-xs" type="button" @click="setApprovalParallel(false)"><i class="bi bi-grid"></i>{{ $t('sdl.approval.label.PARALLEL_CANCEL') }}</button>
|
||||
<span v-if="showRequired" class="ui--slide ms-2 me-2">|</span>
|
||||
<button v-if="showRequired" class="btn btn-outline-info btn-xs" @click="onRequiredApprListPopup" type="button" style="position: relative">
|
||||
{{ $t('sdl.approval.label.addRequiredApprovalUser') }}
|
||||
<div v-if="showRequiredTooltip" class="ui--app-required"><i class="bi bi-info-circle"></i> {{ $t('sdl.approval.message.selectRequiredApprovalUser') }}</div>
|
||||
</button>
|
||||
<span v-if="showRequired" class="ui--slide ms-2 me-2">|</span>
|
||||
<button class="btn ui--btn-icon-sm" :title="$t('sdl.common.label.Top')" type="button" @click="stepTopUp()">
|
||||
<i class="bi bi-skip-end-fill ui--trans-rotate-270"></i>
|
||||
</button>
|
||||
<button class="btn ui--btn-icon-sm" :title="$t('sdl.common.label.moveup')" type="button" @click="stepUp()">
|
||||
<i class="bi bi-play-fill ui--trans-rotate-270"></i>
|
||||
</button>
|
||||
<button class="btn ui--btn-icon-sm" :title="$t('sdl.common.label.movedown')" type="button" @click="stepDown()">
|
||||
<i class="bi bi-play-fill ui--trans-rotate-90"></i>
|
||||
</button>
|
||||
<button class="btn ui--btn-icon-sm" :title="$t('sdl.common.label.bottom')" type="button" @click="stepBottomDown()">
|
||||
<i class="bi bi-skip-end-fill ui--trans-rotate-90"></i>
|
||||
</button>
|
||||
<button class="btn ui--btn-icon-sm" :title="$t('sdl.common.label.delete')" type="button" @click="deleteUserTopBtn">
|
||||
<i class="bi bi-x"></i>
|
||||
</button>
|
||||
<span class="ui--slide ms-2 me-2">|</span>
|
||||
<span
|
||||
>{{ $t('sdl.common.label.total') }}<strong class="ms-1 me-2">{{ apprPathList.length }}</strong></span
|
||||
>
|
||||
</div>
|
||||
<!-- // file-button -->
|
||||
<!-- file-list -->
|
||||
<ul>
|
||||
<li v-for="user in apprPathList" :key="`${user.userId}_${user.stepIdx}`" :class="{ selected: user.selected, draft: user.approvalType === 'DRAFTING' }" @click="selectedPath(user)">
|
||||
<span class="count">{{ user.stepIdx }}</span>
|
||||
<div class="ui--apv-flex" v-if="user.approvalType === 'DRAFTING'">
|
||||
<button class="btn ui--btn-apv btn-apv-request" type="button">{{ $t('sdl.approval.label.REQUEST') }}</button>
|
||||
</div>
|
||||
<div class="ui--apv-flex" v-else-if="user.approvalType === 'EX_POST_APPROVAL'">
|
||||
<button class="btn ui--btn-apv btn-apv-after on" type="button">{{ $t('sdl.approval.label.SKIP') }}</button>
|
||||
</div>
|
||||
<div class="ui--apv-flex" v-else-if="user.approvalType === 'PARALLEL_APPROVAL'">
|
||||
<button class="btn ui--btn-apv ui--btn-apv-approve on" type="button">
|
||||
{{ $t('sdl.approval.label.PARALLEL_APPROVE') }}
|
||||
</button>
|
||||
</div>
|
||||
<div class="ui--apv-flex" v-else-if="user.approvalType === 'PARALLEL_AGREEMENT'">
|
||||
<button class="btn ui--btn-apv ui--btn-apv-approve on" type="button">
|
||||
{{ $t('sdl.approval.label.PARALLEL_AGREEMENT') }}
|
||||
</button>
|
||||
</div>
|
||||
<div class="ui--apv-flex" v-else>
|
||||
<button class="btn ui--btn-apv ui--btn-apv-approve" :class="{ on: user.approvalType === 'APPROVAL' }" @click.stop="changeUserApprovalType(user, 'APPROVAL')" type="button">
|
||||
{{ $t('sdl.approval.label.APPROVE') }}
|
||||
</button>
|
||||
<button class="btn ui--btn-apv ui--btn-apv-agreement" :class="{ on: user.approvalType === 'AGREEMENT' }" @click.stop="changeUserApprovalType(user, 'AGREEMENT')" type="button">
|
||||
{{ $t('sdl.approval.label.AGREEMENT') }}
|
||||
</button>
|
||||
<button class="btn ui--btn-apv ui--btn-apv-notice" :class="{ on: user.approvalType === 'NOTIFICATION' }" @click.stop="changeUserApprovalType(user, 'NOTIFICATION')" type="button">
|
||||
{{ $t('sdl.approval.label.NOTIFY') }}
|
||||
</button>
|
||||
</div>
|
||||
<span class="name" v-if="!user.required"> {{ user.userName }}/{{ user.deptName }}/{{ user.compName }}</span>
|
||||
<span class="name" v-if="user.required"
|
||||
><span class="text-info">{{ user.userName }}/{{ user.deptName }}/{{ user.compName }} ({{ $t('sdl.approval.label.requiredApprovalUser') }})</span></span
|
||||
>
|
||||
<div v-if="user.approvalType === 'DRAFTING'" class="ui--apv-right text-black-50">
|
||||
{{ $t('sdl.common.label.requester') }}
|
||||
</div>
|
||||
<div class="ui--apv-right" v-else>
|
||||
<button class="btn ui--btn-icon-xs" :class="{ on: user.routeModify }" :title="$t('sdl.common.label.pathchange')" @click.stop="approvalRouteModify(user)" type="button">
|
||||
<i class="bi bi-arrow-down-up"></i>
|
||||
</button>
|
||||
<button class="btn ui--btn-icon-xs" :class="{ on: user.bodyModify }" :title="$t('sdl.approval.label.BODYMODIFY')" @click.stop="approvalBodyModify(user)" type="button">
|
||||
<i class="bi bi-fonts"></i>
|
||||
</button>
|
||||
<button class="btn ui--btn-icon-xs" :class="{ on: user.arbitrary }" :title="$t('sdl.approval.label.ARBITRARY')" @click.stop="approvalArbitrary(user)" type="button">
|
||||
<i class="bi bi-pencil-square"></i>
|
||||
</button>
|
||||
<button class="btn ui--btn-icon-sm float-end" :title="$t('sdl.common.label.delete')" @click.stop="deleteUserRightBtn(user)" type="button">
|
||||
<i class="bi bi-x"></i>
|
||||
</button>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<!-- // file-list -->
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { SDLUtil } from '@/utils';
|
||||
import RequiredApprListPopup from '@/components/view/admin/approval/popup/RequiredApprListPopup.vue';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
approvalStepList: {
|
||||
type: Array,
|
||||
},
|
||||
showRequired: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
requiredApprovalUserList: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
internalFlag: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
searchUserNm: '',
|
||||
apprPathList: [],
|
||||
approvalType: 'APPROVAL',
|
||||
selectedDocTypeCode: '',
|
||||
checkParallesGroup: {},
|
||||
showRequiredTooltip: true,
|
||||
approvalPathStatus: [
|
||||
{ code: 'DRAFTING', label: 'sdl.approval.label.REQUEST' },
|
||||
{ code: 'APPROVAL', label: 'sdl.approval.label.APPROVE' },
|
||||
{ code: 'CANCEL', label: 'sdl.approval.label.CANCELLED' },
|
||||
{ code: 'AGREEMENT', label: 'sdl.approval.label.AGREEMENT' },
|
||||
{ code: 'NOTIFICATION', label: 'sdl.approval.label.NOTIFY' },
|
||||
{ code: 'EX_POST_APPROVAL', label: 'sdl.approval.label.SKIP' },
|
||||
{ code: 'PARALLEL_AGREEMENT', label: 'sdl.approval.label.PARALLEL_AGREEMENT' },
|
||||
{ code: 'PARALLEL_APPROVAL', label: 'sdl.approval.label.PARALLEL_APPROVE' },
|
||||
{ code: 'REJECT', label: 'sdl.approval.label.REJECTED' },
|
||||
],
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* param:
|
||||
* return:
|
||||
* exception:
|
||||
* Description : Page Initialization
|
||||
*/
|
||||
init() {
|
||||
// this.apprPathList.splice(0);
|
||||
this.searchUserNm = '';
|
||||
if (this.approvalStepList) {
|
||||
this.setApprovalList();
|
||||
}
|
||||
},
|
||||
/**
|
||||
* param:
|
||||
* return:
|
||||
* exception:
|
||||
* Description : When receiving approval list as props from outside
|
||||
*/
|
||||
setApprovalList() {
|
||||
this.apprPathList.splice(0);
|
||||
Object.keys(this.approvalStepList).forEach(idx => {
|
||||
const apprLine = this.approvalStepList[idx];
|
||||
this.apprPathList.push({
|
||||
stepIdx: apprLine.approvalStepSequence,
|
||||
parallel: apprLine.parallelProcess,
|
||||
approvalType: apprLine.approvalStepType,
|
||||
required: false,
|
||||
hasError: false,
|
||||
approverChangeAllowed: apprLine.approverChangeAllowed,
|
||||
bodyModify: apprLine.bodyModify,
|
||||
routeModify: apprLine.routeModify,
|
||||
arbitrary: apprLine.arbitrary,
|
||||
userId: apprLine.approvalPersonId,
|
||||
userName: apprLine.approvalPersonName,
|
||||
grdName: apprLine.approvalPersonPositionName,
|
||||
deptName: apprLine.approvalPersonDeptName,
|
||||
compName: apprLine.approvalPersonCompanyName,
|
||||
apprPathFlag: apprLine.apprPathFlag,
|
||||
docType: apprLine.docType,
|
||||
});
|
||||
});
|
||||
|
||||
this.resetApprovalSeq();
|
||||
},
|
||||
/**
|
||||
* param:
|
||||
* return:
|
||||
* exception:
|
||||
* Description : When you take the approval list from the inside,
|
||||
*/
|
||||
getApprovalList() {
|
||||
const rtnList = [];
|
||||
Object.keys(this.apprPathList).forEach(idx => {
|
||||
const apprLine = this.apprPathList[idx];
|
||||
rtnList.push({
|
||||
approvalStepSequence: apprLine.stepIdx,
|
||||
parallelProcess: apprLine.parallel,
|
||||
approvalStepType: apprLine.approvalType,
|
||||
required: false,
|
||||
hasError: false,
|
||||
approverChangeAllowed: true,
|
||||
bodyModify: apprLine.bodyModify,
|
||||
routeModify: apprLine.routeModify,
|
||||
arbitrary: apprLine.arbitrary,
|
||||
approvalPersonId: apprLine.userId,
|
||||
approvalPersonName: apprLine.userName,
|
||||
approvalPersonPositionName: apprLine.grdName,
|
||||
approvalPersonDeptName: apprLine.deptName,
|
||||
approvalPersonCompanyName: apprLine.compName,
|
||||
});
|
||||
});
|
||||
return rtnList;
|
||||
},
|
||||
/**
|
||||
* param: user -> json
|
||||
* return:
|
||||
* exception:
|
||||
* Description : Body can be modified
|
||||
*/
|
||||
approvalBodyModify(user) {
|
||||
if (user.approvalStatusTypeCode === 'DRAFTING') return; // No change of DRAFTING
|
||||
|
||||
user.bodyModify = !user.bodyModify;
|
||||
this.apprPathList.splice(this.apprPathList.indexOf(user), 1, user);
|
||||
},
|
||||
/**
|
||||
* param: user -> json
|
||||
* return:
|
||||
* exception:
|
||||
* Description : Is the approval path changed
|
||||
*/
|
||||
approvalRouteModify(user) {
|
||||
if (user.approvalStatusTypeCode === 'DRAFTING') return; // No change of DRAFTING
|
||||
|
||||
user.routeModify = !user.routeModify;
|
||||
this.apprPathList.splice(this.apprPathList.indexOf(user), 1, user);
|
||||
},
|
||||
/**
|
||||
* param: user -> json
|
||||
* return:
|
||||
* exception:
|
||||
* Description : Is the final decision for approval
|
||||
*/
|
||||
approvalArbitrary(user) {
|
||||
if (user.approvalStatusTypeCode === 'DRAFTING') return; // No change of DRAFTING
|
||||
|
||||
user.arbitrary = !user.arbitrary;
|
||||
this.apprPathList.splice(this.apprPathList.indexOf(user), 1, user);
|
||||
},
|
||||
/**
|
||||
* param: parallel -> Boolean
|
||||
* return:
|
||||
* exception:
|
||||
* Description : parallel approval setting
|
||||
*/
|
||||
setApprovalParallel(parallel) {
|
||||
if (this.apprPathList[0].selected) return; // No change of DRAFTING
|
||||
|
||||
if (this.checkRequired()) {
|
||||
// Required Approval Check
|
||||
SDLUtil.alert('sdl.approval.message.cannotChangeRequiredSteps');
|
||||
return;
|
||||
}
|
||||
const selectedRows = [];
|
||||
for (let i = 1; i < this.apprPathList.length; i += 1) {
|
||||
if (this.apprPathList[i].selected) {
|
||||
if (this.apprPathList[i].approvalType === 'NOTIFICATION') {
|
||||
// Notification does not pay in parallel
|
||||
SDLUtil.alert('sdl.approval.message.notifyCannotBeParallel');
|
||||
return;
|
||||
}
|
||||
if (this.apprPathList[i].approvalType === 'EX_POST_APPROVAL') {
|
||||
// No parallel payment for the latter decision
|
||||
SDLUtil.alert('sdl.approval.message.skipCannotBeParallel');
|
||||
return;
|
||||
}
|
||||
selectedRows.push(this.apprPathList[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (selectedRows.length < 2) {
|
||||
// Select more than one parallel approval
|
||||
SDLUtil.alert('sdl.approval.message.parallelRequiresTwoOrMore');
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.checkApprovalStepsParallelPosition(selectedRows, parallel, false)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Object.keys(selectedRows).forEach(idx => {
|
||||
const user = selectedRows[idx];
|
||||
if (user.selected && user.approvalType !== 'NOTIFICATION' && user.approvalType !== 'EX_POST_APPROVAL') {
|
||||
user.parallel = parallel;
|
||||
if (parallel) {
|
||||
if (user.approvalType === 'PARALLEL_APPROVAL' || user.approvalType === 'PARALLEL_AGREEMENT') return;
|
||||
if (user.approvalType === 'APPROVAL') {
|
||||
user.approvalType = 'PARALLEL_APPROVAL';
|
||||
} else {
|
||||
user.approvalType = 'PARALLEL_AGREEMENT';
|
||||
}
|
||||
} else if (user.approvalType === 'PARALLEL_APPROVAL') {
|
||||
user.approvalType = 'APPROVAL';
|
||||
} else if (user.approvalType === 'PARALLEL_AGREEMENT') {
|
||||
user.approvalType = 'AGREEMENT';
|
||||
}
|
||||
}
|
||||
this.apprPathList.splice(this.apprPathList.indexOf(user), 1, user);
|
||||
});
|
||||
|
||||
this.resetApprovalSeq();
|
||||
},
|
||||
/**
|
||||
* param: selectOptions -> array
|
||||
* param: parallel -> true/false(Boolean)
|
||||
* param: submit -> true/false(Boolean)
|
||||
* return:
|
||||
* exception:
|
||||
* Description : Check if parallel approval is possible
|
||||
*/
|
||||
checkApprovalStepsParallelPosition(selectOptions, parallel, submit) {
|
||||
const paralles = [];
|
||||
let retStatus = true;
|
||||
|
||||
selectOptions
|
||||
.filter(opt => opt.parallel)
|
||||
.forEach(opt => {
|
||||
if (typeof this.checkParallesGroup[opt.stepIdx] === 'undefined') {
|
||||
const checkParalles = [];
|
||||
checkParalles.push(opt);
|
||||
this.checkParallesGroup[opt.stepIdx] = checkParalles;
|
||||
} else {
|
||||
this.checkParallesGroup[opt.stepIdx].push(opt);
|
||||
}
|
||||
});
|
||||
|
||||
if (submit) {
|
||||
// Select more than one parallel approval
|
||||
Object.keys(this.checkParallesGroup).some(index => {
|
||||
if (this.checkParallesGroup[index].length === 1) {
|
||||
SDLUtil.alert('sdl.approval.message.parallelRemainTwoOrMore');
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
retStatus = false;
|
||||
} else {
|
||||
selectOptions
|
||||
.filter(opt => opt.selected && opt.approvalType !== 'NOTIFICATION')
|
||||
.forEach(opt => {
|
||||
paralles.push(opt);
|
||||
});
|
||||
|
||||
for (let i = 1; i < paralles.length; i += 1) {
|
||||
const diff = paralles[i - 1].stepIdx - paralles[i].stepIdx;
|
||||
if (diff === 1 || diff === -1 || diff === 0) {
|
||||
if (!parallel && paralles[i - 1].parallel !== paralles[i].parallel) {
|
||||
SDLUtil.alert('sdl.approval.message.notMattchParallele');
|
||||
return true;
|
||||
}
|
||||
retStatus = false;
|
||||
} else {
|
||||
SDLUtil.alert('sdl.approval.message.linked');
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// When releasing parallel payment, only one parallel approval step must be left.
|
||||
// If you select 3 parallel payments and replace the two parallel ones with the approval.
|
||||
if (!parallel && paralles.length > 0) {
|
||||
const opt = paralles[0];
|
||||
const opts = this.checkParallesGroup[opt.stepIdx];
|
||||
if (opts != null && opts.length - paralles.length === 1) {
|
||||
SDLUtil.alert('sdl.approval.message.parallelRemainTwoOrMore');
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return retStatus;
|
||||
},
|
||||
/**
|
||||
* param:
|
||||
* return:
|
||||
* exception:
|
||||
* Description : Required Approval Check
|
||||
*/
|
||||
checkRequired() {
|
||||
for (let i = 0; i < this.apprPathList.length; i += 1) {
|
||||
if (this.apprPathList[i].selected) {
|
||||
if (this.apprPathList[i].required) return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
/**
|
||||
* param:
|
||||
* return:
|
||||
* exception:
|
||||
* Description : open user search popup
|
||||
*/
|
||||
openSearchUser() {
|
||||
if (this.searchUserNm) {
|
||||
if (this.searchUserNm.length < 2) {
|
||||
SDLUtil.alert('sdl.common.message.enterMoreThan2CharForSearchTerms');
|
||||
return;
|
||||
}
|
||||
}
|
||||
SDLUtil.openUserPopup({
|
||||
searchColumn: 'userName',
|
||||
searchTxt: this.searchUserNm,
|
||||
rtnFunc: this.getUserList,
|
||||
knoxSearch: !this.internalFlag,
|
||||
});
|
||||
},
|
||||
/**
|
||||
* param: list -> array
|
||||
* return:
|
||||
* exception:
|
||||
* Description : return user search result
|
||||
*/
|
||||
getUserList(list) {
|
||||
const n = this.apprPathList.length;
|
||||
let dupFlag = false;
|
||||
|
||||
// Double-payer check
|
||||
Object.keys(list).forEach(i => {
|
||||
const userInfo = list[i];
|
||||
for (let j = 0; j < this.apprPathList.length; j += 1) {
|
||||
if (userInfo.userId === this.apprPathList[j].userId) {
|
||||
dupFlag = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
if (dupFlag) {
|
||||
SDLUtil.alert('sdl.approval.message.duplicateApprover');
|
||||
return;
|
||||
}
|
||||
Object.keys(list).forEach(i => {
|
||||
const userInfo = list[i];
|
||||
|
||||
this.apprPathList.push({
|
||||
stepIdx: parseInt(n, 10) + parseInt(i, 10),
|
||||
parallel: false,
|
||||
approvalType: this.approvalType,
|
||||
required: this.requiredApprovalUserList.some(requiredUser => requiredUser.userId === userInfo.userId),
|
||||
hasError: false,
|
||||
approverChangeAllowed: true,
|
||||
bodyModify: false,
|
||||
routeModify: false,
|
||||
arbitrary: false,
|
||||
departmentName: userInfo.deptName,
|
||||
positionName: userInfo.grdName,
|
||||
companyName: userInfo.compName,
|
||||
positionCode: userInfo.grdCode,
|
||||
departmentCode: userInfo.deptCode,
|
||||
companyCode: userInfo.compCode,
|
||||
requiredUser: userInfo.requiredUser,
|
||||
userId: userInfo.userId,
|
||||
userName: userInfo.userName,
|
||||
grdName: userInfo.grdName,
|
||||
deptName: userInfo.deptName,
|
||||
compName: userInfo.compName,
|
||||
});
|
||||
});
|
||||
|
||||
this.resetApprovalSeq();
|
||||
this.searchUserNm = '';
|
||||
},
|
||||
/**
|
||||
* param: user -> Object
|
||||
* return:
|
||||
* exception:
|
||||
* Description : select approval path
|
||||
*/
|
||||
selectedPath(user) {
|
||||
user.selected = !user.selected;
|
||||
|
||||
this.apprPathList.splice(this.apprPathList.indexOf(user), 1, user);
|
||||
},
|
||||
/**
|
||||
* param:
|
||||
* return:
|
||||
* exception:
|
||||
* Description : search popup RequiredApprListPopup
|
||||
*/
|
||||
onRequiredApprListPopup() {
|
||||
const { docType } = this.requiredApprovalUserList[0];
|
||||
SDLUtil.show(
|
||||
RequiredApprListPopup,
|
||||
{
|
||||
docType,
|
||||
rtnFunc: list => {
|
||||
this.showRequiredTooltip = false;
|
||||
|
||||
let dupFlag = false;
|
||||
// Double-payer check
|
||||
Object.keys(list).forEach(i => {
|
||||
const userInfo = list[i];
|
||||
for (let j = 0; j < this.apprPathList.length; j += 1) {
|
||||
if (userInfo.userId === this.apprPathList[j].userId) {
|
||||
dupFlag = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
if (dupFlag) {
|
||||
SDLUtil.alert('sdl.approval.message.duplicateApprover');
|
||||
return;
|
||||
}
|
||||
Object.keys(list).forEach(idx => {
|
||||
const apprLine = list[idx];
|
||||
this.apprPathList.push({
|
||||
stepIdx: apprLine.approvalStepSequence,
|
||||
parallel: false,
|
||||
approvalType: 'APPROVAL',
|
||||
required: true,
|
||||
hasError: false,
|
||||
approverChangeAllowed: true,
|
||||
bodyModify: apprLine.bodyModify,
|
||||
routeModify: apprLine.routeModify,
|
||||
arbitrary: apprLine.arbitrary,
|
||||
userId: apprLine.userId,
|
||||
userName: apprLine.userName,
|
||||
grdName: apprLine.grdName,
|
||||
deptName: apprLine.deptName,
|
||||
compName: apprLine.compName,
|
||||
apprPathFlag: false,
|
||||
docType,
|
||||
});
|
||||
});
|
||||
this.resetApprovalSeq();
|
||||
},
|
||||
},
|
||||
{
|
||||
width: '900px',
|
||||
height: 'auto',
|
||||
},
|
||||
);
|
||||
},
|
||||
/**
|
||||
* param: approvalType -> String
|
||||
* return:
|
||||
* exception:
|
||||
* Description : click top button(APPROVAL, AGREEMENT, NOTIFICATION)
|
||||
*/
|
||||
changeApprovalType(approvalType) {
|
||||
if (this.apprPathList[0].selected) return; // The drafter cannot be changed
|
||||
|
||||
// Approval person cannot be modified
|
||||
for (let i = 1; i < this.apprPathList.length; i += 1) {
|
||||
if (this.apprPathList[i].selected && !this.apprPathList[i].approverChangeAllowed) {
|
||||
SDLUtil.alert('sdl.approval.message.approverNotAllowedToChange');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this.approvalType = approvalType;
|
||||
// if (!this.chkSelectDocType()) return;
|
||||
|
||||
for (let i = 1; i < this.apprPathList.length; i += 1) {
|
||||
// Except the drafter
|
||||
if (this.apprPathList[i].selected) {
|
||||
this.apprPathList[i].approvalType = approvalType;
|
||||
this.apprPathList.splice(i, 1, this.apprPathList[i]);
|
||||
}
|
||||
}
|
||||
},
|
||||
/**
|
||||
* param: user -> Object
|
||||
* param: approvalType -> String
|
||||
* return:
|
||||
* exception:
|
||||
* Description : click left button(APPROVAL, AGREEMENT, NOTIFICATION)
|
||||
*/
|
||||
changeUserApprovalType(user, approvalType) {
|
||||
if (this.apprPathList[0].selected) return; // The drafter cannot be changed
|
||||
|
||||
// The authorized person cannot be modified
|
||||
if (!user.approverChangeAllowed) {
|
||||
SDLUtil.alert('sdl.approval.message.approverNotAllowedToChange');
|
||||
return;
|
||||
}
|
||||
|
||||
user.approvalType = approvalType;
|
||||
this.apprPathList.splice(this.apprPathList.indexOf(user), 1, user);
|
||||
},
|
||||
/**
|
||||
* param:
|
||||
* return:
|
||||
* exception:
|
||||
* Description : click top delete button
|
||||
*/
|
||||
deleteUserTopBtn() {
|
||||
if (this.apprPathList[0].selected) return; // The drafter cannot be changed
|
||||
|
||||
let apprPathFlag = false;
|
||||
let parallelChkFlag = false;
|
||||
for (let i = 0; i < this.apprPathList.length; i += 1) {
|
||||
const opt = this.apprPathList[i];
|
||||
|
||||
if (opt.selected && opt.parallel) {
|
||||
parallelChkFlag = true;
|
||||
}
|
||||
if (opt.selected && opt.apprPathFlag) {
|
||||
apprPathFlag = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (parallelChkFlag) {
|
||||
SDLUtil.alert('sdl.common.message.possibleToDeleteAfterParallelPayment');
|
||||
return;
|
||||
}
|
||||
|
||||
if (apprPathFlag) {
|
||||
SDLUtil.alert('sdl.approval.message.delApprovalPathMgmt');
|
||||
return;
|
||||
}
|
||||
|
||||
const selectedPathList = [];
|
||||
this.apprPathList.forEach(path => {
|
||||
if (path.selected) selectedPathList.push(path);
|
||||
});
|
||||
selectedPathList.forEach(selectedPath => {
|
||||
this.apprPathList.forEach((apprPath, idx) => {
|
||||
if (selectedPath === apprPath) this.apprPathList.splice(idx, 1);
|
||||
});
|
||||
});
|
||||
this.resetApprovalSeq();
|
||||
},
|
||||
/**
|
||||
* param: user -> Object
|
||||
* return:
|
||||
* exception:
|
||||
* Description : click right delete button
|
||||
*/
|
||||
deleteUserRightBtn(user) {
|
||||
if (this.apprPathList[0].selected) return; // The drafter cannot be changed
|
||||
|
||||
if (user.apprPathFlag) {
|
||||
SDLUtil.alert('sdl.approval.message.delApprovalPathMgmt');
|
||||
return;
|
||||
}
|
||||
|
||||
if (user.parallel) {
|
||||
SDLUtil.alert('sdl.common.message.possibleToDeleteAfterParallelPayment');
|
||||
return;
|
||||
}
|
||||
|
||||
this.apprPathList.splice(this.apprPathList.indexOf(user), 1);
|
||||
|
||||
this.resetApprovalSeq();
|
||||
},
|
||||
/**
|
||||
* param:
|
||||
* return:
|
||||
* exception:
|
||||
* Description : reset approval sequence numbering
|
||||
*/
|
||||
resetApprovalSeq() {
|
||||
let stepIdx = -1;
|
||||
let prevIsParallel = false;
|
||||
// let preIndex = -1;
|
||||
let preSelected = false;
|
||||
this.showRequiredTooltip = !this.apprPathList.some(appr => appr.required);
|
||||
for (let i = 0; i < this.apprPathList.length; i += 1) {
|
||||
const opt = this.apprPathList[i];
|
||||
if (!prevIsParallel || !opt.parallel) {
|
||||
stepIdx += 1;
|
||||
} else if (preSelected !== opt.selected) {
|
||||
stepIdx += 1;
|
||||
}
|
||||
// preIndex = opt.stepIdx;
|
||||
prevIsParallel = opt.parallel;
|
||||
opt.stepIdx = stepIdx;
|
||||
preSelected = opt.selected;
|
||||
|
||||
this.apprPathList.splice(i, 1, opt);
|
||||
}
|
||||
},
|
||||
chkSelectDocType() {
|
||||
if (this.selectedDocTypeCode === '') {
|
||||
SDLUtil.alert('sdl.approval.message.selectApprovalDocType');
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
/**
|
||||
* param:
|
||||
* return:
|
||||
* exception:
|
||||
* Description : Move to Top
|
||||
*/
|
||||
stepTopUp() {
|
||||
while (this.stepUp());
|
||||
},
|
||||
/**
|
||||
* param:
|
||||
* return:
|
||||
* exception:
|
||||
* Description : Move up
|
||||
*/
|
||||
stepUp() {
|
||||
if (this.apprPathList[0].selected) return false; // The drafter cannot be changed
|
||||
|
||||
const checkParallesGroup = {};
|
||||
for (let i = 0; i < this.apprPathList.length; i += 1) {
|
||||
const opt = this.apprPathList[i];
|
||||
if (opt.parallel) {
|
||||
if (typeof checkParallesGroup[opt.stepIdx] === 'undefined') {
|
||||
const checkParalles = [];
|
||||
checkParalles.push(opt);
|
||||
checkParallesGroup[opt.stepIdx] = checkParalles;
|
||||
} else {
|
||||
checkParallesGroup[opt.stepIdx].push(opt);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Check if it is parallel
|
||||
let paralleChkFlag = false;
|
||||
Object.keys(checkParallesGroup).forEach(i => {
|
||||
let n = 0;
|
||||
const opt = checkParallesGroup[i];
|
||||
Object.keys(opt).forEach(j => {
|
||||
if (opt[j].selected) {
|
||||
n += 1;
|
||||
}
|
||||
});
|
||||
if (n > 0 && n !== opt.length) {
|
||||
paralleChkFlag = true;
|
||||
}
|
||||
});
|
||||
|
||||
if (paralleChkFlag) {
|
||||
SDLUtil.alert('sdl.common.message.parallelPaymentCanOnlyBeTransferred');
|
||||
return false;
|
||||
}
|
||||
|
||||
let flag = false;
|
||||
for (let i = 1; i < this.apprPathList.length; i += 1) {
|
||||
if (this.apprPathList[i].selected) {
|
||||
if (i === 1) {
|
||||
return false;
|
||||
}
|
||||
const tmpItem = this.apprPathList[i];
|
||||
const parallelFlag = this.apprPathList[i - 1].parallel;
|
||||
this.apprPathList.splice(i, 1, this.apprPathList[i - 1]);
|
||||
this.apprPathList.splice(i - 1, 1, tmpItem);
|
||||
if (i - 2 >= 1 && this.apprPathList[i - 2].parallel && parallelFlag) {
|
||||
this.stepUp();
|
||||
}
|
||||
flag = true;
|
||||
}
|
||||
}
|
||||
this.resetApprovalSeq();
|
||||
return flag;
|
||||
},
|
||||
/**
|
||||
* param:
|
||||
* return:
|
||||
* exception:
|
||||
* Description : Move down
|
||||
*/
|
||||
stepDown() {
|
||||
if (this.apprPathList[0].selected) return false; // The drafter cannot be changed
|
||||
|
||||
const checkParallesGroup = {};
|
||||
for (let i = 0; i < this.apprPathList.length; i += 1) {
|
||||
const opt = this.apprPathList[i];
|
||||
if (opt.parallel) {
|
||||
if (typeof checkParallesGroup[opt.stepIdx] === 'undefined') {
|
||||
const checkParalles = [];
|
||||
checkParalles.push(opt);
|
||||
checkParallesGroup[opt.stepIdx] = checkParalles;
|
||||
} else {
|
||||
checkParallesGroup[opt.stepIdx].push(opt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check if it is parallel
|
||||
let paralleChkFlag = false;
|
||||
Object.keys(checkParallesGroup).forEach(i => {
|
||||
let n = 0;
|
||||
const opt = checkParallesGroup[i];
|
||||
Object.keys(opt).forEach(j => {
|
||||
if (opt[j].selected) {
|
||||
n += 1;
|
||||
}
|
||||
});
|
||||
if (n > 0 && n !== opt.length) {
|
||||
paralleChkFlag = true;
|
||||
}
|
||||
});
|
||||
|
||||
if (paralleChkFlag) {
|
||||
SDLUtil.alert('sdl.common.message.parallelPaymentCanOnlyBeTransferred');
|
||||
return false;
|
||||
}
|
||||
|
||||
let flag = false;
|
||||
for (let i = this.apprPathList.length - 1; i >= 0; i -= 1) {
|
||||
if (this.apprPathList[i].selected) {
|
||||
if (i === this.apprPathList.length - 1) {
|
||||
return false;
|
||||
}
|
||||
const tmpItem = this.apprPathList[i];
|
||||
const parallelFlag = this.apprPathList[i + 1].parallel;
|
||||
this.apprPathList.splice(i, 1, this.apprPathList[i + 1]);
|
||||
this.apprPathList.splice(i + 1, 1, tmpItem);
|
||||
if (i + 2 < this.apprPathList.length && this.apprPathList[i + 2].parallel && parallelFlag) {
|
||||
this.stepDown();
|
||||
}
|
||||
flag = true;
|
||||
}
|
||||
}
|
||||
this.resetApprovalSeq();
|
||||
return flag;
|
||||
},
|
||||
/**
|
||||
* param:
|
||||
* return:
|
||||
* exception:
|
||||
* Description : Move to the bottom
|
||||
*/
|
||||
stepBottomDown() {
|
||||
while (this.stepDown());
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.init();
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style></style>
|
||||
@@ -0,0 +1,162 @@
|
||||
<!--
|
||||
Program: Bread crumb
|
||||
Author: SDL
|
||||
Brief: Components that visually show where the current user is
|
||||
-->
|
||||
<template>
|
||||
<div class="ui--content-heading">
|
||||
<strong>
|
||||
{{ title }}
|
||||
<a href="javascript:"
|
||||
data-bs-toggle="tooltip"
|
||||
data-bs-placement="bottom"
|
||||
:data-bs-original-title="tooltipMessage"
|
||||
class="ui--addfavor"
|
||||
:class="[ isUserMenu ? 'on': 'off' ]"
|
||||
@click="isUserMenu ? deleteMyMenu(menu.menuId) : addMyMenu(menu.menuId)"
|
||||
>
|
||||
<i class="bi bi-star-fill"></i>
|
||||
</a>
|
||||
</strong>
|
||||
<nav aria-label="breadcrumb">
|
||||
<ol class="breadcrumb float-end">
|
||||
<li class="breadcrumb-item" v-if="home">
|
||||
<router-link :to="{ name: 'mainIndexPage' }">{{ homeText }} </router-link>
|
||||
</li>
|
||||
<li
|
||||
class="breadcrumb-item"
|
||||
:class="{ active: index === list.length - 1 }"
|
||||
v-for="(item, index) in list"
|
||||
:key="item.menuId"
|
||||
>
|
||||
<a>{{ getLabel(item) }}</a>
|
||||
<!-- <a :href="'' !== item.menuUrl && item.menuUrl ? item.menuUrl : 'javascript:'">{{item.localeLabel}}</a> -->
|
||||
<!-- <router-link v-if="index === list.length - 1" :to="item.menuUrl" tag="span">{{item.localeLabel}}</router-link>
|
||||
<a :href="'' !== item.menuUrl && item.menuUrl ? item.menuUrl : 'javascript:'" v-else>{{item.localeLabel}}</a> -->
|
||||
</li>
|
||||
|
||||
<!-- <li class="breadcrumb-item"><a href="#">Menu1</a></li>
|
||||
<li class="breadcrumb-item"><a href="#">Submenu1</a></li>
|
||||
<li class="breadcrumb-item active" aria-current="page">Submenu1-1</li> -->
|
||||
</ol>
|
||||
</nav>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapActions, mapGetters } from 'vuex';
|
||||
import _ from 'lodash';
|
||||
import SDLLocale from '@/mixin/SDLLocale';
|
||||
|
||||
export default {
|
||||
name: 'Breadcrumb',
|
||||
mixins: [SDLLocale],
|
||||
props: {
|
||||
home: {
|
||||
required: false,
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
homeText: {
|
||||
required: false,
|
||||
type: String,
|
||||
default: 'Home',
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
title: '',
|
||||
list: [],
|
||||
isUserMenu: false,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters({
|
||||
menus: 'getMenus',
|
||||
myMenus: 'getMyMenus',
|
||||
}),
|
||||
menu() {
|
||||
return _.find(this.menus, ['menuId', this.menuId]);
|
||||
},
|
||||
menuId() {
|
||||
return this.$route.meta.menuId;
|
||||
},
|
||||
pageId() {
|
||||
return this.$route.meta.pageId;
|
||||
},
|
||||
tooltipMessage() {
|
||||
return this.isUserMenu ? 'Delete My Menu' : 'Add My Menu';
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
menus: {
|
||||
handler() {
|
||||
this.setTitle();
|
||||
this.setBreadcrumb();
|
||||
},
|
||||
deep: true
|
||||
},
|
||||
myMenus: {
|
||||
handler() {
|
||||
this.setTitle();
|
||||
this.setBreadcrumb();
|
||||
},
|
||||
deep: true
|
||||
},
|
||||
locale: {
|
||||
handler() {
|
||||
this.setTitle();
|
||||
this.setBreadcrumb();
|
||||
},
|
||||
deep: true
|
||||
},
|
||||
$route: {
|
||||
handler() {
|
||||
this.setTitle();
|
||||
this.setBreadcrumb();
|
||||
},
|
||||
deep: true
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
...mapActions(['addMyMenu', 'deleteMyMenu']),
|
||||
getParentMenu(parentId) {
|
||||
return this.menus.find(item => item.menuId === parentId);
|
||||
},
|
||||
setTitle() {
|
||||
if (this.menu) {
|
||||
this.title = this.getLabel(this.menu);
|
||||
// this.title = this.menu.localeLabel;
|
||||
|
||||
this.isUserMenu = this.myMenus.some(myMenu => myMenu.menuId === this.menu.menuId);
|
||||
}
|
||||
},
|
||||
setBreadcrumb() {
|
||||
if (this.menu) {
|
||||
this.list = [this.menu];
|
||||
for (let i = this.menu.menuLevel - 1; i > 0; i -= 1) {
|
||||
const parentMenu = this.getParentMenu(this.list[0].parentId);
|
||||
if (parentMenu) {
|
||||
this.list = [parentMenu, ...this.list];
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.setTitle();
|
||||
this.setBreadcrumb();
|
||||
},
|
||||
mounted() {
|
||||
const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]');
|
||||
const tooltipList = [...tooltipTriggerList].map(tooltipTriggerEl => new bootstrap.Tooltip(
|
||||
tooltipTriggerEl,
|
||||
{
|
||||
trigger: 'hover',
|
||||
}
|
||||
));
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
@@ -0,0 +1,3 @@
|
||||
import SdlBreadcrumb from './Breadcrumb.vue';
|
||||
|
||||
export default SdlBreadcrumb;
|
||||
@@ -0,0 +1,471 @@
|
||||
<!--
|
||||
Program: Date Picker
|
||||
Author: SDL
|
||||
Brief: Date Picker Component
|
||||
-->
|
||||
<template>
|
||||
<div class="d-inline-flex">
|
||||
<template v-if="lazyLoad">
|
||||
<datepicker
|
||||
v-bind="$attrs"
|
||||
v-if="!period"
|
||||
v-model="date"
|
||||
:ref="refKey"
|
||||
:bootstrap-styling="true"
|
||||
:wrapper-class="wrapClass"
|
||||
:language="currentLang"
|
||||
:format="customFormatter"
|
||||
:minimum-view="minimumView"
|
||||
:disabled-dates="disabledDate"
|
||||
:disabled="disabled"
|
||||
@input="updateDate"
|
||||
>
|
||||
<template #afterDateInput>
|
||||
<button class="btn btn-outline-secondary" type="button" :data-ref="refKey" :disabled="disabled" @click="openPicker(refKey)" v-click-outside="outsideClick">
|
||||
<i class="bi bi-calendar"></i>
|
||||
</button>
|
||||
</template>
|
||||
</datepicker>
|
||||
<template v-else>
|
||||
<datepicker
|
||||
v-model="startDate"
|
||||
:ref="startRefKey"
|
||||
:bootstrap-styling="true"
|
||||
:wrapper-class="wrapClass"
|
||||
:language="currentLang"
|
||||
:format="customFormatter"
|
||||
:minimum-view="minimumView"
|
||||
:disabled-dates="startDisabledDates"
|
||||
:disabled="startDisabled || disabled"
|
||||
:placeholder="startPlaceHolder"
|
||||
@input="updateStartDate"
|
||||
>
|
||||
<template #afterDateInput>
|
||||
<button class="btn btn-outline-secondary" type="button" :data-ref="startRefKey" :disabled="startDisabled || disabled" @click="openPicker(startRefKey)" v-click-outside="outsideClick">
|
||||
<i class="bi bi-calendar"></i>
|
||||
</button>
|
||||
</template>
|
||||
</datepicker>
|
||||
<span class="ms-2 me-2 pt-2"> ~ </span>
|
||||
<datepicker
|
||||
v-model="endDate"
|
||||
:ref="endRefKey"
|
||||
:bootstrap-styling="true"
|
||||
:wrapper-class="wrapClass"
|
||||
:language="currentLang"
|
||||
:format="customFormatter"
|
||||
:minimum-view="minimumView"
|
||||
:disabled-dates="endDisabledDates"
|
||||
:disabled="endDisabled || disabled"
|
||||
:placeholder="endPlaceHolder"
|
||||
@input="updateEndDate"
|
||||
>
|
||||
<template #afterDateInput>
|
||||
<button class="btn btn-outline-secondary" type="button" :data-ref="endRefKey" :disabled="endDisabled || disabled" @click="openPicker(endRefKey)" v-click-outside="outsideClick">
|
||||
<i class="bi bi-calendar"></i>
|
||||
</button>
|
||||
</template>
|
||||
</datepicker>
|
||||
</template>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import Datepicker from './components/Datepicker.vue';
|
||||
import { mapGetters } from 'vuex';
|
||||
import moment from 'moment';
|
||||
import _ from 'lodash';
|
||||
import SDLUtil from '@/utils/SDLUtil';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Datepicker,
|
||||
},
|
||||
emits: ['input', 'moment', 'update:modelValue'],
|
||||
props: {
|
||||
value: {
|
||||
type: [String, Object],
|
||||
},
|
||||
modelValue: {
|
||||
type: [String, Object],
|
||||
},
|
||||
wrapClass: {
|
||||
required: false,
|
||||
type: String,
|
||||
default: 'wd150',
|
||||
},
|
||||
// period information
|
||||
period: {
|
||||
required: false,
|
||||
type: Object,
|
||||
},
|
||||
// minimum unit - ex) When setting to month, picker can be selected on a monthly basis
|
||||
minimumView: {
|
||||
required: false,
|
||||
type: String,
|
||||
default: 'day',
|
||||
},
|
||||
default: {
|
||||
required: false,
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
disabled: {
|
||||
required: false,
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
disabledDate: {
|
||||
required: false,
|
||||
type: Object,
|
||||
default: () => {},
|
||||
},
|
||||
startDisabledDate: {
|
||||
required: false,
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
endDisabledDate: {
|
||||
required: false,
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
refKey: _.uniqueId('dp_'),
|
||||
startRefKey: _.uniqueId('start_dp_'),
|
||||
endRefKey: _.uniqueId('end_dp_'),
|
||||
lazyLoad: false,
|
||||
date: '',
|
||||
startDate: '',
|
||||
endDate: '',
|
||||
currentLang: null,
|
||||
lang: {},
|
||||
};
|
||||
},
|
||||
created() {
|
||||
import('./locale/').then(data => {
|
||||
this.$i18n.availableLocales.forEach(countryCode => {
|
||||
const country = countryCode.replace('_', '-').split('-')[0];
|
||||
this.lang[countryCode] = data[country];
|
||||
});
|
||||
// Applying the language currently set
|
||||
this.currentLang = this.lang[this.$i18n.locale];
|
||||
// period information initialization
|
||||
if (this.period) {
|
||||
this.startDate = _.has(this.modelValue, 'startDate') ? moment(this.modelValue.startDate).format(this.standardFormat) : moment().subtract(1, 'M').format(this.standardFormat);
|
||||
this.endDate = _.has(this.modelValue, 'endDate') ? moment(this.modelValue.endDate).format(this.standardFormat) : moment().format(this.standardFormat);
|
||||
this.updatePeriod();
|
||||
} else {
|
||||
this.date = this.modelValue ? this.modelValue : moment().format(this.standardFormat);
|
||||
this.updateDate(this.date);
|
||||
}
|
||||
this.lazyLoad = true;
|
||||
});
|
||||
},
|
||||
computed: {
|
||||
...mapGetters({
|
||||
locale: 'getLocale',
|
||||
}),
|
||||
// Start Date Zone Disabled Settings
|
||||
startDisabled() {
|
||||
return this.period.startDisabled;
|
||||
},
|
||||
// End Date Zone Disabled Settings
|
||||
endDisabled() {
|
||||
return this.period.endDisabled;
|
||||
},
|
||||
// Start date range settings.
|
||||
startDisabledDates() {
|
||||
return {
|
||||
to: moment(this.startDisabledDate).toDate(),
|
||||
from: moment(this.endDate).parseZone().toDate(),
|
||||
};
|
||||
},
|
||||
// End date range settings.
|
||||
endDisabledDates() {
|
||||
return {
|
||||
to: moment(this.startDate).toDate(),
|
||||
};
|
||||
},
|
||||
// Start Date PlaceHolder
|
||||
startPlaceHolder() {
|
||||
return this.period.startPlaceHolder || '';
|
||||
},
|
||||
// End Date PlaceHolder
|
||||
endPlaceHolder() {
|
||||
return this.period.endPlaceHolder || '';
|
||||
},
|
||||
// Date Format by Locale
|
||||
dateFormat() {
|
||||
return this.minimumView === 'month' ? SDLUtil.getMsgProp('data-format.date.ym') : SDLUtil.getMsgProp('data-format.date.ymd');
|
||||
},
|
||||
standardFormat() {
|
||||
return this.minimumView === 'month' ? 'YYYY-MM' : 'YYYY-MM-DD';
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
// Change of date unit
|
||||
minimumView() {
|
||||
if (this.minimumView === 'day') {
|
||||
this.startDate = moment().subtract(1, 'M').format(this.standardFormat);
|
||||
this.endDate = moment().format(this.standardFormat);
|
||||
}
|
||||
this.updatePeriod();
|
||||
},
|
||||
// locale Change
|
||||
locale() {
|
||||
this.currentLang = this.lang[this.locale];
|
||||
},
|
||||
modelValue() {
|
||||
if (this.period) {
|
||||
this.startDate = _.has(this.modelValue, 'startDate') ? moment(this.modelValue.startDate).format(this.standardFormat) : moment().subtract(1, 'M').format(this.standardFormat);
|
||||
this.endDate = _.has(this.modelValue, 'endDate') ? moment(this.modelValue.endDate).format(this.standardFormat) : moment().format(this.standardFormat);
|
||||
} else {
|
||||
this.date = this.modelValue ? this.modelValue : moment().format(this.standardFormat);
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* param: param -> Object
|
||||
* return: void
|
||||
* exception:
|
||||
* description : Close when clicking outside the calendar button area
|
||||
*/
|
||||
outsideClick(param) {
|
||||
if (_.find(param.evt.path, ['className', 'vdp-datepicker__calendar'])) {
|
||||
return;
|
||||
}
|
||||
if (
|
||||
param.evt.target.className.indexOf('prev') > -1 ||
|
||||
param.evt.target.className.indexOf('next') > -1 ||
|
||||
param.evt.target.className.indexOf('day__month_btn') > -1 ||
|
||||
param.evt.target.className.indexOf('month__year_btn') > -1 ||
|
||||
(param.evt.target.nodeName === 'SPAN' && param.evt.target.parentNode.nodeName === 'HEADER')
|
||||
) {
|
||||
return;
|
||||
}
|
||||
const calendar = this.$refs[param.source.dataset.ref];
|
||||
if (calendar.isOpen) calendar.close(true);
|
||||
},
|
||||
/**
|
||||
* param: date -> Date
|
||||
* return: String
|
||||
* exception:
|
||||
* description : Return to set date format String
|
||||
*/
|
||||
customFormatter(date = moment()) {
|
||||
return moment(date).format(this.dateFormat);
|
||||
},
|
||||
standardFormatter(date = moment()) {
|
||||
return moment(date).format(this.standardFormat);
|
||||
},
|
||||
/**
|
||||
* param: refKey -> String
|
||||
* return: String
|
||||
* exception:
|
||||
* description : Activate the calendar by clicking the calendar button
|
||||
*/
|
||||
openPicker(refKey) {
|
||||
this.$refs[refKey].showCalendar();
|
||||
},
|
||||
/**
|
||||
* param: value -> Date
|
||||
* return: String
|
||||
* exception:
|
||||
* description : When changing value updates.
|
||||
*/
|
||||
updateDate(value) {
|
||||
this.$emit('input', moment(value).format(this.dateFormat));
|
||||
this.$emit('update:modelValue', moment(value).format(this.dateFormat));
|
||||
this.$emit('moment', moment(value));
|
||||
},
|
||||
/**
|
||||
* param: value -> Date
|
||||
* return: String
|
||||
* exception:
|
||||
* description : Start date change
|
||||
*/
|
||||
updateStartDate(value) {
|
||||
this.startDate = value;
|
||||
this.updatePeriod();
|
||||
},
|
||||
/**
|
||||
* param: value -> Date
|
||||
* return: String
|
||||
* exception:
|
||||
* description : End date change
|
||||
*/
|
||||
updateEndDate(value) {
|
||||
this.endDate = value;
|
||||
this.updatePeriod();
|
||||
},
|
||||
/**
|
||||
* param:
|
||||
* return: String
|
||||
* exception:
|
||||
* description : When changing the period updates
|
||||
*/
|
||||
updatePeriod() {
|
||||
this.$emit('input', {
|
||||
startDate: this.standardFormatter(this.startDate),
|
||||
endDate: this.standardFormatter(this.endDate),
|
||||
});
|
||||
this.$emit('update:modelValue', {
|
||||
startDate: this.standardFormatter(this.startDate),
|
||||
endDate: this.standardFormatter(this.endDate),
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.wd150 {
|
||||
width: 150px !important;
|
||||
}
|
||||
.vdp-datepicker__calendar {
|
||||
z-index: 118 !important;
|
||||
}
|
||||
.rtl {
|
||||
direction: rtl;
|
||||
}
|
||||
.vdp-datepicker {
|
||||
position: relative;
|
||||
text-align: left;
|
||||
}
|
||||
.vdp-datepicker * {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.vdp-datepicker__calendar {
|
||||
position: absolute;
|
||||
z-index: 100;
|
||||
background: #fff;
|
||||
width: 300px;
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
.vdp-datepicker__calendar header {
|
||||
display: block;
|
||||
line-height: 40px;
|
||||
}
|
||||
.vdp-datepicker__calendar header span {
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
width: 71.42857142857143%;
|
||||
float: left;
|
||||
}
|
||||
.vdp-datepicker__calendar header .prev,
|
||||
.vdp-datepicker__calendar header .next {
|
||||
width: 14.285714285714286%;
|
||||
float: left;
|
||||
text-indent: -10000px;
|
||||
position: relative;
|
||||
}
|
||||
.vdp-datepicker__calendar header .prev:after,
|
||||
.vdp-datepicker__calendar header .next:after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
transform: translateX(-50%) translateY(-50%);
|
||||
border: 6px solid transparent;
|
||||
}
|
||||
.vdp-datepicker__calendar header .prev:after {
|
||||
border-right: 10px solid #000;
|
||||
margin-left: -5px;
|
||||
}
|
||||
.vdp-datepicker__calendar header .prev.disabled:after {
|
||||
border-right: 10px solid #ddd;
|
||||
}
|
||||
.vdp-datepicker__calendar header .next:after {
|
||||
border-left: 10px solid #000;
|
||||
margin-left: 5px;
|
||||
}
|
||||
.vdp-datepicker__calendar header .next.disabled:after {
|
||||
border-left: 10px solid #ddd;
|
||||
}
|
||||
.vdp-datepicker__calendar header .prev:not(.disabled),
|
||||
.vdp-datepicker__calendar header .next:not(.disabled),
|
||||
.vdp-datepicker__calendar header .up:not(.disabled) {
|
||||
cursor: pointer;
|
||||
}
|
||||
.vdp-datepicker__calendar header .prev:not(.disabled):hover,
|
||||
.vdp-datepicker__calendar header .next:not(.disabled):hover,
|
||||
.vdp-datepicker__calendar header .up:not(.disabled):hover {
|
||||
background: #eee;
|
||||
}
|
||||
.vdp-datepicker__calendar .disabled {
|
||||
color: #ddd;
|
||||
cursor: default;
|
||||
}
|
||||
.vdp-datepicker__calendar .flex-rtl {
|
||||
display: flex;
|
||||
width: inherit;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.vdp-datepicker__calendar .cell {
|
||||
display: inline-block;
|
||||
padding: 0 5px;
|
||||
width: 14.285714285714286%;
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
.vdp-datepicker__calendar .cell:not(.blank):not(.disabled).day,
|
||||
.vdp-datepicker__calendar .cell:not(.blank):not(.disabled).month,
|
||||
.vdp-datepicker__calendar .cell:not(.blank):not(.disabled).year {
|
||||
cursor: pointer;
|
||||
}
|
||||
.vdp-datepicker__calendar .cell:not(.blank):not(.disabled).day:hover,
|
||||
.vdp-datepicker__calendar .cell:not(.blank):not(.disabled).month:hover,
|
||||
.vdp-datepicker__calendar .cell:not(.blank):not(.disabled).year:hover {
|
||||
border: 1px solid #4bd;
|
||||
}
|
||||
.vdp-datepicker__calendar .cell.selected {
|
||||
background: #4bd;
|
||||
}
|
||||
.vdp-datepicker__calendar .cell.selected:hover {
|
||||
background: #4bd;
|
||||
}
|
||||
.vdp-datepicker__calendar .cell.selected.highlighted {
|
||||
background: #4bd;
|
||||
}
|
||||
.vdp-datepicker__calendar .cell.highlighted {
|
||||
background: #cae5ed;
|
||||
}
|
||||
.vdp-datepicker__calendar .cell.highlighted.disabled {
|
||||
color: #a3a3a3;
|
||||
}
|
||||
.vdp-datepicker__calendar .cell.grey {
|
||||
color: #888;
|
||||
}
|
||||
.vdp-datepicker__calendar .cell.grey:hover {
|
||||
background: inherit;
|
||||
}
|
||||
.vdp-datepicker__calendar .cell.day-header {
|
||||
font-size: 85%;
|
||||
white-space: nowrap;
|
||||
cursor: inherit;
|
||||
}
|
||||
.vdp-datepicker__calendar .cell.day-header:hover {
|
||||
background: inherit;
|
||||
}
|
||||
.vdp-datepicker__calendar .month,
|
||||
.vdp-datepicker__calendar .year {
|
||||
width: 33.333%;
|
||||
}
|
||||
.vdp-datepicker__clear-button,
|
||||
.vdp-datepicker__calendar-button {
|
||||
cursor: pointer;
|
||||
font-style: normal;
|
||||
}
|
||||
.vdp-datepicker__clear-button.disabled,
|
||||
.vdp-datepicker__calendar-button.disabled {
|
||||
color: #999;
|
||||
cursor: default;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,158 @@
|
||||
<template>
|
||||
<div :class="{'input-group' : bootstrapStyling}">
|
||||
<!-- Calendar Button -->
|
||||
<span v-if="calendarButton" class="vdp-datepicker__calendar-button" :class="{'input-group-prepend' : bootstrapStyling}" @click="showCalendar" v-bind:style="{'cursor:not-allowed;' : disabled}">
|
||||
<span :class="{'input-group-text' : bootstrapStyling}">
|
||||
<i :class="calendarButtonIcon">
|
||||
{{ calendarButtonIconContent }}
|
||||
<span v-if="!calendarButtonIcon">…</span>
|
||||
</i>
|
||||
</span>
|
||||
</span>
|
||||
<!-- Input -->
|
||||
<input
|
||||
:type="inline ? 'hidden' : 'text'"
|
||||
:class="computedInputClass"
|
||||
:name="name"
|
||||
:ref="refName"
|
||||
:id="id"
|
||||
:value="formattedValue"
|
||||
:open-date="openDate"
|
||||
:placeholder="placeholder"
|
||||
:clear-button="clearButton"
|
||||
:disabled="disabled"
|
||||
:required="required"
|
||||
:readonly="!typeable"
|
||||
@click="showCalendar"
|
||||
@keyup="parseTypedDate"
|
||||
@blur="inputBlurred"
|
||||
autocomplete="off">
|
||||
<!-- Clear Button -->
|
||||
<span v-if="clearButton && selectedDate" class="vdp-datepicker__clear-button" :class="{'input-group-append' : bootstrapStyling}" @click="clearDate()">
|
||||
<span :class="{'input-group-text' : bootstrapStyling}">
|
||||
<i :class="clearButtonIcon">
|
||||
<span v-if="!clearButtonIcon">×</span>
|
||||
</i>
|
||||
</span>
|
||||
</span>
|
||||
<slot name="afterDateInput"></slot>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { makeDateUtils } from '../utils/DateUtils'
|
||||
export default {
|
||||
props: {
|
||||
selectedDate: Date,
|
||||
resetTypedDate: [Date],
|
||||
format: [String, Function],
|
||||
translation: Object,
|
||||
inline: Boolean,
|
||||
id: String,
|
||||
name: String,
|
||||
refName: String,
|
||||
openDate: Date,
|
||||
placeholder: String,
|
||||
inputClass: [String, Object, Array],
|
||||
clearButton: {
|
||||
type: Boolean,
|
||||
default: () => undefined,
|
||||
},
|
||||
clearButtonIcon: String,
|
||||
calendarButton: Boolean,
|
||||
calendarButtonIcon: String,
|
||||
calendarButtonIconContent: String,
|
||||
disabled: Boolean,
|
||||
required: Boolean,
|
||||
typeable: Boolean,
|
||||
bootstrapStyling: Boolean,
|
||||
useUtc: Boolean
|
||||
},
|
||||
emits: ['showCalendar', 'typedDate', 'closeCalendar', 'clearDate'],
|
||||
data () {
|
||||
const constructedDateUtils = makeDateUtils(this.useUtc)
|
||||
return {
|
||||
input: null,
|
||||
typedDate: false,
|
||||
utils: constructedDateUtils
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
formattedValue () {
|
||||
if (!this.selectedDate) {
|
||||
return null
|
||||
}
|
||||
if (this.typedDate) {
|
||||
return this.typedDate
|
||||
}
|
||||
return typeof this.format === 'function'
|
||||
? this.format(this.selectedDate)
|
||||
: this.utils.formatDate(new Date(this.selectedDate), this.format, this.translation)
|
||||
},
|
||||
|
||||
computedInputClass () {
|
||||
if (this.bootstrapStyling) {
|
||||
if (typeof this.inputClass === 'string') {
|
||||
return [this.inputClass, 'form-control'].join(' ')
|
||||
}
|
||||
return {'form-control': true, ...this.inputClass}
|
||||
}
|
||||
return this.inputClass
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
resetTypedDate () {
|
||||
this.typedDate = false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
showCalendar () {
|
||||
this.$emit('showCalendar')
|
||||
},
|
||||
/**
|
||||
* Attempt to parse a typed date
|
||||
* @param {Event} event
|
||||
*/
|
||||
parseTypedDate (event) {
|
||||
// close calendar if escape or enter are pressed
|
||||
if ([
|
||||
27, // escape
|
||||
13 // enter
|
||||
].includes(event.keyCode)) {
|
||||
this.input.blur()
|
||||
}
|
||||
|
||||
if (this.typeable) {
|
||||
const typedDate = Date.parse(this.input.value)
|
||||
if (!isNaN(typedDate)) {
|
||||
this.typedDate = this.input.value
|
||||
this.$emit('typedDate', new Date(this.typedDate))
|
||||
}
|
||||
}
|
||||
},
|
||||
/**
|
||||
* nullify the typed date to defer to regular formatting
|
||||
* called once the input is blurred
|
||||
*/
|
||||
inputBlurred () {
|
||||
if (this.typeable && isNaN(Date.parse(this.input.value))) {
|
||||
this.clearDate()
|
||||
this.input.value = null
|
||||
this.typedDate = null
|
||||
}
|
||||
|
||||
this.$emit('closeCalendar')
|
||||
},
|
||||
/**
|
||||
* emit a clearDate event
|
||||
*/
|
||||
clearDate () {
|
||||
this.$emit('clearDate')
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
this.input = this.$el.querySelector('input')
|
||||
}
|
||||
}
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
</script>
|
||||
@@ -0,0 +1,479 @@
|
||||
<template>
|
||||
<div class="vdp-datepicker" :class="[wrapperClass, isRtl ? 'rtl' : '']">
|
||||
<date-input
|
||||
:selectedDate="selectedDate"
|
||||
:resetTypedDate="resetTypedDate"
|
||||
:format="format"
|
||||
:translation="translation"
|
||||
:inline="inline"
|
||||
:id="id"
|
||||
:name="name"
|
||||
:refName="refName"
|
||||
:openDate="openDate"
|
||||
:placeholder="placeholder"
|
||||
:inputClass="inputClass"
|
||||
:typeable="typeable"
|
||||
:clearButton="clearButton"
|
||||
:clearButtonIcon="clearButtonIcon"
|
||||
:calendarButton="calendarButton"
|
||||
:calendarButtonIcon="calendarButtonIcon"
|
||||
:calendarButtonIconContent="calendarButtonIconContent"
|
||||
:disabled="disabled"
|
||||
:required="required"
|
||||
:bootstrapStyling="bootstrapStyling"
|
||||
:use-utc="useUtc"
|
||||
@showCalendar="showCalendar"
|
||||
@closeCalendar="close"
|
||||
@typedDate="setTypedDate"
|
||||
@clearDate="clearDate">
|
||||
<template #afterDateInput>
|
||||
<slot name="afterDateInput"></slot>
|
||||
</template>
|
||||
</date-input>
|
||||
|
||||
|
||||
<!-- Day View -->
|
||||
<picker-day
|
||||
v-if="allowedToShowView('day')"
|
||||
:pageDate="pageDate"
|
||||
:selectedDate="selectedDate"
|
||||
:showDayView="showDayView"
|
||||
:fullMonthName="fullMonthName"
|
||||
:allowedToShowView="allowedToShowView"
|
||||
:disabledDates="disabledDates"
|
||||
:highlighted="highlighted"
|
||||
:calendarClass="calendarClass"
|
||||
:calendarStyle="calendarStyle"
|
||||
:translation="translation"
|
||||
:pageTimestamp="pageTimestamp"
|
||||
:isRtl="isRtl"
|
||||
:mondayFirst="mondayFirst"
|
||||
:dayCellContent="dayCellContent"
|
||||
:use-utc="useUtc"
|
||||
@changedMonth="handleChangedMonthFromDayPicker"
|
||||
@selectDate="selectDate"
|
||||
@showMonthCalendar="showMonthCalendar"
|
||||
@selectedDisabled="selectDisabledDate">
|
||||
<template #beforeCalendarHeader>
|
||||
<slot name="beforeCalendarHeader"></slot>
|
||||
</template>
|
||||
</picker-day>
|
||||
|
||||
<!-- Month View -->
|
||||
<picker-month
|
||||
v-if="allowedToShowView('month')"
|
||||
:pageDate="pageDate"
|
||||
:selectedDate="selectedDate"
|
||||
:showMonthView="showMonthView"
|
||||
:allowedToShowView="allowedToShowView"
|
||||
:disabledDates="disabledDates"
|
||||
:calendarClass="calendarClass"
|
||||
:calendarStyle="calendarStyle"
|
||||
:translation="translation"
|
||||
:isRtl="isRtl"
|
||||
:use-utc="useUtc"
|
||||
@selectMonth="selectMonth"
|
||||
@showYearCalendar="showYearCalendar"
|
||||
@changedYear="setPageDate">
|
||||
<template #beforeCalendarHeader>
|
||||
<slot name="beforeCalendarHeader"></slot>
|
||||
</template>
|
||||
</picker-month>
|
||||
|
||||
<!-- Year View -->
|
||||
<picker-year
|
||||
v-if="allowedToShowView('year')"
|
||||
:pageDate="pageDate"
|
||||
:selectedDate="selectedDate"
|
||||
:showYearView="showYearView"
|
||||
:allowedToShowView="allowedToShowView"
|
||||
:disabledDates="disabledDates"
|
||||
:calendarClass="calendarClass"
|
||||
:calendarStyle="calendarStyle"
|
||||
:translation="translation"
|
||||
:isRtl="isRtl"
|
||||
:use-utc="useUtc"
|
||||
@selectYear="selectYear"
|
||||
@changedDecade="setPageDate">
|
||||
<template #beforeCalendarHeader>
|
||||
<slot name="beforeCalendarHeader"></slot>
|
||||
</template>
|
||||
</picker-year>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import en from '../locale/translations/en'
|
||||
import DateInput from './DateInput.vue'
|
||||
import PickerDay from './PickerDay.vue'
|
||||
import PickerMonth from './PickerMonth.vue'
|
||||
import PickerYear from './PickerYear.vue'
|
||||
import utils, { makeDateUtils } from '../utils/DateUtils'
|
||||
export default {
|
||||
components: {
|
||||
DateInput,
|
||||
PickerDay,
|
||||
PickerMonth,
|
||||
PickerYear
|
||||
},
|
||||
emits: ['closed', 'selected', 'input', 'update:modelValue', 'changedMonth', 'cleared', 'selectedDisabled', 'changedYear'],
|
||||
props: {
|
||||
value: {
|
||||
validator: val => utils.validateDateInput(val)
|
||||
},
|
||||
modelValue: {
|
||||
validator: val => utils.validateDateInput(val)
|
||||
},
|
||||
name: String,
|
||||
refName: String,
|
||||
id: String,
|
||||
format: {
|
||||
type: [String, Function],
|
||||
default: 'dd MMM yyyy'
|
||||
},
|
||||
language: {
|
||||
type: Object,
|
||||
default: () => en
|
||||
},
|
||||
openDate: {
|
||||
validator: val => utils.validateDateInput(val)
|
||||
},
|
||||
dayCellContent: Function,
|
||||
fullMonthName: Boolean,
|
||||
disabledDates: Object,
|
||||
highlighted: Object,
|
||||
placeholder: String,
|
||||
inline: Boolean,
|
||||
calendarClass: [String, Object, Array],
|
||||
inputClass: [String, Object, Array],
|
||||
wrapperClass: [String, Object, Array],
|
||||
mondayFirst: Boolean,
|
||||
clearButton: {
|
||||
type: Boolean,
|
||||
default: () => undefined,
|
||||
},
|
||||
clearButtonIcon: String,
|
||||
calendarButton: Boolean,
|
||||
calendarButtonIcon: String,
|
||||
calendarButtonIconContent: String,
|
||||
bootstrapStyling: Boolean,
|
||||
initialView: String,
|
||||
disabled: Boolean,
|
||||
required: Boolean,
|
||||
typeable: Boolean,
|
||||
useUtc: Boolean,
|
||||
minimumView: {
|
||||
type: String,
|
||||
default: 'day'
|
||||
},
|
||||
maximumView: {
|
||||
type: String,
|
||||
default: 'year'
|
||||
}
|
||||
},
|
||||
data () {
|
||||
const startDate = this.openDate ? new Date(this.openDate) : new Date()
|
||||
const constructedDateUtils = makeDateUtils(this.useUtc)
|
||||
const pageTimestamp = constructedDateUtils.setDate(startDate, 1)
|
||||
return {
|
||||
/*
|
||||
* Vue cannot observe changes to a Date Object so date must be stored as a timestamp
|
||||
* This represents the first day of the current viewing month
|
||||
* {Number}
|
||||
*/
|
||||
pageTimestamp,
|
||||
/*
|
||||
* Selected Date
|
||||
* {Date}
|
||||
*/
|
||||
selectedDate: null,
|
||||
/*
|
||||
* Flags to show calendar views
|
||||
* {Boolean}
|
||||
*/
|
||||
showDayView: false,
|
||||
showMonthView: false,
|
||||
showYearView: false,
|
||||
/*
|
||||
* Positioning
|
||||
*/
|
||||
calendarHeight: 0,
|
||||
resetTypedDate: new Date(),
|
||||
utils: constructedDateUtils
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value (value) {
|
||||
this.setValue(value)
|
||||
},
|
||||
openDate () {
|
||||
this.setPageDate()
|
||||
},
|
||||
initialView () {
|
||||
this.setInitialView()
|
||||
},
|
||||
modelValue(value) {
|
||||
this.setValue(value);
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
computedInitialView () {
|
||||
if (!this.initialView) {
|
||||
return this.minimumView
|
||||
}
|
||||
|
||||
return this.initialView
|
||||
},
|
||||
pageDate () {
|
||||
return new Date(this.pageTimestamp)
|
||||
},
|
||||
|
||||
translation () {
|
||||
return this.language
|
||||
},
|
||||
|
||||
calendarStyle () {
|
||||
return {
|
||||
position: this.isInline ? 'static' : undefined
|
||||
}
|
||||
},
|
||||
isOpen () {
|
||||
return this.showDayView || this.showMonthView || this.showYearView
|
||||
},
|
||||
isInline () {
|
||||
return !!this.inline
|
||||
},
|
||||
isRtl () {
|
||||
return this.translation.rtl === true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* Called in the event that the user navigates to date pages and
|
||||
* closes the picker without selecting a date.
|
||||
*/
|
||||
resetDefaultPageDate () {
|
||||
if (this.selectedDate === null) {
|
||||
this.setPageDate()
|
||||
return
|
||||
}
|
||||
this.setPageDate(this.selectedDate)
|
||||
},
|
||||
/**
|
||||
* Effectively a toggle to show/hide the calendar
|
||||
* @return {mixed}
|
||||
*/
|
||||
showCalendar () {
|
||||
if (this.disabled || this.isInline) {
|
||||
return false
|
||||
}
|
||||
if (this.isOpen) {
|
||||
return this.close(true)
|
||||
}
|
||||
this.setInitialView()
|
||||
},
|
||||
/**
|
||||
* Sets the initial picker page view: day, month or year
|
||||
*/
|
||||
setInitialView () {
|
||||
const initialView = this.computedInitialView
|
||||
if (!this.allowedToShowView(initialView)) {
|
||||
throw new Error(`initialView '${this.initialView}' cannot be rendered based on minimum '${this.minimumView}' and maximum '${this.maximumView}'`)
|
||||
}
|
||||
switch (initialView) {
|
||||
case 'year':
|
||||
this.showYearCalendar()
|
||||
break
|
||||
case 'month':
|
||||
this.showMonthCalendar()
|
||||
break
|
||||
default:
|
||||
this.showDayCalendar()
|
||||
break
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Are we allowed to show a specific picker view?
|
||||
* @param {String} view
|
||||
* @return {Boolean}
|
||||
*/
|
||||
allowedToShowView (view) {
|
||||
const views = ['day', 'month', 'year']
|
||||
const minimumViewIndex = views.indexOf(this.minimumView)
|
||||
const maximumViewIndex = views.indexOf(this.maximumView)
|
||||
const viewIndex = views.indexOf(view)
|
||||
|
||||
return viewIndex >= minimumViewIndex && viewIndex <= maximumViewIndex
|
||||
},
|
||||
/**
|
||||
* Show the day picker
|
||||
* @return {Boolean}
|
||||
*/
|
||||
showDayCalendar () {
|
||||
if (!this.allowedToShowView('day')) {
|
||||
return false
|
||||
}
|
||||
this.close()
|
||||
this.showDayView = true
|
||||
return true
|
||||
},
|
||||
/**
|
||||
* Show the month picker
|
||||
* @return {Boolean}
|
||||
*/
|
||||
showMonthCalendar () {
|
||||
if (!this.allowedToShowView('month')) {
|
||||
return false
|
||||
}
|
||||
this.close()
|
||||
this.showMonthView = true
|
||||
return true
|
||||
},
|
||||
/**
|
||||
* Show the year picker
|
||||
* @return {Boolean}
|
||||
*/
|
||||
showYearCalendar () {
|
||||
if (!this.allowedToShowView('year')) {
|
||||
return false
|
||||
}
|
||||
this.close()
|
||||
this.showYearView = true
|
||||
return true
|
||||
},
|
||||
/**
|
||||
* Set the selected date
|
||||
* @param {Number} timestamp
|
||||
*/
|
||||
setDate (timestamp) {
|
||||
const date = new Date(timestamp)
|
||||
this.selectedDate = date
|
||||
this.setPageDate(date)
|
||||
this.$emit('selected', date)
|
||||
this.$emit('input', date)
|
||||
this.$emit('update:modelValue', date)
|
||||
},
|
||||
/**
|
||||
* Clear the selected date
|
||||
*/
|
||||
clearDate () {
|
||||
this.selectedDate = null
|
||||
this.setPageDate()
|
||||
this.$emit('selected', null)
|
||||
this.$emit('input', null)
|
||||
this.$emit('cleared')
|
||||
},
|
||||
/**
|
||||
* @param {Object} date
|
||||
*/
|
||||
selectDate (date) {
|
||||
this.setDate(date.timestamp)
|
||||
if (!this.isInline) {
|
||||
this.close(true)
|
||||
}
|
||||
this.resetTypedDate = new Date()
|
||||
},
|
||||
/**
|
||||
* @param {Object} date
|
||||
*/
|
||||
selectDisabledDate (date) {
|
||||
this.$emit('selectedDisabled', date)
|
||||
},
|
||||
/**
|
||||
* @param {Object} month
|
||||
*/
|
||||
selectMonth (month) {
|
||||
const date = new Date(month.timestamp)
|
||||
if (this.allowedToShowView('day')) {
|
||||
this.setPageDate(date)
|
||||
this.$emit('changedMonth', month)
|
||||
this.showDayCalendar()
|
||||
} else {
|
||||
this.selectDate(month)
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @param {Object} year
|
||||
*/
|
||||
selectYear (year) {
|
||||
const date = new Date(year.timestamp)
|
||||
if (this.allowedToShowView('month')) {
|
||||
this.setPageDate(date)
|
||||
this.$emit('changedYear', year)
|
||||
this.showMonthCalendar()
|
||||
} else {
|
||||
this.selectDate(year)
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Set the datepicker value
|
||||
* @param {Date|String|Number|null} date
|
||||
*/
|
||||
setValue (date) {
|
||||
if (typeof date === 'string' || typeof date === 'number') {
|
||||
let parsed = new Date(date)
|
||||
date = isNaN(parsed.valueOf()) ? null : parsed
|
||||
}
|
||||
if (!date) {
|
||||
this.setPageDate()
|
||||
this.selectedDate = null
|
||||
return
|
||||
}
|
||||
this.selectedDate = date
|
||||
this.setPageDate(date)
|
||||
},
|
||||
/**
|
||||
* Sets the date that the calendar should open on
|
||||
*/
|
||||
setPageDate (date) {
|
||||
if (!date) {
|
||||
if (this.openDate) {
|
||||
date = new Date(this.openDate)
|
||||
} else {
|
||||
date = new Date()
|
||||
}
|
||||
}
|
||||
this.pageTimestamp = this.utils.setDate(new Date(date), 1)
|
||||
},
|
||||
/**
|
||||
* Handles a month change from the day picker
|
||||
*/
|
||||
handleChangedMonthFromDayPicker (date) {
|
||||
this.setPageDate(date)
|
||||
this.$emit('changedMonth', date)
|
||||
},
|
||||
/**
|
||||
* Set the date from a typedDate event
|
||||
*/
|
||||
setTypedDate (date) {
|
||||
this.setDate(date.getTime())
|
||||
},
|
||||
/**
|
||||
* Close all calendar layers
|
||||
* @param {Boolean} emitEvent - emit close event
|
||||
*/
|
||||
close (emitEvent) {
|
||||
this.showDayView = this.showMonthView = this.showYearView = false
|
||||
if (!this.isInline) {
|
||||
if (emitEvent) {
|
||||
this.$emit('closed')
|
||||
}
|
||||
document.removeEventListener('click', this.clickOutside, false)
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Initiate the component
|
||||
*/
|
||||
init () {
|
||||
if (this.modelValue) {
|
||||
this.setValue(this.modelValue)
|
||||
}
|
||||
if (this.isInline) {
|
||||
this.setInitialView()
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
this.init();
|
||||
}
|
||||
};
|
||||
</script>
|
||||
@@ -0,0 +1,376 @@
|
||||
<template>
|
||||
<div :class="[calendarClass, 'vdp-datepicker__calendar']" v-show="showDayView" :style="calendarStyle" @mousedown.prevent>
|
||||
<slot name="beforeCalendarHeader"></slot>
|
||||
<header>
|
||||
<span
|
||||
@click="isRtl ? nextMonth() : previousMonth()"
|
||||
class="prev"
|
||||
:class="{'disabled': isLeftNavDisabled}"><</span>
|
||||
<span class="day__month_btn" @click="showMonthCalendar" :class="allowedToShowView('month') ? 'up' : ''">{{ isYmd ? currYearName : currMonthName }} {{ isYmd ? currMonthName : currYearName }}</span>
|
||||
<span
|
||||
@click="isRtl ? previousMonth() : nextMonth()"
|
||||
class="next"
|
||||
:class="{'disabled': isRightNavDisabled}">></span>
|
||||
</header>
|
||||
<div :class="isRtl ? 'flex-rtl' : ''">
|
||||
<span class="cell day-header" v-for="d in daysOfWeek" :key="d.timestamp">{{ d }}</span>
|
||||
<template v-if="blankDays > 0">
|
||||
<span class="cell day blank" v-for="d in blankDays" :key="d.timestamp"></span>
|
||||
</template><!--
|
||||
--><span class="cell day"
|
||||
v-for="day in days"
|
||||
:key="day.timestamp"
|
||||
:class="dayClasses(day)"
|
||||
v-html="dayCellContent(day)"
|
||||
@click="selectDate(day)"></span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { makeDateUtils } from '../utils/DateUtils'
|
||||
export default {
|
||||
props: {
|
||||
showDayView: Boolean,
|
||||
selectedDate: Date,
|
||||
pageDate: Date,
|
||||
pageTimestamp: Number,
|
||||
fullMonthName: Boolean,
|
||||
allowedToShowView: Function,
|
||||
dayCellContent: {
|
||||
type: Function,
|
||||
default: day => day.date
|
||||
},
|
||||
disabledDates: Object,
|
||||
highlighted: Object,
|
||||
calendarClass: [String, Object, Array],
|
||||
calendarStyle: Object,
|
||||
translation: Object,
|
||||
isRtl: Boolean,
|
||||
mondayFirst: Boolean,
|
||||
useUtc: Boolean
|
||||
},
|
||||
emits: ['changedMonth', 'selectedDisabled', 'selectDate', 'showMonthCalendar'],
|
||||
data () {
|
||||
const constructedDateUtils = makeDateUtils(this.useUtc)
|
||||
return {
|
||||
utils: constructedDateUtils
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
/**
|
||||
* Returns an array of day names
|
||||
* @return {String[]}
|
||||
*/
|
||||
daysOfWeek () {
|
||||
if (this.mondayFirst) {
|
||||
const tempDays = this.translation.days.slice()
|
||||
tempDays.push(tempDays.shift())
|
||||
return tempDays
|
||||
}
|
||||
return this.translation.days
|
||||
},
|
||||
/**
|
||||
* Returns the day number of the week less one for the first of the current month
|
||||
* Used to show amount of empty cells before the first in the day calendar layout
|
||||
* @return {Number}
|
||||
*/
|
||||
blankDays () {
|
||||
const d = this.pageDate
|
||||
let dObj = this.useUtc
|
||||
? new Date(Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), 1))
|
||||
: new Date(d.getFullYear(), d.getMonth(), 1, d.getHours(), d.getMinutes())
|
||||
if (this.mondayFirst) {
|
||||
return this.utils.getDay(dObj) > 0 ? this.utils.getDay(dObj) - 1 : 6
|
||||
}
|
||||
return this.utils.getDay(dObj)
|
||||
},
|
||||
/**
|
||||
* @return {Object[]}
|
||||
*/
|
||||
days () {
|
||||
const d = this.pageDate
|
||||
let days = []
|
||||
// set up a new date object to the beginning of the current 'page'
|
||||
let dObj = this.useUtc
|
||||
? new Date(Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), 1))
|
||||
: new Date(d.getFullYear(), d.getMonth(), 1, d.getHours(), d.getMinutes())
|
||||
let daysInMonth = this.utils.daysInMonth(this.utils.getFullYear(dObj), this.utils.getMonth(dObj))
|
||||
for (let i = 0; i < daysInMonth; i++) {
|
||||
days.push({
|
||||
date: this.utils.getDate(dObj),
|
||||
timestamp: dObj.getTime(),
|
||||
isSelected: this.isSelectedDate(dObj),
|
||||
isDisabled: this.isDisabledDate(dObj),
|
||||
isHighlighted: this.isHighlightedDate(dObj),
|
||||
isHighlightStart: this.isHighlightStart(dObj),
|
||||
isHighlightEnd: this.isHighlightEnd(dObj),
|
||||
isToday: this.utils.compareDates(dObj, new Date()),
|
||||
isWeekend: this.utils.getDay(dObj) === 0 || this.utils.getDay(dObj) === 6,
|
||||
isSaturday: this.utils.getDay(dObj) === 6,
|
||||
isSunday: this.utils.getDay(dObj) === 0
|
||||
})
|
||||
this.utils.setDate(dObj, this.utils.getDate(dObj) + 1)
|
||||
}
|
||||
return days
|
||||
},
|
||||
/**
|
||||
* Gets the name of the month the current page is on
|
||||
* @return {String}
|
||||
*/
|
||||
currMonthName () {
|
||||
const monthName = this.fullMonthName ? this.translation.months : this.translation.monthsAbbr
|
||||
return this.utils.getMonthNameAbbr(this.utils.getMonth(this.pageDate), monthName)
|
||||
},
|
||||
/**
|
||||
* Gets the name of the year that current page is on
|
||||
* @return {Number}
|
||||
*/
|
||||
currYearName () {
|
||||
const yearSuffix = this.translation.yearSuffix
|
||||
return `${this.utils.getFullYear(this.pageDate)}${yearSuffix}`
|
||||
},
|
||||
/**
|
||||
* Is this translation using year/month/day format?
|
||||
* @return {Boolean}
|
||||
*/
|
||||
isYmd () {
|
||||
return this.translation.ymd && this.translation.ymd === true
|
||||
},
|
||||
/**
|
||||
* Is the left hand navigation button disabled?
|
||||
* @return {Boolean}
|
||||
*/
|
||||
isLeftNavDisabled () {
|
||||
return this.isRtl
|
||||
? this.isNextMonthDisabled(this.pageTimestamp)
|
||||
: this.isPreviousMonthDisabled(this.pageTimestamp)
|
||||
},
|
||||
/**
|
||||
* Is the right hand navigation button disabled?
|
||||
* @return {Boolean}
|
||||
*/
|
||||
isRightNavDisabled () {
|
||||
return this.isRtl
|
||||
? this.isPreviousMonthDisabled(this.pageTimestamp)
|
||||
: this.isNextMonthDisabled(this.pageTimestamp)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
selectDate (date) {
|
||||
if (date.isDisabled) {
|
||||
this.$emit('selectedDisabled', date)
|
||||
return false
|
||||
}
|
||||
this.$emit('selectDate', date)
|
||||
},
|
||||
/**
|
||||
* @return {Number}
|
||||
*/
|
||||
getPageMonth () {
|
||||
return this.utils.getMonth(this.pageDate)
|
||||
},
|
||||
/**
|
||||
* Emit an event to show the month picker
|
||||
*/
|
||||
showMonthCalendar () {
|
||||
this.$emit('showMonthCalendar')
|
||||
},
|
||||
/**
|
||||
* Change the page month
|
||||
* @param {Number} incrementBy
|
||||
*/
|
||||
changeMonth (incrementBy) {
|
||||
let date = this.pageDate
|
||||
this.utils.setMonth(date, this.utils.getMonth(date) + incrementBy)
|
||||
this.$emit('changedMonth', date)
|
||||
},
|
||||
/**
|
||||
* Decrement the page month
|
||||
*/
|
||||
previousMonth () {
|
||||
if (!this.isPreviousMonthDisabled()) {
|
||||
this.changeMonth(-1)
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Is the previous month disabled?
|
||||
* @return {Boolean}
|
||||
*/
|
||||
isPreviousMonthDisabled () {
|
||||
if (!this.disabledDates || !this.disabledDates.to) {
|
||||
return false
|
||||
}
|
||||
let d = this.pageDate
|
||||
return this.utils.getMonth(this.disabledDates.to) >= this.utils.getMonth(d) &&
|
||||
this.utils.getFullYear(this.disabledDates.to) >= this.utils.getFullYear(d)
|
||||
},
|
||||
/**
|
||||
* Increment the current page month
|
||||
*/
|
||||
nextMonth () {
|
||||
if (!this.isNextMonthDisabled()) {
|
||||
this.changeMonth(+1)
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Is the next month disabled?
|
||||
* @return {Boolean}
|
||||
*/
|
||||
isNextMonthDisabled () {
|
||||
if (!this.disabledDates || !this.disabledDates.from) {
|
||||
return false
|
||||
}
|
||||
let d = this.pageDate
|
||||
return this.utils.getMonth(this.disabledDates.from) <= this.utils.getMonth(d) &&
|
||||
this.utils.getFullYear(this.disabledDates.from) <= this.utils.getFullYear(d)
|
||||
},
|
||||
/**
|
||||
* Whether a day is selected
|
||||
* @param {Date}
|
||||
* @return {Boolean}
|
||||
*/
|
||||
isSelectedDate (dObj) {
|
||||
return this.selectedDate && this.utils.compareDates(this.selectedDate, dObj)
|
||||
},
|
||||
/**
|
||||
* Whether a day is disabled
|
||||
* @param {Date}
|
||||
* @return {Boolean}
|
||||
*/
|
||||
isDisabledDate (date) {
|
||||
let disabledDates = false
|
||||
|
||||
if (typeof this.disabledDates === 'undefined') {
|
||||
return false
|
||||
}
|
||||
|
||||
if (typeof this.disabledDates.dates !== 'undefined') {
|
||||
this.disabledDates.dates.forEach((d) => {
|
||||
if (this.utils.compareDates(date, d)) {
|
||||
disabledDates = true
|
||||
return true
|
||||
}
|
||||
})
|
||||
}
|
||||
if (typeof this.disabledDates.to !== 'undefined' && this.disabledDates.to && date < this.disabledDates.to) {
|
||||
disabledDates = true
|
||||
}
|
||||
if (typeof this.disabledDates.from !== 'undefined' && this.disabledDates.from && date > this.disabledDates.from) {
|
||||
disabledDates = true
|
||||
}
|
||||
if (typeof this.disabledDates.ranges !== 'undefined') {
|
||||
this.disabledDates.ranges.forEach((range) => {
|
||||
if (typeof range.from !== 'undefined' && range.from && typeof range.to !== 'undefined' && range.to) {
|
||||
if (date < range.to && date > range.from) {
|
||||
disabledDates = true
|
||||
return true
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
if (typeof this.disabledDates.days !== 'undefined' && this.disabledDates.days.indexOf(this.utils.getDay(date)) !== -1) {
|
||||
disabledDates = true
|
||||
}
|
||||
if (typeof this.disabledDates.daysOfMonth !== 'undefined' && this.disabledDates.daysOfMonth.indexOf(this.utils.getDate(date)) !== -1) {
|
||||
disabledDates = true
|
||||
}
|
||||
if (typeof this.disabledDates.customPredictor === 'function' && this.disabledDates.customPredictor(date)) {
|
||||
disabledDates = true
|
||||
}
|
||||
return disabledDates
|
||||
},
|
||||
/**
|
||||
* Whether a day is highlighted (only if it is not disabled already except when highlighted.includeDisabled is true)
|
||||
* @param {Date}
|
||||
* @return {Boolean}
|
||||
*/
|
||||
isHighlightedDate (date) {
|
||||
if (!(this.highlighted && this.highlighted.includeDisabled) && this.isDisabledDate(date)) {
|
||||
return false
|
||||
}
|
||||
|
||||
let highlighted = false
|
||||
|
||||
if (typeof this.highlighted === 'undefined') {
|
||||
return false
|
||||
}
|
||||
|
||||
if (typeof this.highlighted.dates !== 'undefined') {
|
||||
this.highlighted.dates.forEach((d) => {
|
||||
if (this.utils.compareDates(date, d)) {
|
||||
highlighted = true
|
||||
return true
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if (this.isDefined(this.highlighted.from) && this.isDefined(this.highlighted.to)) {
|
||||
highlighted = date >= this.highlighted.from && date <= this.highlighted.to
|
||||
}
|
||||
|
||||
if (typeof this.highlighted.days !== 'undefined' && this.highlighted.days.indexOf(this.utils.getDay(date)) !== -1) {
|
||||
highlighted = true
|
||||
}
|
||||
|
||||
if (typeof this.highlighted.daysOfMonth !== 'undefined' && this.highlighted.daysOfMonth.indexOf(this.utils.getDate(date)) !== -1) {
|
||||
highlighted = true
|
||||
}
|
||||
|
||||
if (typeof this.highlighted.customPredictor === 'function' && this.highlighted.customPredictor(date)) {
|
||||
highlighted = true
|
||||
}
|
||||
|
||||
return highlighted
|
||||
},
|
||||
dayClasses (day) {
|
||||
return {
|
||||
'selected': day.isSelected,
|
||||
'disabled': day.isDisabled,
|
||||
'highlighted': day.isHighlighted,
|
||||
'today': day.isToday,
|
||||
'weekend': day.isWeekend,
|
||||
'sat': day.isSaturday,
|
||||
'sun': day.isSunday,
|
||||
'highlight-start': day.isHighlightStart,
|
||||
'highlight-end': day.isHighlightEnd
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Whether a day is highlighted and it is the first date
|
||||
* in the highlighted range of dates
|
||||
* @param {Date}
|
||||
* @return {Boolean}
|
||||
*/
|
||||
isHighlightStart (date) {
|
||||
return this.isHighlightedDate(date) &&
|
||||
(this.highlighted.from instanceof Date) &&
|
||||
(this.utils.getFullYear(this.highlighted.from) === this.utils.getFullYear(date)) &&
|
||||
(this.utils.getMonth(this.highlighted.from) === this.utils.getMonth(date)) &&
|
||||
(this.utils.getDate(this.highlighted.from) === this.utils.getDate(date))
|
||||
},
|
||||
/**
|
||||
* Whether a day is highlighted and it is the first date
|
||||
* in the highlighted range of dates
|
||||
* @param {Date}
|
||||
* @return {Boolean}
|
||||
*/
|
||||
isHighlightEnd (date) {
|
||||
return this.isHighlightedDate(date) &&
|
||||
(this.highlighted.to instanceof Date) &&
|
||||
(this.utils.getFullYear(this.highlighted.to) === this.utils.getFullYear(date)) &&
|
||||
(this.utils.getMonth(this.highlighted.to) === this.utils.getMonth(date)) &&
|
||||
(this.utils.getDate(this.highlighted.to) === this.utils.getDate(date))
|
||||
},
|
||||
/**
|
||||
* Helper
|
||||
* @param {mixed} prop
|
||||
* @return {Boolean}
|
||||
*/
|
||||
isDefined (prop) {
|
||||
return typeof prop !== 'undefined' && prop
|
||||
}
|
||||
}
|
||||
}
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
</script>
|
||||
@@ -0,0 +1,201 @@
|
||||
<template>
|
||||
<div :class="[calendarClass, 'vdp-datepicker__calendar']" v-show="showMonthView" :style="calendarStyle" @mousedown.prevent>
|
||||
<slot name="beforeCalendarHeader"></slot>
|
||||
<header>
|
||||
<span
|
||||
@click="isRtl ? nextYear() : previousYear()"
|
||||
class="prev"
|
||||
:class="{'disabled': isLeftNavDisabled}"><</span>
|
||||
<span class="month__year_btn" @click="showYearCalendar" :class="allowedToShowView('year') ? 'up' : ''">{{ pageYearName }}</span>
|
||||
<span
|
||||
@click="isRtl ? previousYear() : nextYear()"
|
||||
class="next"
|
||||
:class="{'disabled': isRightNavDisabled}">></span>
|
||||
</header>
|
||||
<span class="cell month"
|
||||
v-for="month in months"
|
||||
:key="month.timestamp"
|
||||
:class="{'selected': month.isSelected, 'disabled': month.isDisabled}"
|
||||
@click.stop="selectMonth(month)">{{ month.month }}</span>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { makeDateUtils } from '../utils/DateUtils'
|
||||
export default {
|
||||
props: {
|
||||
showMonthView: Boolean,
|
||||
selectedDate: Date,
|
||||
pageDate: Date,
|
||||
pageTimestamp: Number,
|
||||
disabledDates: Object,
|
||||
calendarClass: [String, Object, Array],
|
||||
calendarStyle: Object,
|
||||
translation: Object,
|
||||
isRtl: Boolean,
|
||||
allowedToShowView: Function,
|
||||
useUtc: Boolean
|
||||
},
|
||||
emits: ['selectMonth', 'changedYear', 'showYearCalendar'],
|
||||
data () {
|
||||
const constructedDateUtils = makeDateUtils(this.useUtc)
|
||||
return {
|
||||
utils: constructedDateUtils
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
months () {
|
||||
const d = this.pageDate
|
||||
let months = []
|
||||
// set up a new date object to the beginning of the current 'page'
|
||||
let dObj = this.useUtc
|
||||
? new Date(Date.UTC(d.getUTCFullYear(), 0, d.getUTCDate()))
|
||||
: new Date(d.getFullYear(), 0, d.getDate(), d.getHours(), d.getMinutes())
|
||||
for (let i = 0; i < 12; i++) {
|
||||
months.push({
|
||||
month: this.utils.getMonthName(i, this.translation.months),
|
||||
timestamp: dObj.getTime(),
|
||||
isSelected: this.isSelectedMonth(dObj),
|
||||
isDisabled: this.isDisabledMonth(dObj)
|
||||
})
|
||||
this.utils.setMonth(dObj, this.utils.getMonth(dObj) + 1)
|
||||
}
|
||||
return months
|
||||
},
|
||||
/**
|
||||
* Get year name on current page.
|
||||
* @return {String}
|
||||
*/
|
||||
pageYearName () {
|
||||
const yearSuffix = this.translation.yearSuffix
|
||||
return `${this.utils.getFullYear(this.pageDate)}${yearSuffix}`
|
||||
},
|
||||
/**
|
||||
* Is the left hand navigation disabled
|
||||
* @return {Boolean}
|
||||
*/
|
||||
isLeftNavDisabled () {
|
||||
return this.isRtl
|
||||
? this.isNextYearDisabled(this.pageTimestamp)
|
||||
: this.isPreviousYearDisabled(this.pageTimestamp)
|
||||
},
|
||||
/**
|
||||
* Is the right hand navigation disabled
|
||||
* @return {Boolean}
|
||||
*/
|
||||
isRightNavDisabled () {
|
||||
return this.isRtl
|
||||
? this.isPreviousYearDisabled(this.pageTimestamp)
|
||||
: this.isNextYearDisabled(this.pageTimestamp)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* Emits a selectMonth event
|
||||
* @param {Object} month
|
||||
*/
|
||||
selectMonth (month) {
|
||||
if (month.isDisabled) {
|
||||
return false
|
||||
}
|
||||
this.$emit('selectMonth', month)
|
||||
},
|
||||
/**
|
||||
* Changes the year up or down
|
||||
* @param {Number} incrementBy
|
||||
*/
|
||||
changeYear (incrementBy) {
|
||||
let date = this.pageDate
|
||||
this.utils.setFullYear(date, this.utils.getFullYear(date) + incrementBy)
|
||||
this.$emit('changedYear', date)
|
||||
},
|
||||
/**
|
||||
* Decrements the year
|
||||
*/
|
||||
previousYear () {
|
||||
if (!this.isPreviousYearDisabled()) {
|
||||
this.changeYear(-1)
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Checks if the previous year is disabled or not
|
||||
* @return {Boolean}
|
||||
*/
|
||||
isPreviousYearDisabled () {
|
||||
if (!this.disabledDates || !this.disabledDates.to) {
|
||||
return false
|
||||
}
|
||||
return this.utils.getFullYear(this.disabledDates.to) >= this.utils.getFullYear(this.pageDate)
|
||||
},
|
||||
/**
|
||||
* Increments the year
|
||||
*/
|
||||
nextYear () {
|
||||
if (!this.isNextYearDisabled()) {
|
||||
this.changeYear(1)
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Checks if the next year is disabled or not
|
||||
* @return {Boolean}
|
||||
*/
|
||||
isNextYearDisabled () {
|
||||
if (!this.disabledDates || !this.disabledDates.from) {
|
||||
return false
|
||||
}
|
||||
return this.utils.getFullYear(this.disabledDates.from) <= this.utils.getFullYear(this.pageDate)
|
||||
},
|
||||
/**
|
||||
* Emits an event that shows the year calendar
|
||||
*/
|
||||
showYearCalendar () {
|
||||
this.$emit('showYearCalendar')
|
||||
},
|
||||
/**
|
||||
* Whether the selected date is in this month
|
||||
* @param {Date}
|
||||
* @return {Boolean}
|
||||
*/
|
||||
isSelectedMonth (date) {
|
||||
return (this.selectedDate &&
|
||||
this.utils.getFullYear(this.selectedDate) === this.utils.getFullYear(date) &&
|
||||
this.utils.getMonth(this.selectedDate) === this.utils.getMonth(date))
|
||||
},
|
||||
/**
|
||||
* Whether a month is disabled
|
||||
* @param {Date}
|
||||
* @return {Boolean}
|
||||
*/
|
||||
isDisabledMonth (date) {
|
||||
let disabledDates = false
|
||||
|
||||
if (typeof this.disabledDates === 'undefined') {
|
||||
return false
|
||||
}
|
||||
|
||||
if (typeof this.disabledDates.to !== 'undefined' && this.disabledDates.to) {
|
||||
if (
|
||||
(this.utils.getMonth(date) < this.utils.getMonth(this.disabledDates.to) && this.utils.getFullYear(date) <= this.utils.getFullYear(this.disabledDates.to)) ||
|
||||
this.utils.getFullYear(date) < this.utils.getFullYear(this.disabledDates.to)
|
||||
) {
|
||||
disabledDates = true
|
||||
}
|
||||
}
|
||||
if (typeof this.disabledDates.from !== 'undefined' && this.disabledDates.from) {
|
||||
if (
|
||||
(this.utils.getMonth(date) > this.utils.getMonth(this.disabledDates.from) && this.utils.getFullYear(date) >= this.utils.getFullYear(this.disabledDates.from)) ||
|
||||
this.utils.getFullYear(date) > this.utils.getFullYear(this.disabledDates.from)
|
||||
) {
|
||||
disabledDates = true
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof this.disabledDates.customPredictor === 'function' && this.disabledDates.customPredictor(date)) {
|
||||
disabledDates = true
|
||||
}
|
||||
return disabledDates
|
||||
}
|
||||
}
|
||||
}
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
</script>
|
||||
@@ -0,0 +1,175 @@
|
||||
<template>
|
||||
<div :class="[calendarClass, 'vdp-datepicker__calendar']" v-show="showYearView" :style="calendarStyle" @mousedown.prevent>
|
||||
<slot name="beforeCalendarHeader"></slot>
|
||||
<header>
|
||||
<span
|
||||
@click="isRtl ? nextDecade() : previousDecade()"
|
||||
class="prev"
|
||||
:class="{'disabled': isLeftNavDisabled}"><</span>
|
||||
<span>{{ getPageDecade }}</span>
|
||||
<span
|
||||
@click="isRtl ? previousDecade() : nextDecade()"
|
||||
class="next"
|
||||
:class="{'disabled': isRightNavDisabled}">></span>
|
||||
</header>
|
||||
<span
|
||||
class="cell year"
|
||||
v-for="year in years"
|
||||
:key="year.timestamp"
|
||||
:class="{ 'selected': year.isSelected, 'disabled': year.isDisabled }"
|
||||
@click.stop="selectYear(year)">{{ year.year }}</span>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { makeDateUtils } from '../utils/DateUtils'
|
||||
export default {
|
||||
props: {
|
||||
showYearView: Boolean,
|
||||
selectedDate: Date,
|
||||
pageDate: Date,
|
||||
pageTimestamp: Number,
|
||||
disabledDates: Object,
|
||||
highlighted: Object,
|
||||
calendarClass: [String, Object, Array],
|
||||
calendarStyle: Object,
|
||||
translation: Object,
|
||||
isRtl: Boolean,
|
||||
allowedToShowView: Function,
|
||||
useUtc: Boolean
|
||||
},
|
||||
emits: ['selectYear', 'changedDecade'],
|
||||
computed: {
|
||||
years () {
|
||||
const d = this.pageDate
|
||||
let years = []
|
||||
// set up a new date object to the beginning of the current 'page'7
|
||||
let dObj = this.useUtc
|
||||
? new Date(Date.UTC(Math.floor(d.getUTCFullYear() / 10) * 10, d.getUTCMonth(), d.getUTCDate()))
|
||||
: new Date(Math.floor(d.getFullYear() / 10) * 10, d.getMonth(), d.getDate(), d.getHours(), d.getMinutes())
|
||||
for (let i = 0; i < 10; i++) {
|
||||
years.push({
|
||||
year: this.utils.getFullYear(dObj),
|
||||
timestamp: dObj.getTime(),
|
||||
isSelected: this.isSelectedYear(dObj),
|
||||
isDisabled: this.isDisabledYear(dObj)
|
||||
})
|
||||
this.utils.setFullYear(dObj, this.utils.getFullYear(dObj) + 1)
|
||||
}
|
||||
return years
|
||||
},
|
||||
/**
|
||||
* @return {String}
|
||||
*/
|
||||
getPageDecade () {
|
||||
const decadeStart = Math.floor(this.utils.getFullYear(this.pageDate) / 10) * 10
|
||||
const decadeEnd = decadeStart + 9
|
||||
const yearSuffix = this.translation.yearSuffix
|
||||
return `${decadeStart} - ${decadeEnd}${yearSuffix}`
|
||||
},
|
||||
/**
|
||||
* Is the left hand navigation button disabled?
|
||||
* @return {Boolean}
|
||||
*/
|
||||
isLeftNavDisabled () {
|
||||
return this.isRtl
|
||||
? this.isNextDecadeDisabled(this.pageTimestamp)
|
||||
: this.isPreviousDecadeDisabled(this.pageTimestamp)
|
||||
},
|
||||
/**
|
||||
* Is the right hand navigation button disabled?
|
||||
* @return {Boolean}
|
||||
*/
|
||||
isRightNavDisabled () {
|
||||
return this.isRtl
|
||||
? this.isPreviousDecadeDisabled(this.pageTimestamp)
|
||||
: this.isNextDecadeDisabled(this.pageTimestamp)
|
||||
}
|
||||
},
|
||||
data () {
|
||||
const constructedDateUtils = makeDateUtils(this.useUtc)
|
||||
return {
|
||||
utils: constructedDateUtils
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
selectYear (year) {
|
||||
if (year.isDisabled) {
|
||||
return false
|
||||
}
|
||||
this.$emit('selectYear', year)
|
||||
},
|
||||
changeYear (incrementBy) {
|
||||
let date = this.pageDate
|
||||
this.utils.setFullYear(date, this.utils.getFullYear(date) + incrementBy)
|
||||
this.$emit('changedDecade', date)
|
||||
},
|
||||
previousDecade () {
|
||||
if (this.isPreviousDecadeDisabled()) {
|
||||
return false
|
||||
}
|
||||
this.changeYear(-10)
|
||||
},
|
||||
isPreviousDecadeDisabled () {
|
||||
if (!this.disabledDates || !this.disabledDates.to) {
|
||||
return false
|
||||
}
|
||||
const disabledYear = this.utils.getFullYear(this.disabledDates.to)
|
||||
const lastYearInPreviousPage = Math.floor(this.utils.getFullYear(this.pageDate) / 10) * 10 - 1
|
||||
return disabledYear > lastYearInPreviousPage
|
||||
},
|
||||
nextDecade () {
|
||||
if (this.isNextDecadeDisabled()) {
|
||||
return false
|
||||
}
|
||||
this.changeYear(10)
|
||||
},
|
||||
isNextDecadeDisabled () {
|
||||
if (!this.disabledDates || !this.disabledDates.from) {
|
||||
return false
|
||||
}
|
||||
const disabledYear = this.utils.getFullYear(this.disabledDates.from)
|
||||
const firstYearInNextPage = Math.ceil(this.utils.getFullYear(this.pageDate) / 10) * 10
|
||||
return disabledYear < firstYearInNextPage
|
||||
},
|
||||
|
||||
/**
|
||||
* Whether the selected date is in this year
|
||||
* @param {Date}
|
||||
* @return {Boolean}
|
||||
*/
|
||||
isSelectedYear (date) {
|
||||
return this.selectedDate && this.utils.getFullYear(this.selectedDate) === this.utils.getFullYear(date)
|
||||
},
|
||||
/**
|
||||
* Whether a year is disabled
|
||||
* @param {Date}
|
||||
* @return {Boolean}
|
||||
*/
|
||||
isDisabledYear (date) {
|
||||
let disabledDates = false
|
||||
if (typeof this.disabledDates === 'undefined' || !this.disabledDates) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (typeof this.disabledDates.to !== 'undefined' && this.disabledDates.to) {
|
||||
if (this.utils.getFullYear(date) < this.utils.getFullYear(this.disabledDates.to)) {
|
||||
disabledDates = true
|
||||
}
|
||||
}
|
||||
if (typeof this.disabledDates.from !== 'undefined' && this.disabledDates.from) {
|
||||
if (this.utils.getFullYear(date) > this.utils.getFullYear(this.disabledDates.from)) {
|
||||
disabledDates = true
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof this.disabledDates.customPredictor === 'function' && this.disabledDates.customPredictor(date)) {
|
||||
disabledDates = true
|
||||
}
|
||||
|
||||
return disabledDates
|
||||
}
|
||||
}
|
||||
}
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
</script>
|
||||
@@ -0,0 +1,3 @@
|
||||
import SdlDatePicker from './SdlDatePicker.vue';
|
||||
|
||||
export default SdlDatePicker;
|
||||
@@ -0,0 +1,57 @@
|
||||
export default class Language {
|
||||
constructor (language, months, monthsAbbr, days) {
|
||||
this.language = language
|
||||
this.months = months
|
||||
this.monthsAbbr = monthsAbbr
|
||||
this.days = days
|
||||
this.rtl = false
|
||||
this.ymd = false
|
||||
this.yearSuffix = ''
|
||||
}
|
||||
|
||||
get language () {
|
||||
return this._language
|
||||
}
|
||||
|
||||
set language (language) {
|
||||
if (typeof language !== 'string') {
|
||||
throw new TypeError('Language must be a string')
|
||||
}
|
||||
this._language = language
|
||||
}
|
||||
|
||||
get months () {
|
||||
return this._months
|
||||
}
|
||||
|
||||
set months (months) {
|
||||
if (months.length !== 12) {
|
||||
throw new RangeError(`There must be 12 months for ${this.language} language`)
|
||||
}
|
||||
this._months = months
|
||||
}
|
||||
|
||||
get monthsAbbr () {
|
||||
return this._monthsAbbr
|
||||
}
|
||||
|
||||
set monthsAbbr (monthsAbbr) {
|
||||
if (monthsAbbr.length !== 12) {
|
||||
throw new RangeError(`There must be 12 abbreviated months for ${this.language} language`)
|
||||
}
|
||||
this._monthsAbbr = monthsAbbr
|
||||
}
|
||||
|
||||
get days () {
|
||||
return this._days
|
||||
}
|
||||
|
||||
set days (days) {
|
||||
if (days.length !== 7) {
|
||||
throw new RangeError(`There must be 7 days for ${this.language} language`)
|
||||
}
|
||||
this._days = days
|
||||
}
|
||||
}
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
@@ -0,0 +1,105 @@
|
||||
import af from './translations/af'
|
||||
import ar from './translations/ar'
|
||||
import bg from './translations/bg'
|
||||
import bs from './translations/bs'
|
||||
import ca from './translations/ca'
|
||||
import cs from './translations/cs'
|
||||
import da from './translations/da'
|
||||
import de from './translations/de'
|
||||
import ee from './translations/ee'
|
||||
import el from './translations/el'
|
||||
import en from './translations/en'
|
||||
import es from './translations/es'
|
||||
import fa from './translations/fa'
|
||||
import fi from './translations/fi'
|
||||
import fo from './translations/fo'
|
||||
import fr from './translations/fr'
|
||||
import ge from './translations/ge'
|
||||
import gl from './translations/gl'
|
||||
import he from './translations/he'
|
||||
import hr from './translations/hr'
|
||||
import hu from './translations/hu'
|
||||
import id from './translations/id'
|
||||
import is from './translations/is'
|
||||
import it from './translations/it'
|
||||
import ja from './translations/ja'
|
||||
import kk from './translations/kk'
|
||||
import ko from './translations/ko'
|
||||
import lb from './translations/lb'
|
||||
import lt from './translations/lt'
|
||||
import lv from './translations/lv'
|
||||
import mk from './translations/mk'
|
||||
import mn from './translations/mn'
|
||||
import nbNO from './translations/nb-NO'
|
||||
import nl from './translations/nl'
|
||||
import pl from './translations/pl'
|
||||
import ptBR from './translations/pt-BR'
|
||||
import ro from './translations/ro'
|
||||
import ru from './translations/ru'
|
||||
import sk from './translations/sk'
|
||||
import slSI from './translations/sl-SI'
|
||||
import srCYRL from './translations/sr-CYRL'
|
||||
import sr from './translations/sr'
|
||||
import sv from './translations/sv'
|
||||
import th from './translations/th'
|
||||
import tr from './translations/tr'
|
||||
import uk from './translations/uk'
|
||||
import ur from './translations/ur'
|
||||
import vi from './translations/vi'
|
||||
import zh from './translations/zh'
|
||||
import zhHK from './translations/zh-HK'
|
||||
|
||||
export {
|
||||
af,
|
||||
ar,
|
||||
bg,
|
||||
bs,
|
||||
ca,
|
||||
cs,
|
||||
da,
|
||||
de,
|
||||
ee,
|
||||
el,
|
||||
en,
|
||||
es,
|
||||
fa,
|
||||
fi,
|
||||
fo,
|
||||
fr,
|
||||
ge,
|
||||
gl,
|
||||
he,
|
||||
hr,
|
||||
hu,
|
||||
id,
|
||||
is,
|
||||
it,
|
||||
ja,
|
||||
kk,
|
||||
ko,
|
||||
lb,
|
||||
lt,
|
||||
lv,
|
||||
mk,
|
||||
mn,
|
||||
nbNO,
|
||||
nl,
|
||||
pl,
|
||||
ptBR,
|
||||
ro,
|
||||
ru,
|
||||
sk,
|
||||
slSI,
|
||||
srCYRL,
|
||||
sr,
|
||||
sv,
|
||||
th,
|
||||
tr,
|
||||
uk,
|
||||
ur,
|
||||
vi,
|
||||
zh,
|
||||
zhHK
|
||||
}
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
@@ -0,0 +1,10 @@
|
||||
import Language from '../Language'
|
||||
|
||||
export default new Language(
|
||||
'Afrikaans',
|
||||
['Januarie', 'Februarie', 'Maart', 'April', 'Mei', 'Junie', 'Julie', 'Augustus', 'September', 'Oktober', 'November', 'Desember'],
|
||||
['Jan', 'Feb', 'Mrt', 'Apr', 'Mei', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Des'],
|
||||
['So.', 'Ma.', 'Di.', 'Wo.', 'Do.', 'Vr.', 'Sa.']
|
||||
)
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
@@ -0,0 +1,14 @@
|
||||
import Language from '../Language'
|
||||
|
||||
const language = new Language(
|
||||
'Arabic',
|
||||
['يناير', 'فبراير', 'مارس', 'أبريل', 'مايو', 'يونيو', 'يوليو', 'أغسطس', 'سبتمبر', 'أكتوبر', 'نوڤمبر', 'ديسمبر'],
|
||||
['يناير', 'فبراير', 'مارس', 'أبريل', 'مايو', 'يونيو', 'يوليو', 'أغسطس', 'سبتمبر', 'أكتوبر', 'نوڤمبر', 'ديسمبر'],
|
||||
['أحد', 'إثنين', 'ثلاثاء', 'أربعاء', 'خميس', 'جمعة', 'سبت']
|
||||
)
|
||||
|
||||
language.rtl = true
|
||||
|
||||
export default language
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
@@ -0,0 +1,10 @@
|
||||
import Language from '../Language'
|
||||
|
||||
export default new Language(
|
||||
'Bulgarian',
|
||||
['Януари', 'Февруари', 'Март', 'Април', 'Май', 'Юни', 'Юли', 'Август', 'Септември', 'Октомври', 'Ноември', 'Декември'],
|
||||
['Ян', 'Фев', 'Мар', 'Апр', 'Май', 'Юни', 'Юли', 'Авг', 'Сеп', 'Окт', 'Ное', 'Дек'],
|
||||
['Нд', 'Пн', 'Вт', 'Ср', 'Чт', 'Пт', 'Сб']
|
||||
)
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
@@ -0,0 +1,10 @@
|
||||
import Language from '../Language'
|
||||
|
||||
export default new Language(
|
||||
'Bosnian',
|
||||
['Januar', 'Februar', 'Mart', 'April', 'Maj', 'Juni', 'Juli', 'Avgust', 'Septembar', 'Oktobar', 'Novembar', 'Decembar'],
|
||||
['Jan', 'Feb', 'Mar', 'Apr', 'Maj', 'Jun', 'Jul', 'Avg', 'Sep', 'Okt', 'Nov', 'Dec'],
|
||||
['Ned', 'Pon', 'Uto', 'Sri', 'Čet', 'Pet', 'Sub']
|
||||
)
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
@@ -0,0 +1,10 @@
|
||||
import Language from '../Language'
|
||||
|
||||
export default new Language(
|
||||
'Catalan',
|
||||
['Gener', 'Febrer', 'Març', 'Abril', 'Maig', 'Juny', 'Juliol', 'Agost', 'Setembre', 'Octubre', 'Novembre', 'Desembre'],
|
||||
['Gen', 'Feb', 'Mar', 'Abr', 'Mai', 'Jun', 'Jul', 'Ago', 'Set', 'Oct', 'Nov', 'Des'],
|
||||
['Diu', 'Dil', 'Dmr', 'Dmc', 'Dij', 'Div', 'Dis']
|
||||
)
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
@@ -0,0 +1,10 @@
|
||||
import Language from '../Language'
|
||||
|
||||
export default new Language(
|
||||
'Czech',
|
||||
['leden', 'únor', 'březen', 'duben', 'květen', 'červen', 'červenec', 'srpen', 'září', 'říjen', 'listopad', 'prosinec'],
|
||||
['led', 'úno', 'bře', 'dub', 'kvě', 'čer', 'čec', 'srp', 'zář', 'říj', 'lis', 'pro'],
|
||||
['ne', 'po', 'út', 'st', 'čt', 'pá', 'so']
|
||||
)
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
@@ -0,0 +1,10 @@
|
||||
import Language from '../Language'
|
||||
|
||||
export default new Language(
|
||||
'Danish',
|
||||
['Januar', 'Februar', 'Marts', 'April', 'Maj', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'December'],
|
||||
['Jan', 'Feb', 'Mar', 'Apr', 'Maj', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dec'],
|
||||
['Sø', 'Ma', 'Ti', 'On', 'To', 'Fr', 'Lø']
|
||||
)
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
@@ -0,0 +1,10 @@
|
||||
import Language from '../Language'
|
||||
|
||||
export default new Language(
|
||||
'German',
|
||||
['Januar', 'Februar', 'März', 'April', 'Mai', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember'],
|
||||
['Jan', 'Feb', 'Mär', 'Apr', 'Mai', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dez'],
|
||||
['So.', 'Mo.', 'Di.', 'Mi.', 'Do.', 'Fr.', 'Sa.']
|
||||
)
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
@@ -0,0 +1,10 @@
|
||||
import Language from '../Language'
|
||||
|
||||
export default new Language(
|
||||
'Estonian',
|
||||
['Jaanuar', 'Veebruar', 'Märts', 'Aprill', 'Mai', 'Juuni', 'Juuli', 'August', 'September', 'Oktoober', 'November', 'Detsember'],
|
||||
['Jaan', 'Veebr', 'Märts', 'Apr', 'Mai', 'Juuni', 'Juuli', 'Aug', 'Sept', 'Okt', 'Nov', 'Dets'],
|
||||
['P', 'E', 'T', 'K', 'N', 'R', 'L']
|
||||
)
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
@@ -0,0 +1,10 @@
|
||||
import Language from '../Language'
|
||||
|
||||
export default new Language(
|
||||
'Greek',
|
||||
['Ιανουάριος', 'Φεβρουάριος', 'Μάρτιος', 'Απρίλιος', 'Μάϊος', 'Ιούνιος', 'Ιούλιος', 'Αύγουστος', 'Σεπτέμβριος', 'Οκτώβριος', 'Νοέμβριος', 'Δεκέμβριος'],
|
||||
['Ιαν', 'Φεβ', 'Μαρ', 'Απρ', 'Μαι', 'Ιουν', 'Ιουλ', 'Αυγ', 'Σεπ', 'Οκτ', 'Νοε', 'Δεκ'],
|
||||
['Κυρ', 'Δευ', 'Τρι', 'Τετ', 'Πεμ', 'Παρ', 'Σαβ']
|
||||
)
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
@@ -0,0 +1,10 @@
|
||||
import Language from '../Language'
|
||||
|
||||
export default new Language(
|
||||
'English',
|
||||
['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
|
||||
['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
|
||||
['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']
|
||||
)
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
@@ -0,0 +1,10 @@
|
||||
import Language from '../Language'
|
||||
|
||||
export default new Language(
|
||||
'Spanish',
|
||||
['Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio', 'Julio', 'Agosto', 'Septiembre', 'Octubre', 'Noviembre', 'Diciembre'],
|
||||
['Ene', 'Feb', 'Mar', 'Abr', 'May', 'Jun', 'Jul', 'Ago', 'Sep', 'Oct', 'Nov', 'Dic'],
|
||||
['Dom', 'Lun', 'Mar', 'Mié', 'Jue', 'Vie', 'Sáb']
|
||||
)
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
@@ -0,0 +1,10 @@
|
||||
import Language from '../Language'
|
||||
|
||||
export default new Language(
|
||||
'Persian',
|
||||
['فروردین', 'اردیبهشت', 'خرداد', 'تیر', 'مرداد', 'شهریور', 'مهر', 'آبان', 'آذر', 'دی', 'بهمن', 'اسفند'],
|
||||
['فرو', 'ارد', 'خرد', 'تیر', 'مرد', 'شهر', 'مهر', 'آبا', 'آذر', 'دی', 'بهم', 'اسف'],
|
||||
['یکشنبه', 'دوشنبه', 'سهشنبه', 'چهارشنبه', 'پنجشنبه', 'جمعه', 'شنبه']
|
||||
)
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
@@ -0,0 +1,10 @@
|
||||
import Language from '../Language'
|
||||
|
||||
export default new Language(
|
||||
'Finnish',
|
||||
['tammikuu', 'helmikuu', 'maaliskuu', 'huhtikuu', 'toukokuu', 'kesäkuu', 'heinäkuu', 'elokuu', 'syyskuu', 'lokakuu', 'marraskuu', 'joulukuu'],
|
||||
['tammi', 'helmi', 'maalis', 'huhti', 'touko', 'kesä', 'heinä', 'elo', 'syys', 'loka', 'marras', 'joulu'],
|
||||
['su', 'ma', 'ti', 'ke', 'to', 'pe', 'la']
|
||||
)
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
@@ -0,0 +1,10 @@
|
||||
import Language from '../Language'
|
||||
|
||||
export default new Language(
|
||||
'Faroese',
|
||||
['Januar', 'Februar', 'Mars', 'Apríl', 'Mai', 'Juni', 'Juli', 'August', 'Septembur', 'Oktobur', 'Novembur', 'Desembur'],
|
||||
['Jan', 'Feb', 'Mar', 'Apr', 'Mai', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Des'],
|
||||
['Sun', 'Mán', 'Týs', 'Mik', 'Hós', 'Frí', 'Ley']
|
||||
)
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
@@ -0,0 +1,10 @@
|
||||
import Language from '../Language'
|
||||
|
||||
export default new Language(
|
||||
'French',
|
||||
['Janvier', 'Février', 'Mars', 'Avril', 'Mai', 'Juin', 'Juillet', 'Août', 'Septembre', 'Octobre', 'Novembre', 'Décembre'],
|
||||
['Jan', 'Fév', 'Mar', 'Avr', 'Mai', 'Juin', 'Juil', 'Août', 'Sep', 'Oct', 'Nov', 'Déc'],
|
||||
['Dim', 'Lun', 'Mar', 'Mer', 'Jeu', 'Ven', 'Sam']
|
||||
)
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
@@ -0,0 +1,10 @@
|
||||
import Language from '../Language'
|
||||
|
||||
export default new Language(
|
||||
'Georgia',
|
||||
['იანვარი', 'თებერვალი', 'მარტი', 'აპრილი', 'მაისი', 'ივნისი', 'ივლისი', 'აგვისტო', 'სექტემბერი', 'ოქტომბერი', 'ნოემბერი', 'დეკემბერი'],
|
||||
['იან', 'თებ', 'მარ', 'აპრ', 'მაი', 'ივნ', 'ივლ', 'აგვ', 'სექ', 'ოქტ', 'ნოე', 'დეკ'],
|
||||
['კვი', 'ორშ', 'სამ', 'ოთხ', 'ხუთ', 'პარ', 'შაბ']
|
||||
)
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
@@ -0,0 +1,10 @@
|
||||
import Language from '../Language'
|
||||
|
||||
export default new Language(
|
||||
'Galician',
|
||||
['Xaneiro', 'Febreiro', 'Marzo', 'Abril', 'Maio', 'Xuño', 'Xullo', 'Agosto', 'Setembro', 'Outubro', 'Novembro', 'Decembro'],
|
||||
['Xan', 'Feb', 'Mar', 'Abr', 'Mai', 'Xuñ', 'Xul', 'Ago', 'Set', 'Out', 'Nov', 'Dec'],
|
||||
['Dom', 'Lun', 'Mar', 'Mér', 'Xov', 'Ven', 'Sáb']
|
||||
)
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
@@ -0,0 +1,14 @@
|
||||
import Language from '../Language'
|
||||
|
||||
const language = new Language(
|
||||
'Hebrew',
|
||||
['ינואר', 'פברואר', 'מרץ', 'אפריל', 'מאי', 'יוני', 'יולי', 'אוגוסט', 'ספטמבר', 'אוקטובר', 'נובמבר', 'דצמבר'],
|
||||
['ינו', 'פבר', 'מרץ', 'אפר', 'מאי', 'יונ', 'יול', 'אוג', 'ספט', 'אוק', 'נוב', 'דצמ'],
|
||||
['א', 'ב', 'ג', 'ד', 'ה', 'ו', 'ש']
|
||||
)
|
||||
|
||||
language.rtl = true
|
||||
|
||||
export default language
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
@@ -0,0 +1,10 @@
|
||||
import Language from '../Language'
|
||||
|
||||
export default new Language(
|
||||
'Croatian',
|
||||
['Siječanj', 'Veljača', 'Ožujak', 'Travanj', 'Svibanj', 'Lipanj', 'Srpanj', 'Kolovoz', 'Rujan', 'Listopad', 'Studeni', 'Prosinac'],
|
||||
['Sij', 'Velj', 'Ožu', 'Tra', 'Svi', 'Lip', 'Srp', 'Kol', 'Ruj', 'Lis', 'Stu', 'Pro'],
|
||||
['Ned', 'Pon', 'Uto', 'Sri', 'Čet', 'Pet', 'Sub']
|
||||
)
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
@@ -0,0 +1,10 @@
|
||||
import Language from '../Language'
|
||||
|
||||
export default new Language(
|
||||
'Hungarian',
|
||||
['Január', 'Február', 'Március', 'Április', 'Május', 'Június', 'Július', 'Augusztus', 'Szeptember', 'Október', 'November', 'December'],
|
||||
['Jan', 'Febr', 'Márc', 'Ápr', 'Máj', 'Jún', 'Júl', 'Aug', 'Szept', 'Okt', 'Nov', 'Dec'],
|
||||
['Vas', 'Hét', 'Ke', 'Sze', 'Csü', 'Pén', 'Szo']
|
||||
)
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
@@ -0,0 +1,10 @@
|
||||
import Language from '../Language'
|
||||
|
||||
export default new Language(
|
||||
'Indonesian',
|
||||
['Januari', 'Februari', 'Maret', 'April', 'Mei', 'Juni', 'Juli', 'Agustus', 'September', 'Oktober', 'November', 'Desember'],
|
||||
['Jan', 'Feb', 'Mar', 'Apr', 'Mei', 'Jun', 'Jul', 'Agu', 'Sep', 'Okt', 'Nov', 'Des'],
|
||||
['Min', 'Sen', 'Sel', 'Rab', 'Kam', 'Jum', 'Sab']
|
||||
)
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
@@ -0,0 +1,10 @@
|
||||
import Language from '../Language'
|
||||
|
||||
export default new Language(
|
||||
'Icelandic',
|
||||
['Janúar', 'Febrúar', 'Mars', 'Apríl', 'Maí', 'Júní', 'Júlí', 'Ágúst', 'September', 'Október', 'Nóvember', 'Desember'],
|
||||
['Jan', 'Feb', 'Mars', 'Apr', 'Maí', 'Jún', 'Júl', 'Ágú', 'Sep', 'Okt', 'Nóv', 'Des'],
|
||||
['Sun', 'Mán', 'Þri', 'Mið', 'Fim', 'Fös', 'Lau']
|
||||
)
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
@@ -0,0 +1,10 @@
|
||||
import Language from '../Language'
|
||||
|
||||
export default new Language(
|
||||
'Italian',
|
||||
['Gennaio', 'Febbraio', 'Marzo', 'Aprile', 'Maggio', 'Giugno', 'Luglio', 'Agosto', 'Settembre', 'Ottobre', 'Novembre', 'Dicembre'],
|
||||
['Gen', 'Feb', 'Mar', 'Apr', 'Mag', 'Giu', 'Lug', 'Ago', 'Set', 'Ott', 'Nov', 'Dic'],
|
||||
['Dom', 'Lun', 'Mar', 'Mer', 'Gio', 'Ven', 'Sab']
|
||||
)
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
@@ -0,0 +1,15 @@
|
||||
import Language from '../Language'
|
||||
|
||||
const language = new Language(
|
||||
'Japanese',
|
||||
['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'],
|
||||
['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'],
|
||||
['日', '月', '火', '水', '木', '金', '土']
|
||||
)
|
||||
|
||||
language.yearSuffix = '年'
|
||||
language.ymd = true
|
||||
|
||||
export default language
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
@@ -0,0 +1,10 @@
|
||||
import Language from '../Language'
|
||||
|
||||
export default new Language(
|
||||
'Kazakh',
|
||||
['Қаңтар', 'Ақпан', 'Наурыз', 'Сәуір', 'Мамыр', 'Маусым', 'Шілде', 'Тамыз', 'Қыркүйек', 'Қазан', 'Қараша', 'Желтоқсан'],
|
||||
['Қаң', 'Ақп', 'Нау', 'Сәу', 'Мам', 'Мау', 'Шіл', 'Там', 'Қыр', 'Қаз', 'Қар', 'Жел'],
|
||||
['Жк', 'Дй', 'Сй', 'Ср', 'Бй', 'Жм', 'Сн']
|
||||
)
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
@@ -0,0 +1,14 @@
|
||||
import Language from '../Language'
|
||||
|
||||
const language = new Language(
|
||||
'Korean',
|
||||
['1월', '2월', '3월', '4월', '5월', '6월', '7월', '8월', '9월', '10월', '11월', '12월'],
|
||||
['1월', '2월', '3월', '4월', '5월', '6월', '7월', '8월', '9월', '10월', '11월', '12월'],
|
||||
['일', '월', '화', '수', '목', '금', '토']
|
||||
)
|
||||
language.yearSuffix = '년'
|
||||
language.ymd = true
|
||||
|
||||
export default language
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
@@ -0,0 +1,10 @@
|
||||
import Language from '../Language'
|
||||
|
||||
export default new Language(
|
||||
'Luxembourgish',
|
||||
['Januar', 'Februar', 'Mäerz', 'Abrëll', 'Mee', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember'],
|
||||
['Jan', 'Feb', 'Mäe', 'Abr', 'Mee', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dez'],
|
||||
['So.', 'Mé.', 'Dë.', 'Më.', 'Do.', 'Fr.', 'Sa.']
|
||||
)
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
@@ -0,0 +1,14 @@
|
||||
import Language from '../Language'
|
||||
|
||||
const language = new Language(
|
||||
'Lithuanian',
|
||||
['Sausis', 'Vasaris', 'Kovas', 'Balandis', 'Gegužė', 'Birželis', 'Liepa', 'Rugpjūtis', 'Rugsėjis', 'Spalis', 'Lapkritis', 'Gruodis'],
|
||||
['Sau', 'Vas', 'Kov', 'Bal', 'Geg', 'Bir', 'Lie', 'Rugp', 'Rugs', 'Spa', 'Lap', 'Gru'],
|
||||
['Sek', 'Pir', 'Ant', 'Tre', 'Ket', 'Pen', 'Šeš']
|
||||
)
|
||||
|
||||
language.ymd = true
|
||||
|
||||
export default language
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
@@ -0,0 +1,10 @@
|
||||
import Language from '../Language'
|
||||
|
||||
export default new Language(
|
||||
'Latvian',
|
||||
['Janvāris', 'Februāris', 'Marts', 'Aprīlis', 'Maijs', 'Jūnijs', 'Jūlijs', 'Augusts', 'Septembris', 'Oktobris', 'Novembris', 'Decembris'],
|
||||
['Jan', 'Feb', 'Mar', 'Apr', 'Mai', 'Jūn', 'Jūl', 'Aug', 'Sep', 'Okt', 'Nov', 'Dec'],
|
||||
['Sv', 'Pr', 'Ot', 'Tr', 'Ce', 'Pk', 'Se']
|
||||
)
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
@@ -0,0 +1,10 @@
|
||||
import Language from '../Language'
|
||||
|
||||
export default new Language(
|
||||
'Macedonian',
|
||||
['Јануари', 'Февруари', 'Март', 'Април', 'Мај', 'Јуни', 'Јули', 'Август', 'Септември', 'Октомври', 'Ноември', 'Декември'],
|
||||
['Јан', 'Фев', 'Мар', 'Апр', 'Мај', 'Јун', 'Јул', 'Авг', 'Сеп', 'Окт', 'Ное', 'Дек'],
|
||||
['Нед', 'Пон', 'Вто', 'Сре', 'Чет', 'Пет', 'Саб']
|
||||
)
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
@@ -0,0 +1,14 @@
|
||||
import Language from '../Language'
|
||||
|
||||
const language = new Language(
|
||||
'Mongolia',
|
||||
['1 дүгээр сар', '2 дугаар сар', '3 дугаар сар', '4 дүгээр сар', '5 дугаар сар', '6 дугаар сар', '7 дугаар сар', '8 дугаар сар', '9 дүгээр сар', '10 дугаар сар', '11 дүгээр сар', '12 дугаар сар'],
|
||||
['1-р сар', '2-р сар', '3-р сар', '4-р сар', '5-р сар', '6-р сар', '7-р сар', '8-р сар', '9-р сар', '10-р сар', '11-р сар', '12-р сар'],
|
||||
['Ня', 'Да', 'Мя', 'Лх', 'Пү', 'Ба', 'Бя']
|
||||
)
|
||||
|
||||
language.ymd = true
|
||||
|
||||
export default language
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
@@ -0,0 +1,10 @@
|
||||
import Language from '../Language'
|
||||
|
||||
export default new Language(
|
||||
'Norwegian Bokmål',
|
||||
['Januar', 'Februar', 'Mars', 'April', 'Mai', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'Desember'],
|
||||
['Jan', 'Feb', 'Mar', 'Apr', 'Mai', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Des'],
|
||||
['Sø', 'Ma', 'Ti', 'On', 'To', 'Fr', 'Lø']
|
||||
)
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
@@ -0,0 +1,10 @@
|
||||
import Language from '../Language'
|
||||
|
||||
export default new Language(
|
||||
'Dutch',
|
||||
['januari', 'februari', 'maart', 'april', 'mei', 'juni', 'juli', 'augustus', 'september', 'oktober', 'november', 'december'],
|
||||
['jan', 'feb', 'mrt', 'apr', 'mei', 'jun', 'jul', 'aug', 'sep', 'okt', 'nov', 'dec'],
|
||||
['zo', 'ma', 'di', 'wo', 'do', 'vr', 'za']
|
||||
)
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
@@ -0,0 +1,10 @@
|
||||
import Language from '../Language'
|
||||
|
||||
export default new Language(
|
||||
'Polish',
|
||||
['Styczeń', 'Luty', 'Marzec', 'Kwiecień', 'Maj', 'Czerwiec', 'Lipiec', 'Sierpień', 'Wrzesień', 'Październik', 'Listopad', 'Grudzień'],
|
||||
['Sty', 'Lut', 'Mar', 'Kwi', 'Maj', 'Cze', 'Lip', 'Sie', 'Wrz', 'Paź', 'Lis', 'Gru'],
|
||||
['Nd', 'Pn', 'Wt', 'Śr', 'Czw', 'Pt', 'Sob']
|
||||
)
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
@@ -0,0 +1,10 @@
|
||||
import Language from '../Language'
|
||||
|
||||
export default new Language(
|
||||
'Brazilian',
|
||||
['Janeiro', 'Fevereiro', 'Março', 'Abril', 'Maio', 'Junho', 'Julho', 'Agosto', 'Setembro', 'Outubro', 'Novembro', 'Dezembro'],
|
||||
['Jan', 'Fev', 'Mar', 'Abr', 'Mai', 'Jun', 'Jul', 'Ago', 'Set', 'Out', 'Nov', 'Dez'],
|
||||
['Dom', 'Seg', 'Ter', 'Qua', 'Qui', 'Sex', 'Sab']
|
||||
)
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
@@ -0,0 +1,10 @@
|
||||
import Language from '../Language'
|
||||
|
||||
export default new Language(
|
||||
'Romanian',
|
||||
['Ianuarie', 'Februarie', 'Martie', 'Aprilie', 'Mai', 'Iunie', 'Iulie', 'August', 'Septembrie', 'Octombrie', 'Noiembrie', 'Decembrie'],
|
||||
['Ian', 'Feb', 'Mar', 'Apr', 'Mai', 'Iun', 'Iul', 'Aug', 'Sep', 'Oct', 'Noi', 'Dec'],
|
||||
['D', 'L', 'Ma', 'Mi', 'J', 'V', 'S']
|
||||
)
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
@@ -0,0 +1,10 @@
|
||||
import Language from '../Language'
|
||||
|
||||
export default new Language(
|
||||
'Russian',
|
||||
['Январь', 'Февраль', 'Март', 'Апрель', 'Май', 'Июнь', 'Июль', 'Август', 'Сентябрь', 'Октябрь', 'Ноябрь', 'Декабрь'],
|
||||
['Янв', 'Февр', 'Март', 'Апр', 'Май', 'Июнь', 'Июль', 'Авг', 'Сент', 'Окт', 'Нояб', 'Дек'],
|
||||
['Вс', 'Пн', 'Вт', 'Ср', 'Чт', 'Пт', 'Сб']
|
||||
)
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
@@ -0,0 +1,10 @@
|
||||
import Language from '../Language'
|
||||
|
||||
export default new Language(
|
||||
'Slovakian',
|
||||
['január', 'február', 'marec', 'apríl', 'máj', 'jún', 'júl', 'august', 'september', 'október', 'november', 'december'],
|
||||
['jan', 'feb', 'mar', 'apr', 'máj', 'jún', 'júl', 'aug', 'sep', 'okt', 'nov', 'dec'],
|
||||
['ne', 'po', 'ut', 'st', 'št', 'pi', 'so']
|
||||
)
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
@@ -0,0 +1,10 @@
|
||||
import Language from '../Language'
|
||||
|
||||
export default new Language(
|
||||
'Sloveian',
|
||||
['Januar', 'Februar', 'Marec', 'April', 'Maj', 'Junij', 'Julij', 'Avgust', 'September', 'Oktober', 'November', 'December'],
|
||||
['Jan', 'Feb', 'Mar', 'Apr', 'Maj', 'Jun', 'Jul', 'Avg', 'Sep', 'Okt', 'Nov', 'Dec'],
|
||||
['Ned', 'Pon', 'Tor', 'Sre', 'Čet', 'Pet', 'Sob']
|
||||
)
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
@@ -0,0 +1,10 @@
|
||||
import Language from '../Language'
|
||||
|
||||
export default new Language(
|
||||
'Serbian in Cyrillic script',
|
||||
['Јануар', 'Фебруар', 'Март', 'Април', 'Мај', 'Јун', 'Јул', 'Август', 'Септембар', 'Октобар', 'Новембар', 'Децембар'],
|
||||
['Јан', 'Феб', 'Мар', 'Апр', 'Мај', 'Јун', 'Јул', 'Авг', 'Сеп', 'Окт', 'Нов', 'Дец'],
|
||||
['Нед', 'Пон', 'Уто', 'Сре', 'Чет', 'Пет', 'Суб']
|
||||
)
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
@@ -0,0 +1,10 @@
|
||||
import Language from '../Language'
|
||||
|
||||
export default new Language(
|
||||
'Serbian',
|
||||
['Januar', 'Februar', 'Mart', 'April', 'Maj', 'Jun', 'Jul', 'Avgust', 'Septembar', 'Oktobar', 'Novembar', 'Decembar'],
|
||||
['Jan', 'Feb', 'Mar', 'Apr', 'Maj', 'Jun', 'Jul', 'Avg', 'Sep', 'Okt', 'Nov', 'Dec'],
|
||||
['Ned', 'Pon', 'Uto', 'Sre', 'Čet', 'Pet', 'Sub']
|
||||
)
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
@@ -0,0 +1,10 @@
|
||||
import Language from '../Language'
|
||||
|
||||
export default new Language(
|
||||
'Swedish',
|
||||
['Januari', 'Februari', 'Mars', 'April', 'Maj', 'Juni', 'Juli', 'Augusti', 'September', 'Oktober', 'November', 'December'],
|
||||
['Jan', 'Feb', 'Mar', 'Apr', 'Maj', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dec'],
|
||||
['Sön', 'Mån', 'Tis', 'Ons', 'Tor', 'Fre', 'Lör']
|
||||
)
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
@@ -0,0 +1,10 @@
|
||||
import Language from '../Language'
|
||||
|
||||
export default new Language(
|
||||
'Thai',
|
||||
['มกราคม', 'กุมภาพันธ์', 'มีนาคม', 'เมษายน', 'พฤษภาคม', 'มิถุนายน', 'กรกฎาคม', 'สิงหาคม', 'กันยายน', 'ตุลาคม', 'พฤศจิกายน', 'ธันวาคม'],
|
||||
['ม.ค.', 'ก.พ.', 'มี.ค.', 'เม.ย.', 'พ.ค.', 'มิ.ย.', 'ก.ค.', 'ส.ค.', 'ก.ย.', 'ต.ค.', 'พ.ย.', 'ธ.ค.'],
|
||||
['อา', 'จ', 'อ', 'พ', 'พฤ', 'ศ', 'ส']
|
||||
)
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
@@ -0,0 +1,10 @@
|
||||
import Language from '../Language'
|
||||
|
||||
export default new Language(
|
||||
'Turkish',
|
||||
['Ocak', 'Şubat', 'Mart', 'Nisan', 'Mayıs', 'Haziran', 'Temmuz', 'Ağustos', 'Eylül', 'Ekim', 'Kasım', 'Aralık'],
|
||||
['Oca', 'Şub', 'Mar', 'Nis', 'May', 'Haz', 'Tem', 'Ağu', 'Eyl', 'Eki', 'Kas', 'Ara'],
|
||||
['Paz', 'Pzt', 'Sal', 'Çar', 'Per', 'Cum', 'Cmt']
|
||||
)
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
@@ -0,0 +1,10 @@
|
||||
import Language from '../Language'
|
||||
|
||||
export default new Language(
|
||||
'Ukraine',
|
||||
['Січень', 'Лютий', 'Березень', 'Квітень', 'Травень', 'Червень', 'Липень', 'Серпень', 'Вересень', 'Жовтень', 'Листопад', 'Грудень'],
|
||||
['Січ', 'Лют', 'Бер', 'Квіт', 'Трав', 'Чер', 'Лип', 'Серп', 'Вер', 'Жовт', 'Лист', 'Груд'],
|
||||
['Нд', 'Пн', 'Вт', 'Ср', 'Чт', 'Пт', 'Сб']
|
||||
)
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
@@ -0,0 +1,14 @@
|
||||
import Language from '../Language'
|
||||
|
||||
const language = new Language(
|
||||
'Urdu',
|
||||
['جنوری', 'فروری', 'مارچ', 'اپریل', 'مئی', 'جون', 'جولائی', 'اگست', 'سپتمبر', 'اکتوبر', 'نومبر', 'دسمبر'],
|
||||
['جنوری', 'فروری', 'مارچ', 'اپریل', 'مئی', 'جون', 'جولائی', 'اگست', 'سپتمبر', 'اکتوبر', 'نومبر', 'دسمبر'],
|
||||
['اتوار', 'پیر', 'منگل', 'بدھ', 'جمعرات', 'جمعہ', 'ہفتہ']
|
||||
)
|
||||
|
||||
language.rtl = true
|
||||
|
||||
export default language
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
@@ -0,0 +1,10 @@
|
||||
import Language from '../Language'
|
||||
|
||||
export default new Language(
|
||||
'Vietnamese',
|
||||
['Tháng 1', 'Tháng 2', 'Tháng 3', 'Tháng 4', 'Tháng 5', 'Tháng 6', 'Tháng 7', 'Tháng 8', 'Tháng 9', 'Tháng 10', 'Tháng 11', 'Tháng 12'],
|
||||
['T 01', 'T 02', 'T 03', 'T 04', 'T 05', 'T 06', 'T 07', 'T 08', 'T 09', 'T 10', 'T 11', 'T 12'],
|
||||
['CN', 'Thứ 2', 'Thứ 3', 'Thứ 4', 'Thứ 5', 'Thứ 6', 'Thứ 7']
|
||||
)
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
@@ -0,0 +1,11 @@
|
||||
import Language from '../Language'
|
||||
|
||||
const language = new Language(
|
||||
'Chinese_HK',
|
||||
['壹月', '贰月', '叁月', '肆月', '伍月', '陆月', '柒月', '捌月', '玖月', '拾月', '拾壹月', '拾贰月'],
|
||||
['壹月', '贰月', '叁月', '肆月', '伍月', '陆月', '柒月', '捌月', '玖月', '拾月', '拾壹月', '拾贰月'],
|
||||
['日', '壹', '贰', '叁', '肆', '伍', '陆']
|
||||
)
|
||||
language.yearSuffix = '年'
|
||||
|
||||
export default language
|
||||
@@ -0,0 +1,13 @@
|
||||
import Language from '../Language'
|
||||
|
||||
const language = new Language(
|
||||
'Chinese',
|
||||
['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'],
|
||||
['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'],
|
||||
['日', '一', '二', '三', '四', '五', '六']
|
||||
)
|
||||
language.yearSuffix = '年'
|
||||
|
||||
export default language
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
@@ -0,0 +1,252 @@
|
||||
import en from '../locale/translations/en'
|
||||
|
||||
const utils = {
|
||||
/**
|
||||
* @type {Boolean}
|
||||
*/
|
||||
useUtc: false,
|
||||
/**
|
||||
* Returns the full year, using UTC or not
|
||||
* @param {Date} date
|
||||
*/
|
||||
getFullYear (date) {
|
||||
return this.useUtc ? date.getUTCFullYear() : date.getFullYear()
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the month, using UTC or not
|
||||
* @param {Date} date
|
||||
*/
|
||||
getMonth (date) {
|
||||
return this.useUtc ? date.getUTCMonth() : date.getMonth()
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the date, using UTC or not
|
||||
* @param {Date} date
|
||||
*/
|
||||
getDate (date) {
|
||||
return this.useUtc ? date.getUTCDate() : date.getDate()
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the day, using UTC or not
|
||||
* @param {Date} date
|
||||
*/
|
||||
getDay (date) {
|
||||
return this.useUtc ? date.getUTCDay() : date.getDay()
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the hours, using UTC or not
|
||||
* @param {Date} date
|
||||
*/
|
||||
getHours (date) {
|
||||
return this.useUtc ? date.getUTCHours() : date.getHours()
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the minutes, using UTC or not
|
||||
* @param {Date} date
|
||||
*/
|
||||
getMinutes (date) {
|
||||
return this.useUtc ? date.getUTCMinutes() : date.getMinutes()
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets the full year, using UTC or not
|
||||
* @param {Date} date
|
||||
*/
|
||||
setFullYear (date, value, useUtc) {
|
||||
return this.useUtc ? date.setUTCFullYear(value) : date.setFullYear(value)
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets the month, using UTC or not
|
||||
* @param {Date} date
|
||||
*/
|
||||
setMonth (date, value, useUtc) {
|
||||
return this.useUtc ? date.setUTCMonth(value) : date.setMonth(value)
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets the date, using UTC or not
|
||||
* @param {Date} date
|
||||
* @param {Number} value
|
||||
*/
|
||||
setDate (date, value, useUtc) {
|
||||
return this.useUtc ? date.setUTCDate(value) : date.setDate(value)
|
||||
},
|
||||
|
||||
/**
|
||||
* Check if date1 is equivalent to date2, without comparing the time
|
||||
* @see https://stackoverflow.com/a/6202196/4455925
|
||||
* @param {Date} date1
|
||||
* @param {Date} date2
|
||||
*/
|
||||
compareDates (date1, date2) {
|
||||
const d1 = new Date(date1.getTime())
|
||||
const d2 = new Date(date2.getTime())
|
||||
|
||||
if (this.useUtc) {
|
||||
d1.setUTCHours(0, 0, 0, 0)
|
||||
d2.setUTCHours(0, 0, 0, 0)
|
||||
} else {
|
||||
d1.setHours(0, 0, 0, 0)
|
||||
d2.setHours(0, 0, 0, 0)
|
||||
}
|
||||
return d1.getTime() === d2.getTime()
|
||||
},
|
||||
|
||||
/**
|
||||
* Validates a date object
|
||||
* @param {Date} date - an object instantiated with the new Date constructor
|
||||
* @return {Boolean}
|
||||
*/
|
||||
isValidDate (date) {
|
||||
if (Object.prototype.toString.call(date) !== '[object Date]') {
|
||||
return false
|
||||
}
|
||||
return !isNaN(date.getTime())
|
||||
},
|
||||
|
||||
/**
|
||||
* Return abbreviated week day name
|
||||
* @param {Date}
|
||||
* @param {Array}
|
||||
* @return {String}
|
||||
*/
|
||||
getDayNameAbbr (date, days) {
|
||||
if (typeof date !== 'object') {
|
||||
throw TypeError('Invalid Type')
|
||||
}
|
||||
return days[this.getDay(date)]
|
||||
},
|
||||
|
||||
/**
|
||||
* Return name of the month
|
||||
* @param {Number|Date}
|
||||
* @param {Array}
|
||||
* @return {String}
|
||||
*/
|
||||
getMonthName (month, months) {
|
||||
if (!months) {
|
||||
throw Error('missing 2nd parameter Months array')
|
||||
}
|
||||
if (typeof month === 'object') {
|
||||
return months[this.getMonth(month)]
|
||||
}
|
||||
if (typeof month === 'number') {
|
||||
return months[month]
|
||||
}
|
||||
throw TypeError('Invalid type')
|
||||
},
|
||||
|
||||
/**
|
||||
* Return an abbreviated version of the month
|
||||
* @param {Number|Date}
|
||||
* @return {String}
|
||||
*/
|
||||
getMonthNameAbbr (month, monthsAbbr) {
|
||||
if (!monthsAbbr) {
|
||||
throw Error('missing 2nd paramter Months array')
|
||||
}
|
||||
if (typeof month === 'object') {
|
||||
return monthsAbbr[this.getMonth(month)]
|
||||
}
|
||||
if (typeof month === 'number') {
|
||||
return monthsAbbr[month]
|
||||
}
|
||||
throw TypeError('Invalid type')
|
||||
},
|
||||
|
||||
/**
|
||||
* Alternative get total number of days in month
|
||||
* @param {Number} year
|
||||
* @param {Number} m
|
||||
* @return {Number}
|
||||
*/
|
||||
daysInMonth (year, month) {
|
||||
return /8|3|5|10/.test(month) ? 30 : month === 1 ? (!(year % 4) && year % 100) || !(year % 400) ? 29 : 28 : 31
|
||||
},
|
||||
|
||||
/**
|
||||
* Get nth suffix for date
|
||||
* @param {Number} day
|
||||
* @return {String}
|
||||
*/
|
||||
getNthSuffix (day) {
|
||||
switch (day) {
|
||||
case 1:
|
||||
case 21:
|
||||
case 31:
|
||||
return 'st'
|
||||
case 2:
|
||||
case 22:
|
||||
return 'nd'
|
||||
case 3:
|
||||
case 23:
|
||||
return 'rd'
|
||||
default:
|
||||
return 'th'
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Formats date object
|
||||
* @param {Date}
|
||||
* @param {String}
|
||||
* @param {Object}
|
||||
* @return {String}
|
||||
*/
|
||||
formatDate (date, format, translation) {
|
||||
translation = (!translation) ? en : translation
|
||||
let year = this.getFullYear(date)
|
||||
let month = this.getMonth(date) + 1
|
||||
let day = this.getDate(date)
|
||||
let str = format
|
||||
.replace(/dd/, ('0' + day).slice(-2))
|
||||
.replace(/d/, day)
|
||||
.replace(/yyyy/, year)
|
||||
.replace(/yy/, String(year).slice(2))
|
||||
.replace(/MMMM/, this.getMonthName(this.getMonth(date), translation.months))
|
||||
.replace(/MMM/, this.getMonthNameAbbr(this.getMonth(date), translation.monthsAbbr))
|
||||
.replace(/MM/, ('0' + month).slice(-2))
|
||||
.replace(/M(?!a|ä|e)/, month)
|
||||
.replace(/su/, this.getNthSuffix(this.getDate(date)))
|
||||
.replace(/D(?!e|é|i)/, this.getDayNameAbbr(date, translation.days))
|
||||
return str
|
||||
},
|
||||
|
||||
/**
|
||||
* Creates an array of dates for each day in between two dates.
|
||||
* @param {Date} start
|
||||
* @param {Date} end
|
||||
* @return {Array}
|
||||
*/
|
||||
createDateArray (start, end) {
|
||||
let dates = []
|
||||
while (start <= end) {
|
||||
dates.push(new Date(start))
|
||||
start = this.setDate(new Date(start), this.getDate(new Date(start)) + 1)
|
||||
}
|
||||
return dates
|
||||
},
|
||||
|
||||
/**
|
||||
* method used as a prop validator for input values
|
||||
* @param {*} val
|
||||
* @return {Boolean}
|
||||
*/
|
||||
validateDateInput (val) {
|
||||
return val === null || val instanceof Date || typeof val === 'string' || typeof val === 'number'
|
||||
}
|
||||
}
|
||||
|
||||
export const makeDateUtils = useUtc => ({...utils, useUtc})
|
||||
|
||||
export default {
|
||||
...utils
|
||||
}
|
||||
// eslint-disable-next-line
|
||||
;
|
||||
@@ -0,0 +1,127 @@
|
||||
<!--
|
||||
Program: Editor
|
||||
Author: SDL
|
||||
Brief: Editor using Cafenote
|
||||
-->
|
||||
<template>
|
||||
<div :id="editorId" :ref="editorId"></div>
|
||||
</template>
|
||||
<script>
|
||||
import { mapGetters } from 'vuex';
|
||||
// import $ from 'jquery';
|
||||
import SDLUtil from '@/utils/SDLUtil';
|
||||
|
||||
export default {
|
||||
inheritAttrs: true,
|
||||
emits: ['update:modelValue'],
|
||||
props: {
|
||||
modelValue: {
|
||||
required: true,
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
format: {
|
||||
required: false,
|
||||
type: String,
|
||||
default: 'html',
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
cafe: {},
|
||||
editorId: 'editor',
|
||||
config: {
|
||||
language: this.getLocale(this.$i18n.locale),
|
||||
height: 250,
|
||||
plugins: 'autolink dragdrop expaste emoticon findreplace image lineheight link lists listsstyle lock media mhtmlloader pagebreak paragraphformat superscript subscript table typo2kr',
|
||||
default_font_size: '11',
|
||||
default_font_family: "SamsungOneKorean, '맑은 고딕', Gulim, Arial",
|
||||
default_line_height: '1.5',
|
||||
font_formats: ['SamsungOneKorean', '굴림', '굴림체', '궁서', '궁서체', '돋움', '돋움체', '맑은 고딕', '바탕', '바탕체', 'Arial', 'Courier', 'sans-serif', 'Verdana'],
|
||||
font_size_style_values: ['8pt', '9pt', '10pt', '11pt', '12pt', '14pt', '16pt', '20pt', '36pt', '48pt'],
|
||||
line_heights: ['0.5', '1', '1.2', '1.5', '1.9', '2', '3', '4', '5'],
|
||||
invalid_elements: 'script',
|
||||
codeview_indent: true,
|
||||
},
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters({
|
||||
locale: 'getLocale',
|
||||
}),
|
||||
},
|
||||
methods: {
|
||||
setContent() {
|
||||
if (this.modelValue && this.modelValue !== this.getContent()) {
|
||||
this.cafe.setContent(this.editorId, this.modelValue, { format: this.format });
|
||||
}
|
||||
},
|
||||
getContent() {
|
||||
if (this.format === 'html') {
|
||||
const html = this.cafe.getContent(this.editorId, { format: this.format });
|
||||
// return $($.parseHTML(html)[5]).html();
|
||||
}
|
||||
return this.cafe.getContent(this.editorId, { format: this.format });
|
||||
},
|
||||
loadComplete() {
|
||||
this.setContent();
|
||||
|
||||
// rich textarea
|
||||
// $('iframe')
|
||||
// .contents()
|
||||
// .find('#cafe-note-contents')
|
||||
// .bind('blur', () => {
|
||||
// this.$emit('update:modelValue', this.getContent());
|
||||
// });
|
||||
|
||||
// html textarea
|
||||
// $(`#${this.editorId}`)
|
||||
// .next()
|
||||
// .find('textarea')
|
||||
// .bind('blur', () => {
|
||||
// this.$emit('update:modelValue', this.getContent());
|
||||
// });
|
||||
SDLUtil.showLoadingBar(false);
|
||||
},
|
||||
getLocale(lang) {
|
||||
return lang === 'ko_KR' ? lang : 'en_US';
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
modelValue() {
|
||||
if (this.cafe.setContent) {
|
||||
this.setContent();
|
||||
}
|
||||
},
|
||||
locale(lang) {
|
||||
const content = this.getContent();
|
||||
this.cafe.destroy(this.editorId);
|
||||
this.config.language = this.getLocale(lang);
|
||||
this.cafe.create(this.editorId, this.config, () => {
|
||||
this.loadComplete();
|
||||
this.$emit('update:modelValue', content);
|
||||
});
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.editorId = `editor_${new Date().getTime()}${Math.floor(Math.random() * 100) + 1}`;
|
||||
},
|
||||
mounted() {
|
||||
this.$nextTick(() => {
|
||||
SDLUtil.showLoadingBar(true);
|
||||
SDLUtil.importJS(`${this.$CONTEXT}/static/libs/cafe/2.3.25/cafe_2.3.25.min.js`)
|
||||
.then(window => {
|
||||
this.cafe = window.cafe;
|
||||
this.cafe.create(this.editorId, this.config, this.loadComplete);
|
||||
})
|
||||
.catch(err => {
|
||||
console.error(err);
|
||||
});
|
||||
});
|
||||
},
|
||||
beforeUnmount() {
|
||||
this.cafe.destroy(this.editorId);
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style scoped></style>
|
||||
@@ -0,0 +1,3 @@
|
||||
import Editor from './Editor.vue';
|
||||
|
||||
export default Editor;
|
||||
@@ -0,0 +1,129 @@
|
||||
<!--
|
||||
Program: Excel Download
|
||||
Author: SDL
|
||||
Description: Excel Download Common Component
|
||||
-->
|
||||
<template>
|
||||
<button type="button" :class="btnClass" @click="excelDownload()">{{ $t(btnLabel) }}</button>
|
||||
</template>
|
||||
<script>
|
||||
import axios from 'axios';
|
||||
import SDLUtil from '@/utils/SDLUtil';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
btnLabel: {
|
||||
required: false,
|
||||
type: String,
|
||||
default: 'sdl.common.label.excelDownload',
|
||||
},
|
||||
btnClass: {
|
||||
type: String,
|
||||
default: 'btn btn-secondary',
|
||||
},
|
||||
excelInfo: {
|
||||
required: false,
|
||||
type: Object,
|
||||
},
|
||||
beforeClick: {
|
||||
type: Function,
|
||||
default: () => true,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {};
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* param:
|
||||
* return:
|
||||
* exception:
|
||||
* Description : click excel down
|
||||
*/
|
||||
excelDownload() {
|
||||
if (!this.beforeClick()) return;
|
||||
|
||||
SDLUtil.confirm({
|
||||
msg: 'sdl.common.message.doExcelDownload',
|
||||
// title: 'confirm title',
|
||||
// okLabel: 'ok label',
|
||||
// cancelLabel: 'Cancel',
|
||||
onOkEvt: () => this.download(),
|
||||
});
|
||||
},
|
||||
/**
|
||||
* param:
|
||||
* return:
|
||||
* exception:
|
||||
* Description : execute download
|
||||
*/
|
||||
download() {
|
||||
const getFileName = contentDisposition => {
|
||||
const fileName = contentDisposition
|
||||
.split(';')
|
||||
.filter(ele => ele.indexOf('filename') > -1)
|
||||
.map(ele => ele.replace(/"/g, '').split('=')[1]);
|
||||
return fileName[0] ? fileName[0] : null;
|
||||
};
|
||||
|
||||
SDLUtil.showLoadingBar(true);
|
||||
|
||||
const param = [];
|
||||
const encodeList = ['columnInfoFile', 'fileName', 'sheetName', 'param'];
|
||||
Object.keys(this.excelInfo).forEach(val => {
|
||||
if (encodeList.includes(val)) {
|
||||
if (val === 'param') {
|
||||
param.push(`${val}=${encodeURIComponent(JSON.stringify(this.excelInfo[val]))}`);
|
||||
} else {
|
||||
param.push(`${val}=${encodeURIComponent(this.excelInfo[val])}`);
|
||||
}
|
||||
} else {
|
||||
param.push(`${val}=${this.excelInfo[val]}`);
|
||||
}
|
||||
});
|
||||
|
||||
axios({
|
||||
url: `${SDLUtil.API_URL}/excel/excel-download?${param.join('&')}`,
|
||||
responseType: 'arraybuffer',
|
||||
method: 'get',
|
||||
})
|
||||
.then(response => {
|
||||
try {
|
||||
const blob = new Blob([response.data], { type: response.headers['content-type'] });
|
||||
let fileName = '';
|
||||
if (response.headers['content-disposition']) {
|
||||
fileName = getFileName(response.headers['content-disposition']);
|
||||
} else {
|
||||
// eslint-disable-next-line prefer-destructuring
|
||||
fileName = this.excelInfo.fileName;
|
||||
}
|
||||
|
||||
fileName = decodeURI(fileName); // File name decoding (option of use according to project)
|
||||
|
||||
if (window.navigator.msSaveOrOpenBlob) {
|
||||
// IE 10+
|
||||
window.navigator.msSaveOrOpenBlob(blob, fileName);
|
||||
} else {
|
||||
// not IE
|
||||
const link = document.createElement('a');
|
||||
link.href = window.URL.createObjectURL(blob);
|
||||
link.target = '_self';
|
||||
if (fileName) link.download = fileName;
|
||||
link.click();
|
||||
}
|
||||
SDLUtil.showLoadingBar(false);
|
||||
// return response.data;
|
||||
} catch (e) {
|
||||
SDLUtil.showLoadingBar(false);
|
||||
console.error(e);
|
||||
}
|
||||
})
|
||||
.catch(response => {
|
||||
SDLUtil.showLoadingBar(false);
|
||||
SDLUtil.alert(response.data);
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style></style>
|
||||
@@ -0,0 +1,119 @@
|
||||
<!--
|
||||
Program: Excel Upload
|
||||
Author: SDL
|
||||
Description: Excel Upload Common Component
|
||||
-->
|
||||
<template>
|
||||
<div class="input-group">ExcelUpload.vue
|
||||
<input type="file" class="form-control" @change="onChange" />
|
||||
<button type="button" :class="btnClass" @click="onBeforeUpload">{{ $t(btnLabel) }}</button>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import axios from 'axios';
|
||||
import SDLUtil from '@/utils/SDLUtil';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
btnLabel: {
|
||||
required: false,
|
||||
type: String,
|
||||
default: 'sdl.common.label.excelUpload',
|
||||
},
|
||||
btnClass: {
|
||||
type: String,
|
||||
default: 'btn btn-secondary',
|
||||
},
|
||||
excelInfo: {
|
||||
required: false,
|
||||
type: Object,
|
||||
},
|
||||
beforeClick: {
|
||||
type: Function,
|
||||
default: () => true,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
files: [],
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* param: event
|
||||
* return:
|
||||
* exception:
|
||||
* Description : Select attachment
|
||||
*/
|
||||
onChange(e) {
|
||||
this.files = e.target.files || e.dataTransfer.files;
|
||||
|
||||
const getFileExt = fileName => fileName.substring(fileName.lastIndexOf('.') + 1, fileName.length).toLowerCase();
|
||||
const extList = 'xls|xlsx'.split('|');
|
||||
|
||||
Object.keys(this.files).forEach(x => {
|
||||
if (!extList.includes(getFileExt(this.files[x].name))) {
|
||||
SDLUtil.alert(
|
||||
SDLUtil.getMsgProp('sdl.common.message.extensionCannotBeAttached', [`${getFileExt(this.files[x].name)}`]),
|
||||
);
|
||||
this.files = [];
|
||||
e.target.value = '';
|
||||
}
|
||||
});
|
||||
},
|
||||
/**
|
||||
* param:
|
||||
* return:
|
||||
* exception:
|
||||
* Description : click upload button
|
||||
*/
|
||||
onBeforeUpload() {
|
||||
if (this.files.length === 0) {
|
||||
SDLUtil.alert('sdl.common.message.selectFile');
|
||||
return;
|
||||
}
|
||||
if (!this.beforeClick()) return;
|
||||
|
||||
SDLUtil.confirm({
|
||||
msg: 'sdl.common.message.wouldYouLikeUploadExcel',
|
||||
// title: 'confirm title',
|
||||
// okLabel: 'ok label',
|
||||
// cancelLabel: 'Cancel',
|
||||
onOkEvt: () => this.upload(),
|
||||
});
|
||||
},
|
||||
/**
|
||||
* param:
|
||||
* return:
|
||||
* exception:
|
||||
* Description : upload attachment
|
||||
*/
|
||||
upload() {
|
||||
this.excelInfo.columnInfoFile = encodeURIComponent(this.excelInfo.columnInfoFile);
|
||||
const formData = new FormData();
|
||||
formData.append('excelFile', this.files[0]);
|
||||
|
||||
Object.keys(this.excelInfo).forEach(val => {
|
||||
formData.append(val, this.excelInfo[val]);
|
||||
});
|
||||
|
||||
SDLUtil.showLoadingBar(true);
|
||||
axios
|
||||
.post(`${SDLUtil.API_URL}/excel/excel-upload`, formData)
|
||||
.then(response => {
|
||||
// Loading bar hidden
|
||||
SDLUtil.showLoadingBar(false);
|
||||
// if (response.data !== '') {
|
||||
// this.$router.push(response.data);
|
||||
// }
|
||||
this.$emit('after-upload', response.data);
|
||||
})
|
||||
.catch(error => {
|
||||
SDLUtil.showLoadingBar(false);
|
||||
console.log(error);
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style></style>
|
||||
@@ -0,0 +1,4 @@
|
||||
import SdlExceldownload from './ExcelDownload.vue';
|
||||
import SdlExcelupload from './ExcelUpload.vue';
|
||||
|
||||
export { SdlExceldownload, SdlExcelupload };
|
||||
@@ -0,0 +1,806 @@
|
||||
<!--
|
||||
Program: Multi File Uploader
|
||||
Author: SDL
|
||||
Description: Multi File Common Component
|
||||
-->
|
||||
<template>
|
||||
<div class="ui--file-attachment" :class="{ active: modifyFlag && !itemsAdded, drag: modifyFlag && itemsAdded && dragging }">
|
||||
<p class="message"><i class="bi bi-cloud-upload"></i><span v-html="initMsg"></span></p>
|
||||
<input
|
||||
type="file"
|
||||
:id="`fileAttach${downloadType}`"
|
||||
name="items[]"
|
||||
multiple
|
||||
@change="onChange"
|
||||
class="addfile"
|
||||
@dragover="ignoreDrag"
|
||||
@dragenter="ignoreDrag"
|
||||
@drop="onChange"
|
||||
@dragend="dragging = false"
|
||||
@dragleave="dragging = false"
|
||||
:style="(modifyFlag && itemsAdded && dragging) || (modifyFlag && !itemsAdded) ? 'z-index:1001;position:absolute;top:0;bottom:0;left:0;right:0;width:100%;height:100%;' : 'width:0%;height:0%;z-index:-1;'"
|
||||
/>
|
||||
<div class="ui--file-list" :class="{ loaderArea: isLoaderVisible }" @dragenter="dragging = true">
|
||||
<!-- file-button -->
|
||||
<div class="ui--file-button" v-if="modifyFlag">
|
||||
<input type="checkbox" @click.stop v-model="selectAll" @change="toggleSelectAll" />
|
||||
<button type="button" class="btn ui--btn-icon-sm" @click="moveUp">
|
||||
<span class="ui--blind">{{ $t('sdl.common.label.moveup') }}</span
|
||||
><i class="bi bi-caret-up-fill"></i>
|
||||
</button>
|
||||
<button type="button" class="btn ui--btn-icon-sm" @click="moveDown">
|
||||
<span class="ui--blind">{{ $t('sdl.common.label.movedown') }}</span
|
||||
><i class="bi bi-caret-down-fill"></i>
|
||||
</button>
|
||||
<button type="button" class="btn ui--btn-icon-sm" @click="selectRemoveItem">
|
||||
<span class="ui--blind">{{ $t('sdl.common.label.delete') }}</span
|
||||
><i class="bi bi-x"></i>
|
||||
</button>
|
||||
<span class="ms-2 me-2 ui--slide">|</span>
|
||||
<span
|
||||
>{{ $t('sdl.common.label.total') }}<strong class="ms-1 me-2">{{ itemsAdded }}</strong
|
||||
>{{ itemsTotalSize }} / {{ getItemsMaxTotalSize }}</span
|
||||
>
|
||||
<label class="btn btn-outline-secondary ms-3 ui--btn-xs" style="margin-bottom: 0px" :for="`fileAttach${downloadType}`">
|
||||
{{ $t('sdl.common.label.fileAddition') }}
|
||||
</label>
|
||||
</div>
|
||||
<div class="ui--file-button" v-else>
|
||||
<input type="checkbox" @click.stop v-model="selectAll" @change="toggleSelectAll" />
|
||||
<span
|
||||
>{{ $t('sdl.common.label.total') }}<strong class="ms-1 me-2">{{ itemsAdded }}</strong
|
||||
>({{ itemsTotalSize }})</span
|
||||
>
|
||||
<!-- If you want to download compressed into a zip file -->
|
||||
<!-- <button type="button" @click="checkedDownload" class="btn btn-outline-secondary ms-3 ui--btn-xs">-->
|
||||
<button type="button" @click="checkedDownloadNonZip" class="btn btn-outline-secondary ms-3 ui--btn-xs">
|
||||
{{ $t('sdl.common.label.alldownload') }}
|
||||
</button>
|
||||
</div>
|
||||
<!-- // file-button -->
|
||||
<!-- file-list -->
|
||||
<ul>
|
||||
<li v-for="item in itemsNames" :key="item.fileName">
|
||||
<div class="form-check">
|
||||
<input type="checkbox" class="form-check-input" v-model="item.selected" @change="selectRow(item)" />
|
||||
<label class="form-check-label" @click="download(item)" :style="!modifyFlag ? 'cursor:pointer;' : ''">
|
||||
{{ item.fileName }}
|
||||
<small>
|
||||
<small>({{ bytesToSize(item.fileSize) }})</small>
|
||||
</small>
|
||||
</label>
|
||||
</div>
|
||||
<button type="button" class="btn ui--btn-icon-sm" v-show="modifyFlag" @click="removeItem(item)">
|
||||
<span class="ui--blind">{{ $t('sdl.common.label.delete') }}</span
|
||||
><i class="bi bi-x"></i>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="loader" :class="{ loaderArea: isLoaderVisible }" v-show="false">
|
||||
<div class="loaderImg"></div>
|
||||
</div>
|
||||
<!-- // file-list -->
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import axios from 'axios';
|
||||
import SDLUtil from '@/utils/SDLUtil';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
modifyFlag: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
uploadURL: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: `${SDLUtil.API_URL}/resource/attachments/multifile-upload`,
|
||||
},
|
||||
multiFileNm: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
downloadType: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
fileList: {
|
||||
type: Array,
|
||||
},
|
||||
useExtList: {
|
||||
type: String,
|
||||
default: 'zip|xlsx|xls|ppt|pptx',
|
||||
},
|
||||
maxItems: {
|
||||
type: Number,
|
||||
default: 10,
|
||||
},
|
||||
method: {
|
||||
type: String,
|
||||
default: 'post',
|
||||
},
|
||||
// postMeta: {
|
||||
// type: [String, Array, Object],
|
||||
// default: '',
|
||||
// },
|
||||
// postData: {
|
||||
// type: [Object],
|
||||
// default: () => {},
|
||||
// },
|
||||
postHeader: {
|
||||
type: [Object],
|
||||
default: () => {},
|
||||
},
|
||||
httpMethodErrorMessage: {
|
||||
type: String,
|
||||
default: "This HTTP method is not allowed. Please use either 'put' or 'post' methods.",
|
||||
},
|
||||
maxTotalSize: {
|
||||
type: Number,
|
||||
default: 1073741824,
|
||||
},
|
||||
},
|
||||
|
||||
/*
|
||||
* The component's data.
|
||||
*/
|
||||
data() {
|
||||
return {
|
||||
dragging: false,
|
||||
items: [],
|
||||
itemsAdded: '',
|
||||
itemsNames: [],
|
||||
itemsTotalSize: 0,
|
||||
itemsMaxTotalSize: 0,
|
||||
formData: '',
|
||||
successMsg: '',
|
||||
errorMsg: '',
|
||||
isLoaderVisible: false,
|
||||
fileSizes: 0,
|
||||
selectAll: false,
|
||||
docId: '',
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
/**
|
||||
* param:
|
||||
* return:
|
||||
* exception:
|
||||
* Description : get maximum attachment capacity
|
||||
*/
|
||||
getItemsMaxTotalSize() {
|
||||
return this.bytesToSize(this.maxTotalSize);
|
||||
},
|
||||
initMsg() {
|
||||
return `${this.$t('sdl.common.message.uploadTheAttachedFileHere')}(${this.getItemsMaxTotalSize})`;
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.init();
|
||||
},
|
||||
methods: {
|
||||
// http://scratch99.com/web-development/javascript/convert-bytes-to-mb-kb/
|
||||
/**
|
||||
* param: bytes -> String
|
||||
* return: String
|
||||
* exception:
|
||||
* Description : convert bytes to size
|
||||
*/
|
||||
bytesToSize(bytes) {
|
||||
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
|
||||
if (bytes === 0) return '0.00KB';
|
||||
const i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)), 10);
|
||||
if (i === 0) return `${bytes} ${sizes[i]}`;
|
||||
// eslint-disable-next-line no-restricted-properties
|
||||
return `${(bytes / Math.pow(1024, i)).toFixed(2)} ${sizes[i]}`;
|
||||
},
|
||||
/**
|
||||
* param:
|
||||
* return:
|
||||
* exception:
|
||||
* Description : select attch file
|
||||
*/
|
||||
selectRow(item) {
|
||||
if (!item.selected) {
|
||||
if (this.selectAll) {
|
||||
this.selectAll = false;
|
||||
}
|
||||
}
|
||||
},
|
||||
/**
|
||||
* param:
|
||||
* return:
|
||||
* exception:
|
||||
* Description : If you select all
|
||||
*/
|
||||
toggleSelectAll() {
|
||||
if (this.selectAll) {
|
||||
for (let i = 0; i < this.itemsNames.length; i += 1) {
|
||||
const selectItem = this.itemsNames[i];
|
||||
selectItem.selected = true;
|
||||
this.itemsNames.splice(i, 1, selectItem);
|
||||
}
|
||||
} else {
|
||||
for (let i = 0; i < this.itemsNames.length; i += 1) {
|
||||
const selectItem = this.itemsNames[i];
|
||||
selectItem.selected = false;
|
||||
this.itemsNames.splice(i, 1, selectItem);
|
||||
}
|
||||
}
|
||||
},
|
||||
/**
|
||||
* param:
|
||||
* return:
|
||||
* exception:
|
||||
* Description : move up
|
||||
*/
|
||||
moveUp() {
|
||||
for (let i = 0; i < this.itemsNames.length; i += 1) {
|
||||
if (this.itemsNames[i].selected) {
|
||||
if (i === 0) {
|
||||
return;
|
||||
}
|
||||
const tmpItem = this.itemsNames[i];
|
||||
this.itemsNames.splice(i, 1, this.itemsNames[i - 1]);
|
||||
this.itemsNames.splice(i - 1, 1, tmpItem);
|
||||
}
|
||||
}
|
||||
},
|
||||
/**
|
||||
* param:
|
||||
* return:
|
||||
* exception:
|
||||
* Description : move down
|
||||
*/
|
||||
moveDown() {
|
||||
for (let i = this.itemsNames.length - 1; i >= 0; i -= 1) {
|
||||
if (this.itemsNames[i].selected) {
|
||||
if (i === this.itemsNames.length - 1) {
|
||||
return;
|
||||
}
|
||||
const tmpItem = this.itemsNames[i];
|
||||
this.itemsNames.splice(i, 1, this.itemsNames[i + 1]);
|
||||
this.itemsNames.splice(i + 1, 1, tmpItem);
|
||||
}
|
||||
}
|
||||
},
|
||||
/**
|
||||
* param:
|
||||
* return:
|
||||
* exception:
|
||||
* Description : Delete selected files
|
||||
*/
|
||||
selectRemoveItem() {
|
||||
let flag = true;
|
||||
const selectedItemList = [];
|
||||
this.itemsNames.forEach(item => {
|
||||
if (item.selected) selectedItemList.push(item);
|
||||
});
|
||||
selectedItemList.forEach(selectedItem => {
|
||||
this.removeItem(selectedItem);
|
||||
flag = false;
|
||||
});
|
||||
if (flag) {
|
||||
SDLUtil.alert('sdl.common.message.selectRow');
|
||||
}
|
||||
},
|
||||
/**
|
||||
* param:
|
||||
* return:
|
||||
* exception:
|
||||
* Description : Download selected files
|
||||
*/
|
||||
checkedDownload() {
|
||||
let flag = true;
|
||||
const items = [];
|
||||
let fileName = '';
|
||||
for (let i = 0; i < this.itemsNames.length; i += 1) {
|
||||
if (this.itemsNames[i].selected) {
|
||||
if (fileName === '') {
|
||||
fileName = this.itemsNames[i].fileName.substring(fileName.lastIndexOf('.'));
|
||||
}
|
||||
items.push(this.itemsNames[i].fileId);
|
||||
flag = false;
|
||||
}
|
||||
}
|
||||
if (flag) {
|
||||
SDLUtil.alert('sdl.common.message.selectTheFileToDownload');
|
||||
} else if (items.length > 1) {
|
||||
this.download({ fileIds: items, fileName });
|
||||
} else {
|
||||
this.download({ fileId: items[0], fileName });
|
||||
}
|
||||
},
|
||||
async checkedDownloadNonZip() {
|
||||
let flag = true;
|
||||
const items = [];
|
||||
let fileName = '';
|
||||
for (let i = 0; i < this.itemsNames.length; i += 1) {
|
||||
if (this.itemsNames[i].selected) {
|
||||
if (fileName === '') {
|
||||
fileName = this.itemsNames[i].fileName.substring(fileName.lastIndexOf('.'));
|
||||
}
|
||||
items.push(this.itemsNames[i].fileId);
|
||||
flag = false;
|
||||
}
|
||||
}
|
||||
if (flag) {
|
||||
SDLUtil.alert('sdl.common.message.selectTheFileToDownload');
|
||||
} else if (items.length > 1) {
|
||||
for (let id of items) {
|
||||
await this.asyncDownload({ fileId: id });
|
||||
}
|
||||
} else {
|
||||
await this.asyncDownload({ fileId: items[0], fileName });
|
||||
}
|
||||
},
|
||||
/**
|
||||
* param:
|
||||
* return:
|
||||
* exception:
|
||||
* Description : download files
|
||||
*/
|
||||
download(params) {
|
||||
if (this.modifyFlag) return;
|
||||
let path = '';
|
||||
if (typeof params.fileIds !== 'undefined') {
|
||||
if (this.multiFileNm !== '') {
|
||||
params.fileName = this.multiFileNm;
|
||||
}
|
||||
if (this.docId) {
|
||||
// case approval
|
||||
path = `${SDLUtil.API_URL}/approval/approval-documents/${this.docId}/attachments?fileIds=${params.fileIds}&zipFileName=${params.fileName}`;
|
||||
} else {
|
||||
path = `${SDLUtil.API_URL}/resource/attachments/multifile-download?fileIds=${params.fileIds}&downloadType=${this.downloadType}&zipFileName=${params.fileName}`;
|
||||
}
|
||||
} else if (this.docId) {
|
||||
// case approval
|
||||
path = `${SDLUtil.API_URL}/approval/approval-documents/${this.docId}/attachments/${params.fileId}`;
|
||||
} else {
|
||||
path = `${SDLUtil.API_URL}/resource/attachments/file-download/${params.fileId}?downloadType=${this.downloadType}`;
|
||||
}
|
||||
|
||||
const getFileName = contentDisposition => {
|
||||
const fileName = contentDisposition
|
||||
.split(';')
|
||||
.filter(ele => ele.indexOf('filename') > -1)
|
||||
.map(ele => ele.replace(/"/g, '').split('=')[1]);
|
||||
return fileName[0] ? fileName[0] : null;
|
||||
};
|
||||
|
||||
SDLUtil.showLoadingBar(true);
|
||||
axios({
|
||||
url: path,
|
||||
responseType: 'arraybuffer',
|
||||
method: 'get',
|
||||
})
|
||||
.then(response => {
|
||||
try {
|
||||
if (response.headers['content-type'] === 'application/json') {
|
||||
response.data = JSON.parse(String.fromCharCode.apply(null, Array.from(new Uint8Array(response.data))));
|
||||
SDLUtil.showLoadingBar(false);
|
||||
SDLUtil.errorAlert(response);
|
||||
return;
|
||||
}
|
||||
const blob = new Blob([response.data], { type: response.headers['content-type'] });
|
||||
|
||||
let fileName = '';
|
||||
if (response.headers['content-disposition']) {
|
||||
fileName = getFileName(response.headers['content-disposition']);
|
||||
} else {
|
||||
// eslint-disable-next-line prefer-destructuring
|
||||
fileName = params.fileName;
|
||||
}
|
||||
|
||||
fileName = decodeURI(fileName);
|
||||
|
||||
if (window.navigator.msSaveOrOpenBlob) {
|
||||
// IE 10+
|
||||
window.navigator.msSaveOrOpenBlob(blob, fileName);
|
||||
} else {
|
||||
// not IE
|
||||
const link = document.createElement('a');
|
||||
link.href = window.URL.createObjectURL(blob);
|
||||
link.target = '_self';
|
||||
if (fileName) link.download = fileName;
|
||||
link.click();
|
||||
}
|
||||
SDLUtil.showLoadingBar(false);
|
||||
// return response.data;
|
||||
} catch (e) {
|
||||
SDLUtil.showLoadingBar(false);
|
||||
console.error(e);
|
||||
}
|
||||
})
|
||||
.catch(response => {
|
||||
SDLUtil.showLoadingBar(false);
|
||||
SDLUtil.alert(response.data);
|
||||
});
|
||||
},
|
||||
async asyncDownload(params) {
|
||||
if (this.modifyFlag) return;
|
||||
let path = '';
|
||||
if (typeof params.fileIds !== 'undefined') {
|
||||
if (this.multiFileNm !== '') {
|
||||
params.fileName = this.multiFileNm;
|
||||
}
|
||||
if (this.docId) {
|
||||
// case approval
|
||||
path = `${SDLUtil.API_URL}/approval/approval-documents/${this.docId}/attachments?fileIds=${params.fileIds}&zipFileName=${params.fileName}`;
|
||||
} else {
|
||||
path = `${SDLUtil.API_URL}/resource/attachments/multifile-download?fileIds=${params.fileIds}&downloadType=${this.downloadType}&zipFileName=${params.fileName}`;
|
||||
}
|
||||
} else if (this.docId) {
|
||||
// case approval
|
||||
path = `${SDLUtil.API_URL}/approval/approval-documents/${this.docId}/attachments/${params.fileId}`;
|
||||
} else {
|
||||
path = `${SDLUtil.API_URL}/resource/attachments/file-download/${params.fileId}?downloadType=${this.downloadType}`;
|
||||
}
|
||||
|
||||
const getFileName = contentDisposition => {
|
||||
const fileName = contentDisposition
|
||||
.split(';')
|
||||
.filter(ele => ele.indexOf('filename') > -1)
|
||||
.map(ele => ele.replace(/"/g, '').split('=')[1]);
|
||||
return fileName[0] ? fileName[0] : null;
|
||||
};
|
||||
|
||||
SDLUtil.showLoadingBar(true);
|
||||
await axios({
|
||||
url: path,
|
||||
responseType: 'arraybuffer',
|
||||
method: 'get',
|
||||
})
|
||||
.then(response => {
|
||||
try {
|
||||
if (response.headers['content-type'] === 'application/json') {
|
||||
response.data = JSON.parse(String.fromCharCode.apply(null, Array.from(new Uint8Array(response.data))));
|
||||
SDLUtil.showLoadingBar(false);
|
||||
SDLUtil.errorAlert(response);
|
||||
return;
|
||||
}
|
||||
const blob = new Blob([response.data], { type: response.headers['content-type'] });
|
||||
|
||||
let fileName = '';
|
||||
if (response.headers['content-disposition']) {
|
||||
fileName = getFileName(response.headers['content-disposition']);
|
||||
} else {
|
||||
// eslint-disable-next-line prefer-destructuring
|
||||
fileName = params.fileName;
|
||||
}
|
||||
|
||||
fileName = decodeURI(fileName);
|
||||
|
||||
if (window.navigator.msSaveOrOpenBlob) {
|
||||
// IE 10+
|
||||
window.navigator.msSaveOrOpenBlob(blob, fileName);
|
||||
} else {
|
||||
// not IE
|
||||
const link = document.createElement('a');
|
||||
link.href = window.URL.createObjectURL(blob);
|
||||
link.target = '_self';
|
||||
if (fileName) link.download = fileName;
|
||||
link.click();
|
||||
}
|
||||
SDLUtil.showLoadingBar(false);
|
||||
// return response.data;
|
||||
} catch (e) {
|
||||
SDLUtil.showLoadingBar(false);
|
||||
console.error(e);
|
||||
}
|
||||
})
|
||||
.catch(response => {
|
||||
SDLUtil.showLoadingBar(false);
|
||||
SDLUtil.alert(response.data);
|
||||
});
|
||||
},
|
||||
/**
|
||||
* param:
|
||||
* return:
|
||||
* exception:
|
||||
* Description : ignore drag event
|
||||
*/
|
||||
ignoreDrag(e) {
|
||||
this.dragging = true;
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
},
|
||||
/**
|
||||
* param:
|
||||
* return:
|
||||
* exception:
|
||||
* Description : if you select a file
|
||||
*/
|
||||
onChange(e) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
// e.dataTransfer.dropEffect = 'copy';
|
||||
this.successMsg = '';
|
||||
this.errorMsg = '';
|
||||
// e.dataTransfer.effectAllowed = 'copy';
|
||||
let files = e.target.files;
|
||||
if (files.length === 0) {
|
||||
files = e.dataTransfer.files || [];
|
||||
}
|
||||
const agent = window.navigator.userAgent.toLowerCase();
|
||||
if ((window.navigator.appName === 'Netscape' && agent.indexOf('trident') !== -1) || agent.indexOf('msie') !== -1) {
|
||||
try {
|
||||
if (files.length === 0) {
|
||||
files = e.dataTransfer.files || [];
|
||||
}
|
||||
} catch (err) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this.dragging = false;
|
||||
|
||||
let tmpTotalFileSize = this.fileSizes;
|
||||
let tmpTotalFileCnt = this.itemsAdded;
|
||||
|
||||
const getFileExt = fileName => fileName.substring(fileName.lastIndexOf('.') + 1, fileName.length).toLowerCase();
|
||||
|
||||
const extList = this.useExtList.split('|');
|
||||
|
||||
let dupChk = false;
|
||||
let extChkTxt = '';
|
||||
Object.keys(files).forEach(x => {
|
||||
tmpTotalFileSize += files[x].size;
|
||||
tmpTotalFileCnt += 1;
|
||||
if (this.itemsNames.filter(item => item.fileName === files[x].name).length > 0) {
|
||||
dupChk = true;
|
||||
return;
|
||||
}
|
||||
if (!extList.includes(getFileExt(files[x].name))) {
|
||||
extChkTxt = getFileExt(files[x].name);
|
||||
}
|
||||
});
|
||||
if (dupChk) {
|
||||
document.querySelector(`#fileAttach${this.downloadType}`).value = '';
|
||||
SDLUtil.alert('sdl.common.message.duplicated');
|
||||
return;
|
||||
}
|
||||
if (extChkTxt) {
|
||||
document.querySelector(`#fileAttach${this.downloadType}`).value = '';
|
||||
SDLUtil.alert('sdl.common.message.extensionCannotBeAttached', [`${extChkTxt}`]);
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.maxTotalSize < tmpTotalFileSize) {
|
||||
document.querySelector(`#fileAttach${this.downloadType}`).value = '';
|
||||
SDLUtil.alert('sdl.common.message.attachedFileExceededMaxCapacity');
|
||||
return;
|
||||
}
|
||||
if (this.maxItems < tmpTotalFileCnt) {
|
||||
document.querySelector(`#fileAttach${this.downloadType}`).value = '';
|
||||
SDLUtil.alert('sdl.common.message.attachedFileExceededMaxNumber');
|
||||
return;
|
||||
}
|
||||
|
||||
Object.keys(files).forEach(x => {
|
||||
this.items = files[x] || files[x];
|
||||
const item = {
|
||||
fileName: files[x].name,
|
||||
fileSize: files[x].size,
|
||||
file: this.items,
|
||||
// selected: false,
|
||||
};
|
||||
this.itemsNames.push(item);
|
||||
});
|
||||
|
||||
document.querySelector(`#fileAttach${this.downloadType}`).value = '';
|
||||
this.calViewVariable();
|
||||
},
|
||||
|
||||
/**
|
||||
* param: docId -> String
|
||||
* return:
|
||||
* exception:
|
||||
* Description : component initialization
|
||||
*/
|
||||
init(docId = '') {
|
||||
this.docId = docId;
|
||||
this.items = '';
|
||||
this.itemsAdded = '';
|
||||
this.itemsNames = [];
|
||||
this.itemsTotalSize = 0;
|
||||
this.dragging = false;
|
||||
if (typeof this.fileList !== 'undefined') {
|
||||
this.fileList.forEach(file => {
|
||||
this.itemsNames.push(file);
|
||||
});
|
||||
this.calViewVariable();
|
||||
}
|
||||
},
|
||||
/**
|
||||
* param:
|
||||
* return:
|
||||
* exception:
|
||||
* Description : capacity calculation
|
||||
*/
|
||||
calViewVariable() {
|
||||
this.fileSizes = 0;
|
||||
for (let i = 0; i < this.itemsNames.length; i += 1) {
|
||||
this.fileSizes += this.itemsNames[i].fileSize;
|
||||
}
|
||||
this.itemsAdded = this.itemsNames.length;
|
||||
this.itemsTotalSize = this.bytesToSize(this.fileSizes);
|
||||
},
|
||||
/**
|
||||
* param:
|
||||
* return:
|
||||
* exception:
|
||||
* Description : delete selected file
|
||||
*/
|
||||
removeItem(item) {
|
||||
this.itemsNames.splice(this.itemsNames.indexOf(item), 1);
|
||||
this.calViewVariable();
|
||||
},
|
||||
/**
|
||||
* param:
|
||||
* return:
|
||||
* exception:
|
||||
* Description : upload selected files
|
||||
*/
|
||||
onUpload(docId) {
|
||||
let { uploadURL } = this;
|
||||
if (docId) {
|
||||
// In case of approval, receive the approval ID and upload it.
|
||||
uploadURL = `${SDLUtil.API_URL}/approval/approval-documents/${docId}/attachments`;
|
||||
}
|
||||
this.isLoaderVisible = true;
|
||||
this.formData = new FormData();
|
||||
|
||||
this.formData.append('downloadType', this.downloadType);
|
||||
let addFileCnt = 0;
|
||||
for (let i = 0; i < this.itemsNames.length; i += 1) {
|
||||
if (typeof this.itemsNames[i].file !== 'undefined') {
|
||||
this.formData.append('files', this.itemsNames[i].file);
|
||||
addFileCnt += 1;
|
||||
// this.itemsNames.splice(i);
|
||||
}
|
||||
}
|
||||
if (addFileCnt > 0) {
|
||||
if (this.method === 'put' || this.method === 'post') {
|
||||
axios({
|
||||
method: this.method,
|
||||
url: uploadURL,
|
||||
data: this.formData,
|
||||
})
|
||||
.then(response => {
|
||||
this.isLoaderVisible = false;
|
||||
// Show success message
|
||||
const fileList = response.data;
|
||||
for (let i = 0; i < fileList.length; i += 1) {
|
||||
const obj = this.itemsNames.filter(item => item.fileName === fileList[i].fileName)[0];
|
||||
this.itemsNames.splice(this.itemsNames.indexOf(obj), 1, Object.assign(obj, fileList[i]));
|
||||
}
|
||||
this.isLoaderVisible = false;
|
||||
this.reIndexOrderIdx();
|
||||
this.$emit('complete', this.downloadType, this.itemsNames);
|
||||
})
|
||||
.catch(error => {
|
||||
this.isLoaderVisible = false;
|
||||
if (error.isAxiosError) SDLUtil.alert('sdl.common.message.attachedFileChanged');
|
||||
else SDLUtil.errorAlert(error);
|
||||
SDLUtil.showLoadingBar(false);
|
||||
});
|
||||
} else {
|
||||
if (this.showHttpMessages) this.errorMsg = this.httpMethodErrorMessage;
|
||||
this.isLoaderVisible = false;
|
||||
SDLUtil.alert({ msg: this.httpMethodErrorMessage });
|
||||
}
|
||||
} else {
|
||||
this.isLoaderVisible = false;
|
||||
this.reIndexOrderIdx();
|
||||
this.$emit('complete', this.downloadType, this.itemsNames);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* param:
|
||||
* return:
|
||||
* exception:
|
||||
* Description : reindex upload file ordering
|
||||
*/
|
||||
reIndexOrderIdx() {
|
||||
for (let i = 0; i < this.itemsNames.length; i += 1) {
|
||||
this.itemsNames[i].orderIdx = i;
|
||||
this.itemsNames.splice(i, 1, this.itemsNames[i]);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* param:
|
||||
* return:
|
||||
* exception:
|
||||
* Description : watch drop event(IE)
|
||||
*/
|
||||
watchDrop() {
|
||||
// console.log('watchDrop');
|
||||
const agent = window.navigator.userAgent.toLowerCase();
|
||||
if ((window.navigator.appName === 'Netscape' && agent.indexOf('trident') !== -1) || agent.indexOf('msie') !== -1) {
|
||||
let el = `#fileAttach${this.downloadType}`;
|
||||
el = document.querySelector(el) || this.$root.$el.querySelector(el);
|
||||
this.dropElement = el;
|
||||
|
||||
if (this.dropElement) {
|
||||
document.addEventListener('dragenter', this.ignoreDrag, false);
|
||||
document.addEventListener('dragover', this.ignoreDrag, false);
|
||||
document.addEventListener('drop', this.ignoreDrag, false);
|
||||
this.dropElement.addEventListener('dragover', this.ignoreDrag, false);
|
||||
this.dropElement.addEventListener('drop', this.onChange, false);
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
dragging: {
|
||||
handler() {
|
||||
this.watchDrop();
|
||||
},
|
||||
deep: true,
|
||||
},
|
||||
fileList: {
|
||||
handler() {
|
||||
this.init(this.docId);
|
||||
},
|
||||
deep: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.addfile {
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
/* Loader */
|
||||
.loaderArea {
|
||||
min-height: 150px;
|
||||
}
|
||||
|
||||
.ui--file-list .loader {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: #fff;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.ui--file-list .loaderImg {
|
||||
border: 9px solid #eee;
|
||||
border-top: 9px solid #00adce;
|
||||
border-radius: 50%;
|
||||
width: 70px;
|
||||
height: 70px;
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
/* End Loader */
|
||||
</style>
|
||||
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* Program: SDL Common Component
|
||||
* Author: SDL
|
||||
* Description: SDL Common Component
|
||||
*/
|
||||
import SdlBreadcrumb from './breadcrumb';
|
||||
import SdlLoadingbar from './loadingbar/LoadingBar.vue';
|
||||
import SdlModal from './modal';
|
||||
import SdlFileuploader from './fileuploader/MultipleFileUploader.vue';
|
||||
import SdlDatePicker from './datepicker';
|
||||
import SdlRegexInput from './input/RegexInput.vue';
|
||||
import sdlPagination from './pagination/Pagination.vue';
|
||||
import { SdlExceldownload, SdlExcelupload } from './excel';
|
||||
import SdlTree from './tree';
|
||||
import SdlApprovalpath from './approvalpath/ApprovalPath.vue';
|
||||
import SdlScrollToTop from './scrollToTop/ScrollToTop.vue';
|
||||
|
||||
const SdlControl = {
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
install(Vue, options) {
|
||||
Vue.use(SdlModal, {
|
||||
dialog: true,
|
||||
dynamic: true,
|
||||
dynamicDefaults: { clickToClose: false },
|
||||
injectModalsContainer: true,
|
||||
});
|
||||
Vue.component('sdl-breadcrumb', SdlBreadcrumb);
|
||||
Vue.component('sdl-loadingbar', SdlLoadingbar);
|
||||
Vue.component('sdl-fileuploader', SdlFileuploader);
|
||||
Vue.component('sdl-datepicker', SdlDatePicker);
|
||||
Vue.component('sdl-pagination', sdlPagination);
|
||||
Vue.component('sdl-exceldownloadbtn', SdlExceldownload);
|
||||
Vue.component('sdl-exceluploadbtn', SdlExcelupload);
|
||||
Vue.component('sdl-tree', SdlTree);
|
||||
Vue.component('sdl-approvalpath', SdlApprovalpath);
|
||||
Vue.component('sdl-regex-input', SdlRegexInput);
|
||||
// Exclude Editor from SDL common component related to license
|
||||
// Vue.component('sdl-editor', SdlEditor);
|
||||
Vue.component('sdl-scroll-to-top', SdlScrollToTop);
|
||||
},
|
||||
};
|
||||
|
||||
export default SdlControl;
|
||||
@@ -0,0 +1,70 @@
|
||||
<!--
|
||||
Program: RegexInput
|
||||
Author: SDL
|
||||
Brief: Input using regex
|
||||
-->
|
||||
<template>
|
||||
<input v-bind="$attrs" @input="updateValue($event.target.value)" @change="emitChange" v-model="val" ref="input" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import _ from 'lodash';
|
||||
|
||||
export default {
|
||||
name: 'sdl-regex-input',
|
||||
props: {
|
||||
value: {
|
||||
required: false,
|
||||
type: [Number, String],
|
||||
},
|
||||
modelValue: {
|
||||
required: false,
|
||||
type: [Number, String],
|
||||
},
|
||||
regExp: {
|
||||
type: RegExp,
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
val: '',
|
||||
oldVal: '',
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
isValid(val) {
|
||||
return _.isEmpty(val) || val.toString().match(this.regExp);
|
||||
},
|
||||
// update the value of input
|
||||
updateValue(val) {
|
||||
const newValue = this.isValid(val) ? val : this.oldVal;
|
||||
this.val = newValue;
|
||||
this.oldVal = newValue;
|
||||
this.emitInput(newValue);
|
||||
},
|
||||
// emit input event
|
||||
emitInput(val) {
|
||||
this.$emit('input', val);
|
||||
this.$emit('update:modelValue', val);
|
||||
},
|
||||
// emit change event
|
||||
emitChange() {
|
||||
this.$emit('change', this.val);
|
||||
this.$emit('update:modelValue', this.val);
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
// watch value prop
|
||||
value(val) {
|
||||
this.updateValue(val);
|
||||
},
|
||||
modelValue(val) {
|
||||
this.updateValue(val);
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.updateValue(this.modelValue);
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -0,0 +1,54 @@
|
||||
<!--
|
||||
Program: LoadingBar
|
||||
Author: SDL
|
||||
Description: Loadingbar Common Component
|
||||
-->
|
||||
<template>
|
||||
<modal name="loadingbar" :scrollable="false" :click-to-close="false" classes="modal-sm" :width="270" :height="400" @before-open="beforeOpened">
|
||||
<div class="flex-loading">
|
||||
<div class="spinner-border spinner-border-lg" role="status">
|
||||
<span class="visually-hidden">Loading...</span>
|
||||
</div>
|
||||
<div v-if="params.msg" class="loadingBarMessage">
|
||||
<p style="text-align: center; font-weight: bold" v-html="$t(params.msg)"></p>
|
||||
</div>
|
||||
</div>
|
||||
</modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'loadingbar',
|
||||
data() {
|
||||
return {
|
||||
params: {},
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
beforeOpened(event) {
|
||||
this.params = event.params || {};
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style scoped>
|
||||
.flex-loading {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.loadingBarMessage {
|
||||
margin-top: 10px;
|
||||
padding-top: 17px;
|
||||
padding-bottom: 1px;
|
||||
background-color: white;
|
||||
border-radius: 20px;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.spinner-border.spinner-border-lg {
|
||||
margin-left: 100px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,210 @@
|
||||
<!--
|
||||
Program: Modal Dialog
|
||||
Author: SDL
|
||||
Description: Dialog Common Component
|
||||
-->
|
||||
<template>
|
||||
<modal
|
||||
name="dialog"
|
||||
height="auto"
|
||||
:classes="['v--modal', 'vue-dialog', this.params.class]"
|
||||
:width="width"
|
||||
:pivot-y="0.3"
|
||||
:adaptive="true"
|
||||
:clickToClose="clickToClose"
|
||||
:transition="transition"
|
||||
@before-open="beforeOpened"
|
||||
@before-close="beforeClosed"
|
||||
@opened="$emit('opened', $event)"
|
||||
@closed="$emit('closed', $event)"
|
||||
>
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title h4" v-if="params.title" v-html="params.title || ''"></h5>
|
||||
<button type="button" class="btn-close" data-dismiss="modal" aria-label="Close" @click="closeClick($event)"> </button>
|
||||
</div>
|
||||
<component v-if="params.component" v-bind="params.props" :is="params.component"> </component>
|
||||
<div class="modal-body" style="word-break: break-all" v-else v-html="params.text || ''"></div>
|
||||
<div class="modal-footer" v-if="buttons">
|
||||
<button v-for="(button, i) in buttons" :class="button.default ? 'btn btn-secondary' : 'btn btn-primary'" type="button" :key="i" v-html="button.title" @click.stop="click(i, $event)"></button>
|
||||
</div>
|
||||
<div v-else class="vue-dialog-buttons-none"></div>
|
||||
</div>
|
||||
</modal>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: 'sdl-dialog',
|
||||
props: {
|
||||
width: {
|
||||
type: [Number, String],
|
||||
default: 400,
|
||||
},
|
||||
clickToClose: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
transition: {
|
||||
type: String,
|
||||
default: 'fade',
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
params: {},
|
||||
defaultButtons: [{ title: 'CLOSE' }],
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
buttons() {
|
||||
return this.params.buttons || this.defaultButtons;
|
||||
},
|
||||
/**
|
||||
* Returns FLEX style with correct width for arbitrary number of
|
||||
* buttons.
|
||||
*/
|
||||
buttonStyle() {
|
||||
return {
|
||||
flex: `1 1 ${100 / this.buttons.length}%`,
|
||||
};
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* param: event
|
||||
* return:
|
||||
* exception:
|
||||
* Description : Run Event before Modal opening
|
||||
*/
|
||||
beforeOpened(event) {
|
||||
window.addEventListener('keyup', this.onKeyUp);
|
||||
|
||||
this.params = event.params || {};
|
||||
this.$emit('before-opened', event);
|
||||
},
|
||||
/**
|
||||
* param: event
|
||||
* return:
|
||||
* exception:
|
||||
* Description : Run Event before Modal closing
|
||||
*/
|
||||
beforeClosed(event) {
|
||||
window.removeEventListener('keyup', this.onKeyUp);
|
||||
|
||||
this.params = {};
|
||||
this.$emit('before-closed', event);
|
||||
},
|
||||
/**
|
||||
* param: i -> button index
|
||||
* param: event
|
||||
* param: source -> String
|
||||
* return:
|
||||
* exception:
|
||||
* Description : Run Event Button Click
|
||||
*/
|
||||
click(i, event, source = 'click') {
|
||||
const button = this.buttons[i];
|
||||
|
||||
if (button && typeof button.handler === 'function') {
|
||||
button.handler(i, event, { source });
|
||||
} else {
|
||||
this.$modal.hide('dialog');
|
||||
}
|
||||
},
|
||||
/**
|
||||
* param: event
|
||||
* param: source -> String
|
||||
* return:
|
||||
* exception:
|
||||
* Description : Run Event Close Button Click
|
||||
*/
|
||||
closeClick(event, source = 'click') {
|
||||
const button = this.buttons[0];
|
||||
|
||||
if (button && typeof button.handler === 'function') {
|
||||
button.handler(0, event, { source });
|
||||
}
|
||||
this.$modal.hide('dialog');
|
||||
},
|
||||
/**
|
||||
* param: event
|
||||
* return:
|
||||
* exception:
|
||||
* Description : Run Event Key Up
|
||||
*/
|
||||
onKeyUp(event) {
|
||||
if (event.which === 13 && this.buttons.length > 0) {
|
||||
const buttonIndex = this.buttons.length === 1 ? 0 : this.buttons.findIndex(button => button.default);
|
||||
|
||||
if (buttonIndex !== -1) {
|
||||
this.click(buttonIndex, event, 'keypress');
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style>
|
||||
button span {
|
||||
pointer-events: none;
|
||||
}
|
||||
.vue-dialog div {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.vue-dialog .dialog-flex {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.vue-dialog .dialog-content {
|
||||
flex: 1 0 auto;
|
||||
width: 100%;
|
||||
padding: 15px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.vue-dialog .dialog-c-title {
|
||||
font-weight: 600;
|
||||
padding-bottom: 15px;
|
||||
}
|
||||
|
||||
.vue-dialog .vue-dialog-buttons {
|
||||
display: flex;
|
||||
flex: 0 1 auto;
|
||||
width: 100%;
|
||||
border-top: 1px solid #eee;
|
||||
}
|
||||
|
||||
.vue-dialog .vue-dialog-buttons-none {
|
||||
width: 100%;
|
||||
padding-bottom: 15px;
|
||||
}
|
||||
|
||||
.vue-dialog-button {
|
||||
font-size: 12px !important;
|
||||
background: transparent;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
border: 0;
|
||||
cursor: pointer;
|
||||
box-sizing: border-box;
|
||||
line-height: 40px;
|
||||
height: 40px;
|
||||
color: inherit;
|
||||
font: inherit;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.vue-dialog-button:hover {
|
||||
background: rgba(0, 0, 0, 0.01);
|
||||
}
|
||||
|
||||
.vue-dialog-button:active {
|
||||
background: rgba(0, 0, 0, 0.025);
|
||||
}
|
||||
|
||||
.vue-dialog-button:not(:first-of-type) {
|
||||
border-left: 1px solid #eee;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,734 @@
|
||||
<!--
|
||||
Program: Modal
|
||||
Author: SDL
|
||||
Description: Modal Common Component
|
||||
-->
|
||||
<template>
|
||||
<transition :name="overlayTransition">
|
||||
<div v-if="visibility.overlay" ref="overlay" :class="overlayClass" :aria-expanded="visibility.overlay.toString()" :data-modal="name">
|
||||
<div class="v--modal-background-click" :style="scrollable ? 'overflow: hidden;' : ''" @mousedown.self="handleBackgroundClick" @touchstart.self.passive="handleBackgroundClick">
|
||||
<div class="v--modal-top-right">
|
||||
<slot name="top-right" />
|
||||
</div>
|
||||
<transition :name="transition" @before-enter="beforeTransitionEnter" @after-enter="afterTransitionEnter" @after-leave="afterTransitionLeave">
|
||||
<div v-if="visibility.modal" ref="modal" :class="modalClass" :style="modalStyle" style="display: block;">
|
||||
<slot />
|
||||
<resizer v-if="resizable && !isAutoHeight" :min-width="minWidth" :min-height="minHeight" @resize="handleModalResize" />
|
||||
</div>
|
||||
</transition>
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
</template>
|
||||
<script>
|
||||
/* eslint-disable no-underscore-dangle */
|
||||
// eslint-disable-next-line indent
|
||||
// eslint-disable-next-line comma-dangle
|
||||
import Modal from './index';
|
||||
import Resizer from './Resizer.vue';
|
||||
import { inRange, createModalEvent, getMutationObserver, blurActiveElement } from './utils';
|
||||
import { parseNumber, validateNumber } from './parser';
|
||||
|
||||
export default {
|
||||
name: 'Modal',
|
||||
props: {
|
||||
name: {
|
||||
required: true,
|
||||
type: String,
|
||||
},
|
||||
delay: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
resizable: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
adaptive: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
draggable: {
|
||||
type: [Boolean, String],
|
||||
default: false,
|
||||
},
|
||||
scrollable: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
reset: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
overlayTransition: {
|
||||
type: String,
|
||||
default: 'overlay-fade',
|
||||
},
|
||||
transition: {
|
||||
type: String,
|
||||
},
|
||||
clickToClose: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
classes: {
|
||||
type: [String, Array],
|
||||
default: 'v--modal',
|
||||
},
|
||||
minWidth: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
validator(value) {
|
||||
return value >= 0;
|
||||
},
|
||||
},
|
||||
minHeight: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
validator(value) {
|
||||
return value >= 0;
|
||||
},
|
||||
},
|
||||
maxWidth: {
|
||||
type: Number,
|
||||
default: Infinity,
|
||||
},
|
||||
maxHeight: {
|
||||
type: Number,
|
||||
default: Infinity,
|
||||
},
|
||||
width: {
|
||||
type: [Number, String],
|
||||
default: 600,
|
||||
validator: validateNumber,
|
||||
},
|
||||
height: {
|
||||
type: [Number, String],
|
||||
default: 300,
|
||||
validator(value) {
|
||||
return value === 'auto' || validateNumber(value);
|
||||
},
|
||||
},
|
||||
pivotX: {
|
||||
type: Number,
|
||||
default: 0.5,
|
||||
validator(value) {
|
||||
return value >= 0 && value <= 1;
|
||||
},
|
||||
},
|
||||
pivotY: {
|
||||
type: Number,
|
||||
default: 0.5,
|
||||
validator(value) {
|
||||
return value >= 0 && value <= 1;
|
||||
},
|
||||
},
|
||||
},
|
||||
components: {
|
||||
Resizer,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
visible: false,
|
||||
|
||||
visibility: {
|
||||
modal: false,
|
||||
overlay: false,
|
||||
},
|
||||
|
||||
shift: {
|
||||
left: 0,
|
||||
top: 0,
|
||||
},
|
||||
|
||||
modal: {
|
||||
width: 0,
|
||||
widthType: 'px',
|
||||
height: 0,
|
||||
heightType: 'px',
|
||||
renderedHeight: 0,
|
||||
},
|
||||
|
||||
window: {
|
||||
width: 0,
|
||||
height: 0,
|
||||
},
|
||||
|
||||
mutationObserver: null,
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.setInitialSize();
|
||||
},
|
||||
/**
|
||||
* Sets global listeners
|
||||
*/
|
||||
beforeMount() {
|
||||
Modal.event.$on('toggle', this.handleToggleEvent);
|
||||
|
||||
window.addEventListener('resize', this.handleWindowResize);
|
||||
this.handleWindowResize();
|
||||
/**
|
||||
* Making sure that autoHeight is enabled when using "scrollable"
|
||||
*/
|
||||
if (this.scrollable && !this.isAutoHeight) {
|
||||
console.warn(`Modal "${this.name}" has scrollable flag set to true but height is not "auto" (${this.height})`);
|
||||
}
|
||||
/**
|
||||
* Only observe when using height: 'auto'
|
||||
* The callback will be called when modal DOM changes,
|
||||
* this is for updating the `top` attribute for height 'auto' modals.
|
||||
*/
|
||||
if (this.isAutoHeight) {
|
||||
/**
|
||||
* MutationObserver feature detection:
|
||||
*
|
||||
* Detects if MutationObserver is available, return false if not.
|
||||
* No polyfill is provided here, so height 'auto' recalculation will
|
||||
* simply stay at its initial height (won't crash).
|
||||
* (Provide polyfill to support IE < 11)
|
||||
*
|
||||
* https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver
|
||||
*
|
||||
* For the sake of SSR, MutationObserver cannot be initialized
|
||||
* before component creation >_<
|
||||
*/
|
||||
const MutationObserver = getMutationObserver();
|
||||
|
||||
if (MutationObserver) {
|
||||
this.mutationObserver = new MutationObserver(() => {
|
||||
this.updateRenderedHeight();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (this.clickToClose) {
|
||||
window.addEventListener('keyup', this.handleEscapeKeyUp);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Removes global listeners
|
||||
*/
|
||||
beforeUnmount() {
|
||||
Modal.event.$off('toggle', this.handleToggleEvent);
|
||||
window.removeEventListener('resize', this.handleWindowResize);
|
||||
|
||||
if (this.clickToClose) {
|
||||
window.removeEventListener('keyup', this.handleEscapeKeyUp);
|
||||
}
|
||||
/**
|
||||
* Removes blocked scroll
|
||||
*/
|
||||
if (this.scrollable) {
|
||||
if (this.$root._dynamicContainer.modals.length === 0 || typeof this.$root._dynamicContainer.modals === 'undefined') {
|
||||
document.body.classList.remove('v--modal-block-scroll');
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
/**
|
||||
* Returns true if height is set to "auto"
|
||||
*/
|
||||
isAutoHeight() {
|
||||
return this.modal.heightType === 'auto';
|
||||
},
|
||||
/**
|
||||
* Calculates and returns modal position based on the
|
||||
* pivots, window size and modal size
|
||||
*/
|
||||
position() {
|
||||
// eslint-disable-next-line indent
|
||||
// eslint-disable-next-line object-curly-newline
|
||||
const { window, shift, pivotX, pivotY, trueModalWidth, trueModalHeight } = this;
|
||||
|
||||
const maxLeft = window.width - trueModalWidth;
|
||||
const maxTop = window.height - trueModalHeight;
|
||||
|
||||
const left = shift.left + pivotX * maxLeft;
|
||||
const top = shift.top + pivotY * maxTop;
|
||||
|
||||
return {
|
||||
left: parseInt(inRange(0, maxLeft, left), 10),
|
||||
top: parseInt(inRange(0, maxTop, top), 10),
|
||||
};
|
||||
},
|
||||
/**
|
||||
* Returns pixel width (if set with %) and makes sure that modal size
|
||||
* fits the window
|
||||
*/
|
||||
trueModalWidth() {
|
||||
// eslint-disable-next-line indent
|
||||
// eslint-disable-next-line object-curly-newline
|
||||
const { window, modal, adaptive, minWidth, maxWidth } = this;
|
||||
|
||||
const value = modal.widthType === '%' ? (window.width / 100) * modal.width : modal.width;
|
||||
|
||||
const max = Math.min(window.width, maxWidth);
|
||||
|
||||
return adaptive ? inRange(minWidth, max, value) : value;
|
||||
},
|
||||
/**
|
||||
* Returns pixel height (if set with %) and makes sure that modal size
|
||||
* fits the window.
|
||||
*
|
||||
* Returns modal.renderedHeight if height set as "auto"
|
||||
*/
|
||||
trueModalHeight() {
|
||||
// eslint-disable-next-line indent
|
||||
// eslint-disable-next-line object-curly-newline
|
||||
const { window, modal, isAutoHeight, adaptive, maxHeight } = this;
|
||||
|
||||
const value = modal.heightType === '%' ? (window.height / 100) * modal.height : modal.height;
|
||||
|
||||
if (isAutoHeight) {
|
||||
// use renderedHeight when height 'auto'
|
||||
return this.modal.renderedHeight;
|
||||
}
|
||||
|
||||
const max = Math.min(window.height, maxHeight);
|
||||
|
||||
return adaptive ? inRange(this.minHeight, max, value) : value;
|
||||
},
|
||||
/**
|
||||
* Returns class list for screen overlay (modal background)
|
||||
*/
|
||||
overlayClass() {
|
||||
return {
|
||||
'v--modal-overlay': true,
|
||||
scrollable: this.scrollable && this.isAutoHeight,
|
||||
};
|
||||
},
|
||||
/**
|
||||
* Returns class list for modal itself
|
||||
*/
|
||||
modalClass() {
|
||||
return ['v--modal-box', 'modal', this.classes];
|
||||
},
|
||||
/**
|
||||
* CSS styles for position and size of the modal
|
||||
*/
|
||||
modalStyle() {
|
||||
return {
|
||||
top: `${this.position.top}px`,
|
||||
left: `${this.position.left}px`,
|
||||
width: `${this.trueModalWidth}px`,
|
||||
height: this.isAutoHeight ? 'auto' : `${this.trueModalHeight}px`,
|
||||
};
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
/**
|
||||
* Sets the visibility of overlay and modal.
|
||||
* Events 'opened' and 'closed' is called here
|
||||
* inside `setTimeout` and `$nextTick`, after the DOM changes.
|
||||
* This fixes `$refs.modal` `undefined` bug (fixes #15)
|
||||
*/
|
||||
visible(value) {
|
||||
if (value) {
|
||||
this.visibility.overlay = true;
|
||||
setTimeout(() => {
|
||||
this.visibility.modal = true;
|
||||
this.$nextTick(() => {
|
||||
this.addDraggableListeners();
|
||||
this.callAfterEvent(true);
|
||||
});
|
||||
}, this.delay);
|
||||
} else {
|
||||
this.visibility.modal = false;
|
||||
setTimeout(() => {
|
||||
this.visibility.overlay = false;
|
||||
this.$nextTick(() => {
|
||||
this.removeDraggableListeners();
|
||||
this.callAfterEvent(false);
|
||||
});
|
||||
}, this.delay);
|
||||
}
|
||||
},
|
||||
// 페이지 변경시 자동 close처리
|
||||
$route() {
|
||||
this.$modal.hide(this.name);
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
handleToggleEvent(name, state, params) {
|
||||
if (this.name === name) {
|
||||
const nextState = typeof state === 'undefined' ? !this.visible : state;
|
||||
|
||||
this.toggle(nextState, params);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Initializes modal's size & position,
|
||||
* if "reset" flag is set to true - this function will be called
|
||||
* every time "beforeOpen" is triggered
|
||||
*/
|
||||
setInitialSize() {
|
||||
const { modal } = this;
|
||||
const width = parseNumber(this.width);
|
||||
const height = parseNumber(this.height);
|
||||
|
||||
modal.width = width.value;
|
||||
modal.widthType = width.type;
|
||||
modal.height = height.value;
|
||||
modal.heightType = height.type;
|
||||
},
|
||||
|
||||
handleEscapeKeyUp(event) {
|
||||
if (event.which === 27 && this.visible) {
|
||||
this.$modal.hide(this.name);
|
||||
}
|
||||
},
|
||||
|
||||
handleWindowResize() {
|
||||
this.window.width = window.innerWidth;
|
||||
this.window.height = window.innerHeight;
|
||||
|
||||
this.ensureShiftInWindowBounds();
|
||||
},
|
||||
/**
|
||||
* Generates event object
|
||||
*/
|
||||
createModalEvent(args = {}) {
|
||||
return createModalEvent({
|
||||
name: this.name,
|
||||
ref: this.$refs.modal,
|
||||
...args,
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Event handler which is triggered on modal resize
|
||||
*/
|
||||
handleModalResize(event) {
|
||||
this.modal.widthType = 'px';
|
||||
this.modal.width = event.size.width;
|
||||
|
||||
this.modal.heightType = 'px';
|
||||
this.modal.height = event.size.height;
|
||||
|
||||
const { size } = this.modal;
|
||||
|
||||
this.$emit('resize', this.createModalEvent({ size }));
|
||||
},
|
||||
/**
|
||||
* Event handler which is triggered on $modal.show and $modal.hide
|
||||
* BeforeEvents: ('before-close' and 'before-open') are `$emit`ed here,
|
||||
* but AfterEvents ('opened' and 'closed') are moved to `watch.visible`.
|
||||
*/
|
||||
toggle(nextState, params) {
|
||||
const { reset, scrollable, visible } = this;
|
||||
|
||||
if (visible === nextState) {
|
||||
return;
|
||||
}
|
||||
|
||||
const beforeEventName = visible ? 'before-close' : 'before-open';
|
||||
|
||||
if (beforeEventName === 'before-open') {
|
||||
/**
|
||||
* Need to unfocus previously focused element, otherwise
|
||||
* all keypress events (ESC press, for example) will trigger on that element.
|
||||
*/
|
||||
blurActiveElement();
|
||||
|
||||
if (reset) {
|
||||
this.setInitialSize();
|
||||
|
||||
this.shift.left = 0;
|
||||
this.shift.top = 0;
|
||||
}
|
||||
|
||||
if (scrollable) {
|
||||
document.body.classList.add('v--modal-block-scroll');
|
||||
}
|
||||
} else if (scrollable) {
|
||||
if (this.$root._dynamicContainer.modals.length === 0 || typeof this.$root._dynamicContainer.modals === 'undefined') {
|
||||
document.body.classList.remove('v--modal-block-scroll');
|
||||
}
|
||||
}
|
||||
|
||||
let stopEventExecution = false;
|
||||
|
||||
const stop = () => {
|
||||
stopEventExecution = true;
|
||||
};
|
||||
|
||||
const beforeEvent = this.createModalEvent({
|
||||
stop,
|
||||
state: nextState,
|
||||
params,
|
||||
});
|
||||
|
||||
this.$emit(beforeEventName, beforeEvent);
|
||||
|
||||
if (!stopEventExecution) {
|
||||
this.visible = nextState;
|
||||
}
|
||||
},
|
||||
|
||||
getDraggableElement() {
|
||||
const selector = typeof this.draggable !== 'string' ? '.v--modal-box' : this.draggable;
|
||||
|
||||
return selector ? this.$refs.overlay.querySelector(selector) : null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Event handler that is triggered when background overlay is clicked
|
||||
*/
|
||||
handleBackgroundClick() {
|
||||
if (this.clickToClose) {
|
||||
this.toggle(false);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
*'opened' and 'closed' events are `$emit`ed here.
|
||||
* This is called in watch.visible.
|
||||
* Because modal DOM updates are async,
|
||||
* wrapping afterEvents in `$nextTick` fixes `$refs.modal` undefined bug.
|
||||
* (fixes #15)
|
||||
*/
|
||||
callAfterEvent(state) {
|
||||
if (state) {
|
||||
this.connectObserver();
|
||||
} else {
|
||||
this.disconnectObserver();
|
||||
}
|
||||
const eventName = state ? 'opened' : 'closed';
|
||||
const event = this.createModalEvent({ state });
|
||||
this.$emit(eventName, event);
|
||||
},
|
||||
|
||||
addDraggableListeners() {
|
||||
if (!this.draggable) {
|
||||
return;
|
||||
}
|
||||
|
||||
const dragger = this.getDraggableElement();
|
||||
|
||||
if (dragger) {
|
||||
let startX = 0;
|
||||
let startY = 0;
|
||||
let cachedShiftX = 0;
|
||||
let cachedShiftY = 0;
|
||||
|
||||
const getPosition = event => (event.touches && event.touches.length > 0 ? event.touches[0] : event);
|
||||
|
||||
const handleDraggableMousemove = event => {
|
||||
const { clientX, clientY } = getPosition(event);
|
||||
|
||||
this.shift.left = cachedShiftX + clientX - startX;
|
||||
this.shift.top = cachedShiftY + clientY - startY;
|
||||
|
||||
event.preventDefault();
|
||||
};
|
||||
|
||||
const handleDraggableMouseup = event => {
|
||||
this.ensureShiftInWindowBounds();
|
||||
|
||||
document.removeEventListener('mousemove', handleDraggableMousemove);
|
||||
document.removeEventListener('touchmove', handleDraggableMousemove);
|
||||
|
||||
document.removeEventListener('mouseup', handleDraggableMouseup);
|
||||
document.removeEventListener('touchend', handleDraggableMouseup);
|
||||
|
||||
event.preventDefault();
|
||||
};
|
||||
|
||||
const handleDraggableMousedown = event => {
|
||||
const { target } = event;
|
||||
|
||||
if (target && target.nodeName === 'INPUT') {
|
||||
return;
|
||||
}
|
||||
|
||||
const { clientX, clientY } = getPosition(event);
|
||||
|
||||
document.addEventListener('mousemove', handleDraggableMousemove);
|
||||
document.addEventListener('touchmove', handleDraggableMousemove);
|
||||
|
||||
document.addEventListener('mouseup', handleDraggableMouseup);
|
||||
document.addEventListener('touchend', handleDraggableMouseup);
|
||||
|
||||
startX = clientX;
|
||||
startY = clientY;
|
||||
|
||||
cachedShiftX = this.shift.left;
|
||||
cachedShiftY = this.shift.top;
|
||||
};
|
||||
|
||||
dragger.addEventListener('mousedown', handleDraggableMousedown);
|
||||
dragger.addEventListener('touchstart', handleDraggableMousedown, { passive: true });
|
||||
}
|
||||
},
|
||||
|
||||
removeDraggableListeners() {
|
||||
/**
|
||||
* Ideally this is not needed because "dragger" will be unmounted anyway.
|
||||
*/
|
||||
},
|
||||
/**
|
||||
* Update $data.modal.renderedHeight using getBoundingClientRect.
|
||||
* This method is called when:
|
||||
* 1. modal opened
|
||||
* 2. MutationObserver's observe callback
|
||||
*/
|
||||
updateRenderedHeight() {
|
||||
if (this.$refs.modal) {
|
||||
this.modal.renderedHeight = this.$refs.modal.getBoundingClientRect().height;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Start observing modal's DOM, if childList or subtree changes,
|
||||
* the callback (registered in beforeMount) will be called.
|
||||
*/
|
||||
connectObserver() {
|
||||
if (this.mutationObserver) {
|
||||
this.mutationObserver.observe(this.$refs.overlay, {
|
||||
childList: true,
|
||||
attributes: true,
|
||||
subtree: true,
|
||||
});
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Disconnects MutationObserver
|
||||
*/
|
||||
disconnectObserver() {
|
||||
if (this.mutationObserver) {
|
||||
this.mutationObserver.disconnect();
|
||||
}
|
||||
},
|
||||
|
||||
beforeTransitionEnter() {
|
||||
this.connectObserver();
|
||||
},
|
||||
|
||||
afterTransitionEnter() {
|
||||
// console.log('after transition enter')
|
||||
},
|
||||
|
||||
afterTransitionLeave() {
|
||||
// console.log('after transition leave')
|
||||
},
|
||||
|
||||
ensureShiftInWindowBounds() {
|
||||
// eslint-disable-next-line indent
|
||||
// eslint-disable-next-line object-curly-newline
|
||||
const { window, shift, pivotX, pivotY, trueModalWidth, trueModalHeight } = this;
|
||||
|
||||
const maxLeft = window.width - trueModalWidth;
|
||||
const maxTop = window.height - trueModalHeight;
|
||||
|
||||
const left = shift.left + pivotX * maxLeft;
|
||||
const top = shift.top + pivotY * maxTop;
|
||||
|
||||
this.shift.left -= left - inRange(0, maxLeft, left);
|
||||
this.shift.top -= top - inRange(0, maxTop, top);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.v--modal-block-scroll {
|
||||
overflow: hidden;
|
||||
width: 100vw;
|
||||
}
|
||||
.modal-dialog {
|
||||
margin: 0 !important;
|
||||
--bs-modal-margin: 0.5rem !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style scoped>
|
||||
.v--modal-block-scroll {
|
||||
overflow: hidden;
|
||||
width: 100vw;
|
||||
}
|
||||
|
||||
.v--modal-overlay {
|
||||
position: fixed;
|
||||
box-sizing: border-box;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
background: rgba(0, 0, 0, 0.2);
|
||||
z-index: 2000;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.v--modal-overlay.scrollable {
|
||||
height: 100%;
|
||||
min-height: 100vh;
|
||||
overflow-y: auto;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
|
||||
.v--modal-overlay .v--modal-background-click {
|
||||
width: 100%;
|
||||
min-height: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.v--modal-overlay .v--modal-box {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.v--modal-overlay.scrollable .v--modal-box {
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
.v--modal {
|
||||
background-color: white;
|
||||
text-align: left;
|
||||
border-radius: 3px;
|
||||
box-shadow: 0 20px 60px -2px rgba(27, 33, 58, 0.4);
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.v--modal.v--modal-fullscreen {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
margin: 0;
|
||||
left: 0;
|
||||
top: 0;
|
||||
}
|
||||
.modal-dialog {
|
||||
margin: 0 !important;
|
||||
}
|
||||
.modal-lg {
|
||||
max-width: 1200px;
|
||||
}
|
||||
|
||||
.v--modal-top-right {
|
||||
display: block;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.overlay-fade-enter-active,
|
||||
.overlay-fade-leave-active {
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.overlay-fade-enter,
|
||||
.overlay-fade-leave-active {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.nice-modal-fade-enter-active,
|
||||
.nice-modal-fade-leave-active {
|
||||
transition: all 0.4s;
|
||||
}
|
||||
|
||||
.nice-modal-fade-enter,
|
||||
.nice-modal-fade-leave-active {
|
||||
opacity: 0;
|
||||
transform: translateY(-20px);
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,66 @@
|
||||
<!--
|
||||
Program: Modal Container
|
||||
Author: SDL
|
||||
Description: Modal Container Common Component
|
||||
-->
|
||||
<template>
|
||||
<div id="modals-container">
|
||||
<modal
|
||||
v-bind="modal.modalAttrs"
|
||||
v-for="modal in modals"
|
||||
:key="modal.id"
|
||||
v-on="modal.modalListeners"
|
||||
@closed="remove(modal.id)"
|
||||
>
|
||||
<component
|
||||
v-bind="modal.componentAttrs"
|
||||
:is="modal.component"
|
||||
@close="$modal.hide(modal.modalAttrs.name)"
|
||||
/>
|
||||
</modal>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { generateId } from './utils';
|
||||
import { shallowRef } from 'vue';
|
||||
|
||||
const PREFIX = '_dynamic_modal_';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
modals: [],
|
||||
};
|
||||
},
|
||||
created() {
|
||||
// eslint-disable-next-line no-underscore-dangle
|
||||
this.$root._dynamicContainer = this;
|
||||
},
|
||||
methods: {
|
||||
add(component, componentAttrs = {}, modalAttrs = {}, modalListeners = {}) {
|
||||
const id = generateId();
|
||||
const name = modalAttrs.name || PREFIX + id;
|
||||
const shallowComponent = shallowRef(component);
|
||||
|
||||
this.modals.push({
|
||||
id,
|
||||
modalAttrs: { ...modalAttrs, name },
|
||||
modalListeners,
|
||||
component: shallowComponent,
|
||||
componentAttrs,
|
||||
});
|
||||
|
||||
this.$nextTick(() => {
|
||||
this.$modal.show(name);
|
||||
});
|
||||
},
|
||||
remove(id) {
|
||||
const index = this.modals.findIndex(v => v.id === id);
|
||||
|
||||
if (index !== -1) {
|
||||
this.modals.splice(index, 1);
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -0,0 +1,115 @@
|
||||
<!--
|
||||
Program: Modal Resizer
|
||||
Author: SDL
|
||||
Description: Modal Resizer Common Component
|
||||
-->
|
||||
<template>
|
||||
<div :class="className"></div>
|
||||
</template>
|
||||
<script>
|
||||
import { inRange } from './utils';
|
||||
|
||||
export default {
|
||||
name: 'VueJsModalResizer',
|
||||
props: {
|
||||
minHeight: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
minWidth: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
clicked: false,
|
||||
size: {},
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.$el.addEventListener('mousedown', this.start, false);
|
||||
},
|
||||
computed: {
|
||||
className() {
|
||||
return { 'vue-modal-resizer': true, clicked: this.clicked };
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
start(event) {
|
||||
this.clicked = true;
|
||||
|
||||
window.addEventListener('mousemove', this.mousemove, false);
|
||||
window.addEventListener('mouseup', this.stop, false);
|
||||
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
},
|
||||
stop() {
|
||||
this.clicked = false;
|
||||
|
||||
window.removeEventListener('mousemove', this.mousemove, false);
|
||||
window.removeEventListener('mouseup', this.stop, false);
|
||||
|
||||
this.$emit('resize-stop', {
|
||||
element: this.$el.parentElement,
|
||||
size: this.size,
|
||||
});
|
||||
},
|
||||
mousemove(event) {
|
||||
this.resize(event);
|
||||
},
|
||||
resize(event) {
|
||||
const el = this.$el.parentElement;
|
||||
|
||||
if (el) {
|
||||
let width = event.clientX - el.offsetLeft;
|
||||
let height = event.clientY - el.offsetTop;
|
||||
|
||||
width = inRange(this.minWidth, window.innerWidth, width);
|
||||
height = inRange(this.minHeight, window.innerHeight, height);
|
||||
|
||||
this.size = { width, height };
|
||||
el.style.width = `${width}px`;
|
||||
el.style.height = `${height}px`;
|
||||
|
||||
this.$emit('resize', {
|
||||
element: el,
|
||||
size: this.size,
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style>
|
||||
.vue-modal-resizer {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 9999999;
|
||||
background: transparent;
|
||||
cursor: se-resize;
|
||||
}
|
||||
|
||||
.vue-modal-resizer::after {
|
||||
display: block;
|
||||
position: absolute;
|
||||
content: '';
|
||||
background: transparent;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-bottom: 10px solid #ddd;
|
||||
border-left: 10px solid transparent;
|
||||
}
|
||||
|
||||
.vue-modal-resizer.clicked::after {
|
||||
border-bottom: 10px solid #369be9;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,121 @@
|
||||
/**
|
||||
* Program: Modal Plugin
|
||||
* Author: SDL
|
||||
* Description: Modal Plugin Common Component
|
||||
*/
|
||||
import emitter from '@/event/event';
|
||||
import Modal from './Modal.vue';
|
||||
import Dialog from './Dialog.vue';
|
||||
import ModalsContainer from './ModalsContainer.vue';
|
||||
import { createDivInBody } from './utils';
|
||||
|
||||
const DEFAULT_COMPONENT_NAME = 'Modal';
|
||||
const UNMOUNTED_ROOT_ERROR_MESSAGE = '[vue-js-modal] ' + 'In order to render dynamic modals, a <modals-container> ' + 'component must be present on the page.';
|
||||
const DYNAMIC_MODAL_DISABLED_ERROR = '[vue-js-modal] ' + '$modal() received object as a first argument, but dynamic modals are ' + 'switched off. https://github.com/euvl/vue-js-modal/#dynamic-modals';
|
||||
const UNSUPPORTED_ARGUMENT_ERROR = '[vue-js-modal] $modal() received an unsupported argument as a first argument.';
|
||||
|
||||
export const getModalsContainer = (Vue, options, root) => {
|
||||
// eslint-disable-next-line no-underscore-dangle
|
||||
if (!root._dynamicContainer && options.injectModalsContainer) {
|
||||
// const container = createDivInBody();
|
||||
// createApp({
|
||||
// parent: root,
|
||||
// render: h => h(ModalsContainer),
|
||||
// }).mount(container);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-underscore-dangle
|
||||
return root._dynamicContainer;
|
||||
};
|
||||
|
||||
const Plugin = {
|
||||
install(Vue, options = {}) {
|
||||
/**
|
||||
* Makes sure that plugin can be installed only once
|
||||
*/
|
||||
if (this.installed) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.installed = true;
|
||||
this.app = Vue;
|
||||
this.event = emitter;
|
||||
this.rootInstance = null;
|
||||
|
||||
const componentName = options.componentName || DEFAULT_COMPONENT_NAME;
|
||||
const dynamicDefaults = options.dynamicDefaults || {};
|
||||
/**
|
||||
* Plugin API
|
||||
*/
|
||||
const showStaticModal = (modal, params) => {
|
||||
emitter.$emit('toggle', modal, true, params);
|
||||
};
|
||||
|
||||
const showDynamicModal = (modal, props, params, events) => {
|
||||
/**
|
||||
* Get root for dynamic modal
|
||||
*/
|
||||
const root = params && params.root ? params.root : Plugin.rootInstance;
|
||||
const container = getModalsContainer(Vue, options, root);
|
||||
/**
|
||||
* Show dynamic modal
|
||||
*/
|
||||
if (container) {
|
||||
container.add(modal, props, { ...dynamicDefaults, ...params }, events);
|
||||
return;
|
||||
}
|
||||
|
||||
console.warn(UNMOUNTED_ROOT_ERROR_MESSAGE);
|
||||
};
|
||||
|
||||
Vue.config.globalProperties.$modal = {
|
||||
// eslint-disable-next-line consistent-return
|
||||
show(modal, ...args) {
|
||||
switch (typeof modal) {
|
||||
case 'string': {
|
||||
return showStaticModal(modal, ...args);
|
||||
}
|
||||
case 'object':
|
||||
case 'function': {
|
||||
return options.dynamic ? showDynamicModal(modal, ...args) : console.warn(DYNAMIC_MODAL_DISABLED_ERROR);
|
||||
}
|
||||
default: {
|
||||
console.warn(UNSUPPORTED_ARGUMENT_ERROR, modal);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
},
|
||||
hide(name, params) {
|
||||
emitter.$emit('toggle', name, false, params);
|
||||
},
|
||||
toggle(name, params) {
|
||||
emitter.$emit('toggle', name, undefined, params);
|
||||
},
|
||||
};
|
||||
/**
|
||||
* Sets custom component name (if provided)
|
||||
*/
|
||||
Vue.component(componentName, Modal);
|
||||
/**
|
||||
* Registration of <Dialog/> component
|
||||
*/
|
||||
if (options.dialog) {
|
||||
Vue.component('sdl-dialog', Dialog);
|
||||
}
|
||||
/**
|
||||
* Registration of <ModalsContainer/> component
|
||||
*/
|
||||
if (options.dynamic) {
|
||||
Vue.component('sdl-modal-container', ModalsContainer);
|
||||
Vue.mixin({
|
||||
beforeMount() {
|
||||
if (Plugin.rootInstance === null) {
|
||||
Plugin.rootInstance = this.$root;
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
export default Plugin;
|
||||
@@ -0,0 +1,70 @@
|
||||
/**
|
||||
* Program: Modal parser
|
||||
* Author: SDL
|
||||
* Description: Modal parser util
|
||||
*/
|
||||
const floatRegexp = '[-+]?[0-9]*.?[0-9]+';
|
||||
|
||||
const types = [
|
||||
{
|
||||
name: 'px',
|
||||
regexp: new RegExp(`^${floatRegexp}px$`),
|
||||
},
|
||||
{
|
||||
name: '%',
|
||||
regexp: new RegExp(`^${floatRegexp}%$`),
|
||||
},
|
||||
/**
|
||||
* Fallback optopn
|
||||
* If no suffix specified, assigning "px"
|
||||
*/
|
||||
{
|
||||
name: 'px',
|
||||
regexp: new RegExp(`^${floatRegexp}$`),
|
||||
},
|
||||
];
|
||||
|
||||
const getType = value => {
|
||||
if (value === 'auto') {
|
||||
return {
|
||||
type: value,
|
||||
value: 0,
|
||||
};
|
||||
}
|
||||
|
||||
for (let i = 0; i < types.length; i += 1) {
|
||||
const type = types[i];
|
||||
|
||||
if (type.regexp.test(value)) {
|
||||
return {
|
||||
type: type.name,
|
||||
value: parseFloat(value),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
type: '',
|
||||
value,
|
||||
};
|
||||
};
|
||||
|
||||
export const parseNumber = value => {
|
||||
switch (typeof value) {
|
||||
case 'number':
|
||||
return { type: 'px', value };
|
||||
case 'string':
|
||||
return getType(value);
|
||||
default:
|
||||
return { type: '', value };
|
||||
}
|
||||
};
|
||||
|
||||
export const validateNumber = value => {
|
||||
if (typeof value === 'string') {
|
||||
const value2 = parseNumber(value);
|
||||
return (value2.type === '%' || value2.type === 'px') && value2.value > 0;
|
||||
}
|
||||
|
||||
return value >= 0;
|
||||
};
|
||||
@@ -0,0 +1,60 @@
|
||||
export const generateId = ((index = 0) => () => {
|
||||
index += 1;
|
||||
return index.toString();
|
||||
})();
|
||||
/**
|
||||
*
|
||||
* @param {Number} from Lower limit
|
||||
* @param {Number} to Upper limit
|
||||
* @param {Number} value Checked number value
|
||||
*
|
||||
* @return {Number} Either source value itself or limit value if range limits
|
||||
* are exceeded
|
||||
*/
|
||||
// eslint-disable-next-line no-nested-ternary
|
||||
export const inRange = (from, to, value) => (value < from ? from : value > to ? to : value);
|
||||
|
||||
export const createModalEvent = (args = {}) => ({
|
||||
id: generateId(),
|
||||
timestamp: Date.now(),
|
||||
canceled: false,
|
||||
...args,
|
||||
});
|
||||
|
||||
export const getMutationObserver = () => {
|
||||
if (typeof window !== 'undefined') {
|
||||
const prefixes = ['', 'WebKit', 'Moz', 'O', 'Ms'];
|
||||
|
||||
for (let i = 0; i < prefixes.length; i += 1) {
|
||||
const name = `${prefixes[i]}MutationObserver`;
|
||||
|
||||
if (name in window) {
|
||||
return window[name];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
export const createDivInBody = () => {
|
||||
const div = document.createElement('div');
|
||||
document.body.appendChild(div);
|
||||
|
||||
return div;
|
||||
};
|
||||
|
||||
export const blurActiveElement = () => {
|
||||
if (
|
||||
typeof document !== 'undefined'
|
||||
&& document.activeElement
|
||||
&& document.activeElement.tagName !== 'BODY'
|
||||
&& document.activeElement.blur
|
||||
) {
|
||||
document.activeElement.blur();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user