add 3 demo calendar
This commit is contained in:
parent
da46b61ad7
commit
466ecf5981
20 changed files with 765 additions and 33 deletions
|
|
@ -1,9 +1,9 @@
|
|||
version: "3.4"
|
||||
services:
|
||||
hi:
|
||||
qualifying-exam-cms:
|
||||
image: docker.frappet.com/demo/qualifying-exam-cms:latest
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "4010:80"
|
||||
environment:
|
||||
TZ: Asia/Bangkok
|
||||
TZ: Asia/Bangkok
|
||||
46
cms/package-lock.json
generated
46
cms/package-lock.json
generated
|
|
@ -18,6 +18,7 @@
|
|||
"@typescript-eslint/eslint-plugin": "^5.45.0",
|
||||
"@typescript-eslint/parser": "^5.45.0",
|
||||
"autoprefixer": "^10.4.7",
|
||||
"dayjs": "^1.11.7",
|
||||
"eslint": "^8.28.0",
|
||||
"eslint-config-prettier": "^8.5.0",
|
||||
"eslint-plugin-svelte3": "^4.0.0",
|
||||
|
|
@ -28,6 +29,7 @@
|
|||
"prettier-plugin-svelte": "^2.8.1",
|
||||
"sass": "^1.58.3",
|
||||
"svelte": "^3.54.0",
|
||||
"svelte-calendar": "^3.1.6",
|
||||
"svelte-check": "^3.0.1",
|
||||
"svelte-preprocess": "^4.10.7",
|
||||
"svelte-seo": "^1.5.3",
|
||||
|
|
@ -1553,6 +1555,12 @@
|
|||
"postcss": "^8.1.6"
|
||||
}
|
||||
},
|
||||
"node_modules/dayjs": {
|
||||
"version": "1.11.7",
|
||||
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.7.tgz",
|
||||
"integrity": "sha512-+Yw9U6YO5TQohxLcIkrXBeY73WP3ejHWVvx8XCk3gxvQDCTEmS48ZrSZCKciI7Bhl/uCMyxYtE9UqRILmFphkQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/debug": {
|
||||
"version": "4.3.4",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
|
||||
|
|
@ -2514,6 +2522,12 @@
|
|||
"npm": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/just-throttle": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/just-throttle/-/just-throttle-2.3.1.tgz",
|
||||
"integrity": "sha512-0H4miIAWZYpnpg7oD/Y/PBb77ISSHAETif5xK9EnwIgYCO6oC8ErkJxDumMUTR44shSOwptRIArRuvNuvN/hOw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/jwa": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz",
|
||||
|
|
@ -3563,6 +3577,16 @@
|
|||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/svelte-calendar": {
|
||||
"version": "3.1.6",
|
||||
"resolved": "https://registry.npmjs.org/svelte-calendar/-/svelte-calendar-3.1.6.tgz",
|
||||
"integrity": "sha512-jOHiPlxBAa1LGVFQZoczdAGnCSI2RKcuQQHj32TjLbXO/P2Povx4JphPEcP7XjKj/s/jimBC6xQyFhyjD0vxbg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"dayjs": "^1.10.6",
|
||||
"just-throttle": "^2.3.1"
|
||||
}
|
||||
},
|
||||
"node_modules/svelte-check": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/svelte-check/-/svelte-check-3.1.0.tgz",
|
||||
|
|
@ -5160,6 +5184,12 @@
|
|||
"tailwindcss": "^3"
|
||||
}
|
||||
},
|
||||
"dayjs": {
|
||||
"version": "1.11.7",
|
||||
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.7.tgz",
|
||||
"integrity": "sha512-+Yw9U6YO5TQohxLcIkrXBeY73WP3ejHWVvx8XCk3gxvQDCTEmS48ZrSZCKciI7Bhl/uCMyxYtE9UqRILmFphkQ==",
|
||||
"dev": true
|
||||
},
|
||||
"debug": {
|
||||
"version": "4.3.4",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
|
||||
|
|
@ -5882,6 +5912,12 @@
|
|||
"semver": "^7.3.8"
|
||||
}
|
||||
},
|
||||
"just-throttle": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/just-throttle/-/just-throttle-2.3.1.tgz",
|
||||
"integrity": "sha512-0H4miIAWZYpnpg7oD/Y/PBb77ISSHAETif5xK9EnwIgYCO6oC8ErkJxDumMUTR44shSOwptRIArRuvNuvN/hOw==",
|
||||
"dev": true
|
||||
},
|
||||
"jwa": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz",
|
||||
|
|
@ -6575,6 +6611,16 @@
|
|||
"integrity": "sha512-S+87/P0Ve67HxKkEV23iCdAh/SX1xiSfjF1HOglno/YTbSTW7RniICMCofWGdJJbdjw3S+0PfFb1JtGfTXE0oQ==",
|
||||
"dev": true
|
||||
},
|
||||
"svelte-calendar": {
|
||||
"version": "3.1.6",
|
||||
"resolved": "https://registry.npmjs.org/svelte-calendar/-/svelte-calendar-3.1.6.tgz",
|
||||
"integrity": "sha512-jOHiPlxBAa1LGVFQZoczdAGnCSI2RKcuQQHj32TjLbXO/P2Povx4JphPEcP7XjKj/s/jimBC6xQyFhyjD0vxbg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"dayjs": "^1.10.6",
|
||||
"just-throttle": "^2.3.1"
|
||||
}
|
||||
},
|
||||
"svelte-check": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/svelte-check/-/svelte-check-3.1.0.tgz",
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@
|
|||
"@typescript-eslint/eslint-plugin": "^5.45.0",
|
||||
"@typescript-eslint/parser": "^5.45.0",
|
||||
"autoprefixer": "^10.4.7",
|
||||
"dayjs": "^1.11.7",
|
||||
"eslint": "^8.28.0",
|
||||
"eslint-config-prettier": "^8.5.0",
|
||||
"eslint-plugin-svelte3": "^4.0.0",
|
||||
|
|
@ -31,6 +32,7 @@
|
|||
"prettier-plugin-svelte": "^2.8.1",
|
||||
"sass": "^1.58.3",
|
||||
"svelte": "^3.54.0",
|
||||
"svelte-calendar": "^3.1.6",
|
||||
"svelte-check": "^3.0.1",
|
||||
"svelte-preprocess": "^4.10.7",
|
||||
"svelte-seo": "^1.5.3",
|
||||
|
|
|
|||
131
cms/src/lib/components/Calendar/Calendar.svelte
Normal file
131
cms/src/lib/components/Calendar/Calendar.svelte
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
<script lang="ts">
|
||||
import {createEventDispatcher, onMount} from 'svelte'
|
||||
let dispatch = createEventDispatcher();
|
||||
import calendarize from './calendarize'
|
||||
import Arrow from './CalendarArrow.svelte';
|
||||
export let year = 2023;
|
||||
export let month = 0; // Jan
|
||||
export let offset = 0; // Sun
|
||||
export let locale = "en" as (keyof typeof text_labels)|(keyof typeof text_months);
|
||||
export let buddhist = false
|
||||
const text_labels = {
|
||||
en: ['SUN', 'MON', 'TUE', 'WED', 'THU', 'FRI', 'SAT'],
|
||||
th: ['อา', 'จ', 'อ', 'พ', 'พฤ', 'ศ', 'ส'],
|
||||
}
|
||||
const text_months = {
|
||||
en: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'July', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
|
||||
th: ['ม.ค.', 'ก.พ.', 'มี.ค.', 'เม.ย.', 'พ.ค.', 'มิ.ย.', 'ก.ค.', 'ส.ค.', 'ก.ย.', 'ต.ค.', 'พ.ย.', 'ธ.ค.'],
|
||||
}
|
||||
const today = new Date
|
||||
const labels = (locale in text_labels)? text_labels[locale]:text_labels.en
|
||||
const months = (locale in text_months)? text_months[locale]:text_months.en
|
||||
$: today_month = today.getMonth();
|
||||
$: today_year = today.getFullYear();
|
||||
$: today_day = today.getDate();
|
||||
|
||||
let prev = calendarize(new Date(year, month-1), offset);
|
||||
let current = calendarize(new Date(year, month), offset);
|
||||
let next = calendarize(new Date(year, month+1), offset);
|
||||
|
||||
function toPrev() {
|
||||
[current, next] = [prev, current];
|
||||
|
||||
if (--month < 0) {
|
||||
month = 11;
|
||||
year--;
|
||||
}
|
||||
|
||||
prev = calendarize(new Date(year, month-1), offset);
|
||||
}
|
||||
|
||||
function toNext() {
|
||||
[prev, current] = [current, next];
|
||||
|
||||
if (++month > 11) {
|
||||
month = 0;
|
||||
year++;
|
||||
}
|
||||
next = calendarize(new Date(year, month+1), offset);
|
||||
}
|
||||
|
||||
function isToday(day:number ) {
|
||||
return today && today_year === year && today_month === month && today_day === day;
|
||||
}
|
||||
</script>
|
||||
|
||||
<header>
|
||||
<Arrow left on:click={toPrev} />
|
||||
<h4>{months[month]} - {year+ (buddhist?543:0)}</h4>
|
||||
<Arrow on:click={toNext} />
|
||||
</header>
|
||||
|
||||
<div class="month">
|
||||
{#each labels as txt, idx (txt)}
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<span class="label" on:click={()=>dispatch('headerClick',txt)} >{ labels[(idx + offset) % 7] }</span>
|
||||
{/each}
|
||||
|
||||
{#each { length:6 } as w,idxw (idxw)}
|
||||
{#if current[idxw]}
|
||||
{#each { length:7 } as d,idxd (idxd)}
|
||||
{#if current[idxw][idxd] != 0}
|
||||
<span class="date" class:today={isToday(current[idxw][idxd])}>
|
||||
{ current[idxw][idxd] }
|
||||
</span>
|
||||
{:else if (idxw < 1)}
|
||||
<span class="date other">{ prev[prev.length - 1][idxd] }</span>
|
||||
{:else}
|
||||
<span class="date other">{ next[0][idxd] }</span>
|
||||
{/if}
|
||||
{/each}
|
||||
{/if}
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
header {
|
||||
display: flex;
|
||||
margin: 2rem auto;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
h4 {
|
||||
display: block;
|
||||
text-align: center;
|
||||
text-transform: uppercase;
|
||||
font-size: 140%;
|
||||
margin: 0 1rem;
|
||||
}
|
||||
.month {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(7, 1fr);
|
||||
text-align: right;
|
||||
grid-gap: 4px;
|
||||
}
|
||||
.label {
|
||||
font-weight: 300;
|
||||
text-align: center;
|
||||
text-transform: uppercase;
|
||||
margin-bottom: 0.5rem;
|
||||
opacity: 0.6;
|
||||
}
|
||||
.date {
|
||||
height: 50px;
|
||||
font-size: 16px;
|
||||
letter-spacing: -1px;
|
||||
border: 1px solid #e6e4e4;
|
||||
padding-right: 4px;
|
||||
font-weight: 700;
|
||||
padding: 0.5rem;
|
||||
}
|
||||
.date.today {
|
||||
color: #5286fa;
|
||||
background: #c4d9fd;
|
||||
border-color: currentColor;
|
||||
}
|
||||
.date.other {
|
||||
opacity: 0.2;
|
||||
}
|
||||
</style>
|
||||
22
cms/src/lib/components/Calendar/CalendarArrow.svelte
Normal file
22
cms/src/lib/components/Calendar/CalendarArrow.svelte
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
<script lang="ts">
|
||||
export let left = false;
|
||||
</script>
|
||||
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<svg class:left on:click xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
|
||||
<path fill="#c4d9fd" d="M0 256c0 141.2 114.8 256 256 256V0A256.3 256.3 0 000 256z"/>
|
||||
<path fill="#c4d9fd" d="M256 0v512c141.2 0 256-114.8 256-256S397.2 0 256 0z"/>
|
||||
<path fill="#5286fa" d="M226 115.4a23.3 23.3 0 00-33 33L300.7 256 193 363.7a23.3 23.3 0 1033 32.9l124-124.1a23.3 23.3 0 000-33l-124-124z"/></svg>
|
||||
|
||||
<style>
|
||||
svg {
|
||||
width: 3rem;
|
||||
height: 3rem;
|
||||
padding: 12px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.left {
|
||||
transform: rotateY(180deg);
|
||||
}
|
||||
</style>
|
||||
22
cms/src/lib/components/Calendar/calendarize.ts
Normal file
22
cms/src/lib/components/Calendar/calendarize.ts
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
//Modify by Oom
|
||||
//code from https://www.npmjs.com/package/calendarize
|
||||
export default function (target?: Date|string, offset=0) {
|
||||
let i=0, j=0, week = Array(7)
|
||||
const out=[], date = new Date(target || new Date);
|
||||
const year = date.getFullYear(), month = date.getMonth();
|
||||
// day index (of week) for 1st of month
|
||||
let first = new Date(year, month, 1 - (offset | 0)).getDay();
|
||||
|
||||
// how many days there are in this month
|
||||
const days = new Date(year, month+1, 0).getDate();
|
||||
|
||||
while (i < days) {
|
||||
for (j=0, week=Array(7); j < 7;) {
|
||||
while (j < first) week[j++] = 0;
|
||||
week[j++] = ++i > days ? 0 : i;
|
||||
first = 0;
|
||||
}
|
||||
out.push(week);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
191
cms/src/lib/components/Calendar2/Calendar.svelte
Normal file
191
cms/src/lib/components/Calendar2/Calendar.svelte
Normal file
|
|
@ -0,0 +1,191 @@
|
|||
<script lang="ts">
|
||||
import Calendar from "./CalendarContent.svelte";
|
||||
export let locale = "en" as (keyof typeof text_labels)|(keyof typeof text_months);
|
||||
export let buddhist = false
|
||||
const text_labels = {
|
||||
en: ['SUN', 'MON', 'TUE', 'WED', 'THU', 'FRI', 'SAT'],
|
||||
th: ['อา', 'จ', 'อ', 'พ', 'พฤ', 'ศ', 'ส'],
|
||||
}
|
||||
const text_months = {
|
||||
en: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'July', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
|
||||
th: ['ม.ค.', 'ก.พ.', 'มี.ค.', 'เม.ย.', 'พ.ค.', 'มิ.ย.', 'ก.ค.', 'ส.ค.', 'ก.ย.', 'ต.ค.', 'พ.ย.', 'ธ.ค.'],
|
||||
}
|
||||
const labels = (locale in text_labels)? text_labels[locale]:text_labels.en
|
||||
const months = (locale in text_months)? text_months[locale]:text_months.en
|
||||
|
||||
let headers: string[] = [];
|
||||
let now = new Date();
|
||||
let year = now.getFullYear(); // this is the month & year displayed
|
||||
let month = now.getMonth();
|
||||
let eventText="Click an item or date";
|
||||
|
||||
var days: { name:string,enabled:boolean,date: Date }[] = []; // The days to display in each box
|
||||
|
||||
function randInt(max: number) {
|
||||
return Math.floor(Math.random()*max)+1;
|
||||
}
|
||||
|
||||
// The Calendar Component just displays stuff in a row & column. It has no knowledge of dates.
|
||||
// The items[] below are placed (by you) in a specified row & column of the calendar.
|
||||
// You need to call findRowCol() to calc the row/col based on each items start date. Each date box has a Date() property.
|
||||
// And, if an item overlaps rows, then you need to add a 2nd item on the subsequent row.
|
||||
var items: (
|
||||
{ title: string; className: string; date: Date; len: number; isBottom?: boolean;
|
||||
detailHeader?: string; detailContent?: string; vlen?: number; startCol?:number;startRow?:number}
|
||||
)[] = [];
|
||||
|
||||
function initMonthItems() {
|
||||
let y = year;
|
||||
let m = month;
|
||||
let d1=new Date(y,m,randInt(7)+7);
|
||||
items=[
|
||||
{title:"11:00 Task Early in month",className:"task--primary",date:new Date(y,m,randInt(6)),len:randInt(4)+1},
|
||||
{title:"7:30 Wk 2 tasks",className:"task--warning",date:d1,len:randInt(4)+2},
|
||||
{title:"Overlapping Stuff (isBottom:true)",date:d1,className:"task--info",len:4,isBottom:true},
|
||||
{title:"10:00 More Stuff to do",date:new Date(y,m,randInt(7)+14),className:"task--info",len:randInt(4)+1,detailHeader:"Difficult",detailContent:"But not especially so"},
|
||||
{title:"All day task",date:new Date(y,m,randInt(7)+21),className:"task--danger",len:1,vlen:2},
|
||||
];
|
||||
|
||||
//This is where you calc the row/col to put each dated item
|
||||
for (let i of items) {
|
||||
let rc = findRowCol(i.date);
|
||||
if (rc == null) {
|
||||
console.log('didn`t find date for ',i);
|
||||
console.log(i.date);
|
||||
console.log(days);
|
||||
i.startCol = i.startRow = 0;
|
||||
} else {
|
||||
i.startCol = rc.col;
|
||||
i.startRow = rc.row;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$: month,year,initContent();
|
||||
|
||||
// choose what date/day gets displayed in each date box.
|
||||
function initContent() {
|
||||
headers = labels;
|
||||
initMonth();
|
||||
initMonthItems();
|
||||
}
|
||||
|
||||
function initMonth() {
|
||||
days = [];
|
||||
let monthAbbrev = months[month].slice(0,3);
|
||||
let nextMonthAbbrev = months[(month+1)%12].slice(0,3);
|
||||
// find the last Monday of the previous month
|
||||
var firstDay = new Date(year, month, 1).getDay();
|
||||
//console.log('fd='+firstDay+' '+dayNames[firstDay]);
|
||||
var daysInThisMonth = new Date(year, month+1, 0).getDate();
|
||||
var daysInLastMonth = new Date(year, month, 0).getDate();
|
||||
var prevMonth = month==0 ? 11 : month-1;
|
||||
|
||||
// show the days before the start of this month (disabled) - always less than 7
|
||||
for (let i=daysInLastMonth-firstDay;i<daysInLastMonth;i++) {
|
||||
let d = new Date(prevMonth==11?year-1:year,prevMonth,i+1);
|
||||
days.push({name:''+(i+1),enabled:false,date:d,});
|
||||
}
|
||||
// show the days in this month (enabled) - always 28 - 31
|
||||
for (let i=0;i<daysInThisMonth;i++) {
|
||||
let d = new Date(year,month,i+1);
|
||||
if (i==0) days.push({name:monthAbbrev+' '+(i+1),enabled:true,date:d,});
|
||||
else days.push({name:''+(i+1),enabled:true,date:d,});
|
||||
//console.log('i='+i+' dt is '+d+' date() is '+d.getDate());
|
||||
}
|
||||
// show any days to fill up the last row (disabled) - always less than 7
|
||||
for (let i=0;days.length%7;i++) {
|
||||
let d = new Date((month==11?year+1:year),(month+1)%12,i+1);
|
||||
if (i==0) days.push({name:nextMonthAbbrev+' '+(i+1),enabled:false,date:d,});
|
||||
else days.push({name:''+(i+1),enabled:false,date:d,});
|
||||
}
|
||||
}
|
||||
|
||||
function findRowCol(dt:Date) {
|
||||
for (let i=0;i<days.length;i++) {
|
||||
let d = days[i].date;
|
||||
if (d.getFullYear() === dt.getFullYear()
|
||||
&& d.getMonth() === dt.getMonth()
|
||||
&& d.getDate() === dt.getDate())
|
||||
return {row:Math.floor(i/7)+2,col:i%7+1};
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function itemClick(e: { date: { toString: () => string; }; }) {
|
||||
eventText='itemClick '+JSON.stringify(e) + ' localtime='+e.date.toString();
|
||||
}
|
||||
function dayClick(e: { date: { toString: () => string; }; }) {
|
||||
eventText='onDayClick '+JSON.stringify(e) + ' localtime='+e.date.toString();
|
||||
}
|
||||
function headerClick(e: any) {
|
||||
eventText='onHheaderClick '+JSON.stringify(e);
|
||||
}
|
||||
function next() {
|
||||
month++;
|
||||
if (month == 12) {
|
||||
year++;
|
||||
month=0;
|
||||
}
|
||||
}
|
||||
function prev() {
|
||||
if (month==0) {
|
||||
month=11;
|
||||
year--;
|
||||
} else {
|
||||
month--;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="calendar-container">
|
||||
<div class="calendar-header">
|
||||
<h1>
|
||||
<button on:click={()=>year--}>≪</button>
|
||||
<button on:click={()=>prev()}><</button>
|
||||
{months[month]} {year+ (buddhist?543:0)}
|
||||
<button on:click={()=>next()}>></button>
|
||||
<button on:click={()=>year++}>≫</button>
|
||||
</h1>
|
||||
{eventText}
|
||||
</div>
|
||||
|
||||
<Calendar
|
||||
{headers}
|
||||
{days}
|
||||
{items}
|
||||
on:dayClick={(e)=>dayClick(e.detail)}
|
||||
on:itemClick={(e)=>itemClick(e.detail)}
|
||||
on:headerClick={(e)=>headerClick(e.detail)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.calendar-container {
|
||||
width: 100%;
|
||||
margin: auto;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 2px 20px rgba(0, 0, 0, 0.1);
|
||||
border-radius: 10px;
|
||||
background: #fff;
|
||||
max-width: 1200px;
|
||||
}
|
||||
.calendar-header {
|
||||
text-align: center;
|
||||
padding: 20px 0;
|
||||
background: #eef;
|
||||
border-bottom: 1px solid rgba(166, 168, 179, 0.12);
|
||||
}
|
||||
.calendar-header h1 {
|
||||
margin: 0;
|
||||
font-size: 18px;
|
||||
}
|
||||
.calendar-header button {
|
||||
background: #eef;
|
||||
border: 1px ;
|
||||
padding: 6;
|
||||
color: rgba(81, 86, 93, 0.7);
|
||||
cursor: pointer;
|
||||
outline: 0;
|
||||
}
|
||||
</style>
|
||||
222
cms/src/lib/components/Calendar2/CalendarContent.svelte
Normal file
222
cms/src/lib/components/Calendar2/CalendarContent.svelte
Normal file
|
|
@ -0,0 +1,222 @@
|
|||
<div class="calendar">
|
||||
{#each headers as header}
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<span class="day-name" on:click={()=>dispatch('headerClick',header)}>{header}</span>
|
||||
{/each}
|
||||
|
||||
{#each days as day}
|
||||
{#if day.enabled}
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<span class="day" on:click={()=>dispatch('dayClick',day)}>{day.name}</span>
|
||||
{:else}
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<span class="day day-disabled" on:click={()=>dispatch('dayClick',day)}>{day.name}</span>
|
||||
{/if}
|
||||
{/each}
|
||||
|
||||
{#each items as item}
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<section
|
||||
on:click={()=>dispatch('itemClick',item)}
|
||||
class="task {item.className}"
|
||||
style="grid-column: {item.startCol} / span {item.len};
|
||||
grid-row: {item.startRow};
|
||||
align-self: {item.isBottom?'end':'center'};"
|
||||
>
|
||||
{item.title}
|
||||
{#if item.detailHeader}
|
||||
<div class="task-detail">
|
||||
<h2>{item.detailHeader}</h2>
|
||||
<p>{item.detailContent}</p>
|
||||
</div>
|
||||
{/if}
|
||||
</section>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<script>
|
||||
import {createEventDispatcher, onMount} from 'svelte';
|
||||
|
||||
/**
|
||||
* @type {any[]}
|
||||
*/
|
||||
export var headers = [];
|
||||
/**
|
||||
* @type {any[]}
|
||||
*/
|
||||
export let days = [];
|
||||
/**
|
||||
* @type {any[]}
|
||||
*/
|
||||
export let items = [];
|
||||
|
||||
let dispatch = createEventDispatcher();
|
||||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.calendar {
|
||||
display: grid;
|
||||
width: 100%;
|
||||
grid-template-columns: repeat(7, minmax(120px, 1fr));
|
||||
grid-template-rows: 50px;
|
||||
grid-auto-rows: 120px;
|
||||
overflow: auto;
|
||||
}
|
||||
.day {
|
||||
border-bottom: 1px solid rgba(166, 168, 179, 0.12);
|
||||
border-right: 1px solid rgba(166, 168, 179, 0.12);
|
||||
text-align: right;
|
||||
padding: 14px 20px;
|
||||
letter-spacing: 1px;
|
||||
font-size: 14px;
|
||||
box-sizing: border-box;
|
||||
color: #98a0a6;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
.day:nth-of-type(7n + 7) {
|
||||
border-right: 0;
|
||||
}
|
||||
.day:nth-of-type(n + 1):nth-of-type(-n + 7) {
|
||||
grid-row: 1;
|
||||
}
|
||||
.day:nth-of-type(n + 8):nth-of-type(-n + 14) {
|
||||
grid-row: 2;
|
||||
}
|
||||
.day:nth-of-type(n + 15):nth-of-type(-n + 21) {
|
||||
grid-row: 3;
|
||||
}
|
||||
.day:nth-of-type(n + 22):nth-of-type(-n + 28) {
|
||||
grid-row: 4;
|
||||
}
|
||||
.day:nth-of-type(n + 29):nth-of-type(-n + 35) {
|
||||
grid-row: 5;
|
||||
}
|
||||
.day:nth-of-type(n + 36):nth-of-type(-n + 42) {
|
||||
grid-row: 6;
|
||||
}
|
||||
.day:nth-of-type(7n + 1) {
|
||||
grid-column: 1/1;
|
||||
}
|
||||
.day:nth-of-type(7n + 2) {
|
||||
grid-column: 2/2;
|
||||
}
|
||||
.day:nth-of-type(7n + 3) {
|
||||
grid-column: 3/3;
|
||||
}
|
||||
.day:nth-of-type(7n + 4) {
|
||||
grid-column: 4/4;
|
||||
}
|
||||
.day:nth-of-type(7n + 5) {
|
||||
grid-column: 5/5;
|
||||
}
|
||||
.day:nth-of-type(7n + 6) {
|
||||
grid-column: 6/6;
|
||||
}
|
||||
.day:nth-of-type(7n + 7) {
|
||||
grid-column: 7/7;
|
||||
}
|
||||
.day-name {
|
||||
font-size: 12px;
|
||||
text-transform: uppercase;
|
||||
color: #e9a1a7;
|
||||
text-align: center;
|
||||
border-bottom: 1px solid rgba(166, 168, 179, 0.12);
|
||||
line-height: 50px;
|
||||
font-weight: 500;
|
||||
}
|
||||
.day-disabled {
|
||||
color: rgba(152, 160, 166, 0.5);
|
||||
background-color: #ffffff;
|
||||
background-image: url("data:image/svg+xml,%3Csvg width='40' height='40' viewBox='0 0 40 40' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='%23fdf9ff' fill-opacity='1' fill-rule='evenodd'%3E%3Cpath d='M0 40L40 0H20L0 20M40 40V20L20 40'/%3E%3C/g%3E%3C/svg%3E");
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.task {
|
||||
border-left-width: 3px;
|
||||
padding: 8px 12px;
|
||||
margin: 10px;
|
||||
border-left-style: solid;
|
||||
font-size: 14px;
|
||||
position: relative;
|
||||
align-self: center;
|
||||
z-index:2;
|
||||
border-radius: 15px;
|
||||
}
|
||||
.task--warning {
|
||||
border-left-color: #fdb44d;
|
||||
background: #fef0db;
|
||||
color: #fc9b10;
|
||||
margin-top: -5px;
|
||||
}
|
||||
.task--danger {
|
||||
border-left-color: #fa607e;
|
||||
grid-column: 2 / span 3;
|
||||
grid-row: 3;
|
||||
margin-top: 15px;
|
||||
background: rgba(253, 197, 208, 0.7);
|
||||
color: #f8254e;
|
||||
}
|
||||
.task--info {
|
||||
border-left-color: #4786ff;
|
||||
margin-top: 15px;
|
||||
background: rgba(218, 231, 255, 0.7);
|
||||
color: #0a5eff;
|
||||
}
|
||||
.task--primary {
|
||||
background: #4786ff;
|
||||
border: 0;
|
||||
border-radius: 14px;
|
||||
color: #f00;
|
||||
box-shadow: 0 10px 14px rgba(71, 134, 255, 0.4);
|
||||
}
|
||||
.task-detail {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: calc(100% + 8px);
|
||||
background: #efe;
|
||||
border: 1px solid rgba(166, 168, 179, 0.2);
|
||||
color: #100;
|
||||
padding: 20px;
|
||||
box-sizing: border-box;
|
||||
border-radius: 14px;
|
||||
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.08);
|
||||
z-index: 2;
|
||||
}
|
||||
.task-detail:after, .task-detail:before {
|
||||
bottom: 100%;
|
||||
left: 30%;
|
||||
border: solid transparent;
|
||||
content: " ";
|
||||
height: 0;
|
||||
width: 0;
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
}
|
||||
.task-detail:before {
|
||||
border-bottom-color: rgba(166, 168, 179, 0.2);
|
||||
border-width: 8px;
|
||||
margin-left: -8px;
|
||||
}
|
||||
.task-detail:after {
|
||||
border-bottom-color: #fff;
|
||||
border-width: 6px;
|
||||
margin-left: -6px;
|
||||
}
|
||||
.task-detail h2 {
|
||||
font-size: 15px;
|
||||
margin: 0;
|
||||
color: #91565d;
|
||||
}
|
||||
.task-detail p {
|
||||
margin-top: 4px;
|
||||
font-size: 12px;
|
||||
margin-bottom: 0;
|
||||
font-weight: 500;
|
||||
color: rgba(81, 86, 93, 0.7);
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
|
||||
5
cms/src/lib/data/qualify-exam.json
Normal file
5
cms/src/lib/data/qualify-exam.json
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
[
|
||||
{"id":20,"date":"2023/03/25","detail":"ประกาศรับสมัครสอบและคัดเลือกบุคคลเข้ารับราชการเป็นลูกจ้างชั่วคราว","institute":"สำนักอนามัย","time":"๕/๒๕๖๕"},
|
||||
{"id":19,"date":"2023/03/23","detail":"ประกาศรายชื่อผู้มีสิทธิเข้ารับการสอบคัดเลือกบุคคลภายนอกช่วยปฎิบัติราชการ","institute":"สำนักการแพทย์","time":"๔/๒๕๖๔"},
|
||||
{"id":18,"date":"2023/03/20","detail":"ประกาศกรุงเทพมหานคร เรื่อง ประกาศผู้ชนะการเสนอราคา จ้างเหมาซ่อมแซมป้ายชื่อ","institute":"สำนักวัฒนธรรม กีฬาและการท่องเที่ยว","time":"๔/๒๕๖๓"}
|
||||
]
|
||||
4
cms/src/lib/data/qualify-exam.ts
Normal file
4
cms/src/lib/data/qualify-exam.ts
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
import exams from "./qualify-exam.json"
|
||||
export async function getExams(){
|
||||
return exams
|
||||
}
|
||||
|
|
@ -4,7 +4,7 @@
|
|||
</script>
|
||||
|
||||
<Header />
|
||||
<div class="p-10 max-w-4xl place-content-center ">
|
||||
<div class="p-5 max-w-4xl place-content-center ">
|
||||
<div>
|
||||
<slot />
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -7,5 +7,5 @@ export const load: PageServerLoad = async () => {
|
|||
}*/
|
||||
|
||||
export const load = (async () => {
|
||||
return getContact()
|
||||
return await getContact()
|
||||
}) satisfies PageServerLoad
|
||||
|
|
|
|||
|
|
@ -1,15 +1,54 @@
|
|||
<script lang="ts">
|
||||
import type { PageData } from './$types'
|
||||
import 'dayjs/locale/th.js';
|
||||
import 'dayjs/locale/zh-cn.js';
|
||||
import 'dayjs/locale/es.js';
|
||||
import 'dayjs/locale/ar-dz.js';
|
||||
// มันรองรับ พ.ศ. ใน dayjs แต่ใน velte-calendar ไม่ได้เอาไปใช้ ยังหาวิธีแก้หัวปฎิทินไม่ได้ยังเป็น พ.ศ.
|
||||
import buddhistEra from 'dayjs/plugin/buddhistEra'
|
||||
import dayjs from 'dayjs';
|
||||
dayjs.extend(buddhistEra)
|
||||
import { InlineCalendar, Swappable, themes } from 'svelte-calendar';
|
||||
import { onDestroy } from 'svelte';
|
||||
const locales = ['th','en', 'es', 'zh-cn', 'ar-dz'];
|
||||
$: dayjs.locale(locale);
|
||||
onDestroy(() => dayjs.locale('en'));
|
||||
|
||||
import Calendar2 from '$lib/components/Calendar2/Calendar.svelte';
|
||||
import Calendar from '$lib/components/Calendar/Calendar.svelte';
|
||||
const today = new Date
|
||||
const locale = "th"
|
||||
const buddhist = true //buddhist calendar + 543
|
||||
let year = today.getFullYear()
|
||||
let month = today.getMonth()
|
||||
|
||||
import ver from "$lib/ver.json"
|
||||
import type { PageData } from './$types'
|
||||
export let data: PageData;
|
||||
|
||||
</script>
|
||||
|
||||
<h1 class="text-4xl">{data.company_name}</h1>
|
||||
|
||||
<h1 class="text-4xl">เกี่ยวกับ {data.company_name}</h1>
|
||||
<div>{data.description}</div>
|
||||
<div>{ver.version}</div>
|
||||
<div>{ver.builddate}</div>
|
||||
|
||||
<h2 class="text-2xl">svelte-calendar</h2>
|
||||
<p>3rd svelte-calendar ไม่รองรับ พ.ศ.</p>
|
||||
<!-- note: Locale is not reactive -->
|
||||
<Swappable value={{ locale }} vertical={false} >
|
||||
<InlineCalendar theme={themes.light} />
|
||||
</Swappable>
|
||||
|
||||
<h2 class="text-2xl">Custom Calendar1</h2>
|
||||
<div>
|
||||
Calendar ทำเองยังไม่รองรับการแสดง event ในวัน
|
||||
</div>
|
||||
<Calendar {year} {month} {locale} {buddhist}/>
|
||||
|
||||
<h2 class="text-2xl">Custom Calendar2</h2>
|
||||
<div>
|
||||
ทำเองรองรับการแสดง event และคลิ้กได้ ได้แต่ยังปรับโค้ดไม่เรียบร้อย
|
||||
</div>
|
||||
<Calendar2 {locale} {buddhist} />
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,35 @@
|
|||
<h1 class="text-4xl">การสอบแข่งขัน</h1>
|
||||
<script lang="ts">
|
||||
import 'dayjs/locale/th.js';
|
||||
import 'dayjs/locale/zh-cn.js';
|
||||
import 'dayjs/locale/es.js';
|
||||
import 'dayjs/locale/ar-dz.js';
|
||||
// มันรองรับ พ.ศ. ใน dayjs แต่ใน velte-calendar ไม่ได้เอาไปใช้ ยังหาวิธีแก้หัวปฎิทินไม่ได้ยังเป็น พ.ศ.
|
||||
import buddhistEra from 'dayjs/plugin/buddhistEra'
|
||||
import dayjs from 'dayjs';
|
||||
dayjs.extend(buddhistEra)
|
||||
import { InlineCalendar, Swappable, themes } from 'svelte-calendar';
|
||||
import { onDestroy } from 'svelte';
|
||||
|
||||
fdsafda
|
||||
fdsafda sdaf
|
||||
sdaffsdasaf rfffffffffffffffff fffffffffffffffffffffffff ffffffffffff fgedfgfdgfdsg
|
||||
fedsddddddddddddd ddddddddddddddd ddddddddd d d d d d d d d d d d d xxxx
|
||||
|
||||
const locales = ['th','en', 'es', 'zh-cn', 'ar-dz'];
|
||||
let format = 'DD/MM/BBBB'
|
||||
let locale = 'th';
|
||||
|
||||
$: dayjs.locale(locale);
|
||||
|
||||
onDestroy(() => dayjs.locale('en'));
|
||||
</script>
|
||||
|
||||
|
||||
<h1 class="text-4xl">การสอบแข่งขัน</h1>
|
||||
<!-- note: Locale is not reactive -->
|
||||
<Swappable value={{ locale }} vertical={false} >
|
||||
<InlineCalendar theme={themes.light} {format} />
|
||||
</Swappable>
|
||||
|
||||
<div class="button-group">
|
||||
{#each locales as loc}
|
||||
<button on:click={() => (locale = loc)} class:selected={locale === loc}>
|
||||
{loc}
|
||||
</button> |
|
||||
{/each}
|
||||
</div>
|
||||
|
|
|
|||
8
cms/src/routes/qualifying/+page.server.ts
Normal file
8
cms/src/routes/qualifying/+page.server.ts
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
|
||||
import {getExams} from "$lib/data/qualify-exam"
|
||||
import type { PageServerLoad } from './$types'
|
||||
export const load: PageServerLoad = async () => {
|
||||
const exams = await getExams()
|
||||
return {exams}
|
||||
}
|
||||
|
||||
|
|
@ -1,6 +1,28 @@
|
|||
<script lang="ts">
|
||||
import type { PageData } from './$types'
|
||||
export let data: PageData;
|
||||
const exams = data.exams
|
||||
</script>
|
||||
|
||||
<h1 class="text-4xl">การสอบคัดเลือก</h1>
|
||||
|
||||
<button class="btn btn-outline">Button</button>
|
||||
<button class="btn btn-outline btn-primary">Button</button>
|
||||
<button class="btn btn-outline btn-secondary">Button</button>
|
||||
|
||||
้<h1 class="text-2xl">รายการสอบ</h1>
|
||||
<div class="overflow-x-auto">
|
||||
<table class="table table-compact w-full">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>วันที่</th>
|
||||
<th>การสอบ</th>
|
||||
<th>หน่วยงาน</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{#each exams as exam}
|
||||
<tr>
|
||||
<th><a href={"/exams/"+exam.id}>{exam.date}</a></th>
|
||||
<td>{exam.detail}</td>
|
||||
<td>{exam.institute}</td>
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
|
@ -15,6 +15,7 @@ const config = {
|
|||
],
|
||||
|
||||
kit: {
|
||||
|
||||
// adapter-auto only supports some environments, see https://kit.svelte.dev/docs/adapter-auto for a list.
|
||||
// If your environment is not supported or you settled on a specific environment, switch out the adapter.
|
||||
// See https://kit.svelte.dev/docs/adapters for more information about adapters.
|
||||
|
|
|
|||
|
|
@ -6,4 +6,5 @@ export default defineConfig({
|
|||
test: {
|
||||
include: ['src/**/*.{test,spec}.{js,ts}']
|
||||
}
|
||||
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue