Add calendar

pull/1/head
Made Baruna 2022-08-13 16:36:53 +07:00
parent 9b66e3368b
commit d0fa5d33f4
13 changed files with 1617 additions and 50 deletions

View File

@ -105,6 +105,7 @@
'weapons',
'artifacts',
'radiant-spincrystal',
'calendar',
].includes(segment)}
image="/images/items.png"
label={$t('sidebar.database')}
@ -117,6 +118,7 @@
{ label: $t('sidebar.artifacts'), href: '/artifacts' },
{ label: $t('sidebar.fishing'), href: '/fishing' },
{ label: $t('sidebar.radiantSpincrystal'), href: '/radiant-spincrystal' },
{ label: $t('sidebar.calendar'), href: '/calendar' },
]}
/>
<SidebarItem

53
src/data/birthdays.js Normal file
View File

@ -0,0 +1,53 @@
export const birthdays = {
thoma: [1, 9],
diona: [1, 18],
rosaria: [1, 24],
beidou: [2, 14],
sangonomiya_kokomi: [2, 22],
bennett: [2, 29],
qiqi: [3, 3],
shenhe: [3, 10],
jean: [3, 14],
noelle: [3, 21],
kamisato_ayato: [3, 26],
aloy: [4, 4],
xiao: [4, 17],
yelan: [4, 20],
diluc: [4, 30],
gorou: [5, 18],
yun_jin: [5, 21],
fischl: [5, 27],
arataki_itto: [6, 1],
paimon: [6, 1],
lisa: [6, 9],
venti: [6, 16],
yoimiya: [6, 21],
raiden_shogun: [6, 26],
yae_miko: [6, 27],
barbara: [7, 5],
kujou_sara: [7, 14],
hu_tao: [7, 15],
tartaglia: [7, 20],
shikanoin_heizou: [7, 24],
klee: [7, 27],
kuki_shinobu: [7, 27],
yanfei: [7, 28],
amber: [8, 10],
ningguang: [8, 26],
mona: [8, 31],
chongyun: [9, 7],
razor: [9, 9],
albedo: [9, 13],
kamisato_ayaka: [9, 28],
xingqiu: [10, 9],
xinyan: [10, 16],
sayu: [10, 19],
eula: [10, 25],
kaedehara_kazuha: [10, 29],
xiangling: [11, 2],
keqing: [11, 20],
sucrose: [11, 26],
kaeya: [11, 30],
ganyu: [12, 2],
zhongli: [12, 31],
};

693
src/data/events.js Normal file
View File

@ -0,0 +1,693 @@
export const events = [
{
name: 'Vishaps and Where to Find Them',
image: 'vishaps_and_where_to_find_them.jpg',
pos: '0% 30%',
start: '2021-03-05 04:00:00',
end: '2021-03-12 04:00:00',
color: '#F6AD55',
showOnHome: true,
},
{
name: 'A Wanmin Welcome - Web Event',
pos: '0% 45%',
image: 'a_wanmin_welcome.png',
start: '2021-03-16 13:00:00',
end: '2021-03-25 23:59:00',
color: '#FAE2B4',
url: 'https://webstatic-sea.mihoyo.com/ys/event/e20210316cooking-sea/index.html?lang=en-us?utm_source=hoyolab&utm_medium=banner',
showOnHome: true,
},
{
name: 'Outland Gastronomy - Daily Login Event',
pos: '0% 50%',
image: 'outland_gastronomy.jpg',
start: '2021-03-17 06:00:00',
end: '2021-04-01 04:00:00',
color: '#DDD7E8',
zoom: '180%',
url: 'https://genshin.mihoyo.com/en/news/detail/9262',
showOnHome: true,
},
{
name: 'Contending Tides Event',
pos: '0% 10%',
image: 'contending_tides.jpg',
start: '2021-04-02 10:00:00',
end: '2021-04-12 04:00:00',
color: '#6C99F7',
zoom: '180%',
url: 'https://www.hoyolab.com/genshin/article/275307',
showOnHome: true,
},
{
name: 'A Thousand Questions With Paimon',
pos: '60% 30%',
image: 'a_thousand_questions_with_paimon.jpg',
start: '2021-04-06 13:00:00',
end: '2021-04-08 23:59:59',
color: '#EFDEB4',
zoom: '180%',
url: 'https://genshin.mihoyo.com/en/news/detail/9694',
showOnHome: true,
},
{
name: 'Wishful Drops - Oceanid Event',
pos: '0% 20%',
image: 'wishful_drops.jpg',
start: '2021-04-09 10:00:00',
end: '2021-04-16 04:00:00',
color: '#579DE5',
zoom: '170%',
url: 'https://www.hoyolab.com/genshin/article/286280',
showOnHome: true,
},
{
name: 'Hilidream Camp',
pos: '0% 58%',
image: 'hilidream_camp.png',
start: '2021-04-21 13:00:00',
end: '2021-04-27 23:59:59',
color: '#FAF8EB',
zoom: '180%',
url: 'https://webstatic-sea.mihoyo.com/ys/event/e20210421-homeland/index.html',
showOnHome: true,
},
{
name: 'To the Stars Once More',
pos: '20% 15%',
image: 'to_the_stars_once_more.jpg',
start: '2021-04-28 13:00:00',
end: '2021-05-17 23:59:59',
color: '#DDC59A',
zoom: '180%',
url: 'https://genshin.mihoyo.com/en/news/detail/12368',
showOnHome: true,
},
{
name: 'Windtrace',
pos: '0% 80%',
image: 'windtrace.jpg',
start: '2021-05-14 10:00:00',
end: '2021-05-24 04:00:00',
color: '#E8D8B7',
zoom: '190%',
url: 'https://genshin.mihoyo.com/en/news/detail/12281',
showOnHome: true,
},
{
name: 'Battlefront: Misty Dungeon',
pos: '0% 45%',
image: 'battlefront_misty_dungeon.jpg',
start: '2021-05-21 10:00:00',
end: '2021-05-31 04:00:00',
color: '#95C9B9',
zoom: '200%',
url: 'https://www.hoyolab.com/genshin/article/377249',
showOnHome: true,
},
{
name: 'Mimi Tomo',
pos: '0% 35%',
image: 'mimi_tomo_update.jpg',
start: '2021-05-27 10:00:00',
end: '2021-06-06 04:00:00',
color: '#E5C18B',
zoom: '200%',
url: 'https://www.hoyolab.com/genshin/article/387817',
showOnHome: true,
},
{
name: 'Echoing Tales',
pos: '0% 15%',
image: 'echoing_tales.png',
start: '2021-06-09 11:00:00',
end: '2021-07-21 06:00:00',
color: '#90CEF5',
zoom: '230%',
url: 'https://www.hoyolab.com/genshin/article/415684',
showOnHome: true,
timezoneDependent: true,
},
{
name: 'Legend of the Vagabond Sword',
pos: '0% 45%',
image: 'legend_of_the_vagabond_sword.png',
start: '2021-06-25 10:00:00',
end: '2021-07-08 03:59:59',
color: '#7A92FF',
zoom: '180%',
showOnHome: true,
},
{
name: 'Kaboomball Kombat',
pos: '0% 15%',
image: 'kaboomball_kombat.png',
start: '2021-07-02 10:00:00',
end: '2021-07-12 04:00:00',
color: '#A8E4FC',
zoom: '150%',
url: 'https://www.hoyolab.com/genshin/article/503383',
showOnHome: true,
},
{
name: "Traveler's Picture Book Web Event",
pos: '50% 83%',
image: 'traveler_picture_book.jpg',
start: '2021-07-03 11:00:00',
end: '2021-07-13 03:59:59',
color: '#F3EFC9',
zoom: '150%',
url: 'https://www.hoyolab.com/genshin/article/512380',
showOnHome: true,
},
{
name: 'Never-Ending Battle',
pos: '0% 55%',
image: 'neverending_battle.png',
start: '2021-07-09 10:00:00',
end: '2021-07-19 04:00:00',
color: '#CCFFCB',
zoom: '160%',
url: 'https://twitter.com/GenshinImpact/status/1409001038758121478',
showOnHome: true,
},
{
name: 'Mysterious Voyage Web Event',
pos: '70% 50%',
image: 'mysterious_voyage.jpg',
start: '2021-07-13 04:00:00',
end: '2021-07-20 23:59:59',
color: '#FFBE7D',
zoom: '120%',
url: 'https://www.hoyolab.com/genshin/article/543290',
showOnHome: true,
},
{
name: 'Thunder Sojourn',
pos: '0% 25%',
image: 'thunder_sojourn.jpg',
start: '2021-07-22 10:00:00',
end: '2021-08-09 03:59:59',
color: '#F0DBFF',
zoom: '200%',
url: 'https://genshin.mihoyo.com/en/news/detail/14337',
showOnHome: true,
},
{
name: 'Summer Night Mementos',
pos: '0% 40%',
image: 'summer_night_mementos.jpg',
start: '2021-08-05 04:00:00',
end: '2021-08-11 23:59:59',
color: '#FFFEF9',
zoom: '200%',
url: 'https://mhy.link/071OHHA6',
showOnHome: true,
},
{
name: 'Lost Riches',
pos: '0% 50%',
image: 'lost_riches_2.jpg',
start: '2021-08-06 10:00:00',
end: '2021-08-16 03:59:59',
color: '#FFF2DC',
zoom: '170%',
url: 'https://genshin.mihoyo.com/en/news/detail/14337',
showOnHome: true,
},
{
name: 'Theater Mechanicus: Stage of Wonders',
pos: '0% 45%',
image: 'theater_mechanicus_wonders.jpg',
start: '2021-08-12 10:00:00',
end: '2021-08-26 03:59:59',
color: '#F8EAC4',
zoom: '200%',
url: 'https://www.hoyolab.com/genshin/article/661106',
showOnHome: true,
},
{
name: 'Phantom Flow',
pos: '0% 20%',
image: 'phantom_flow.jpg',
start: '2021-08-20 10:00:00',
end: '2021-08-30 03:59:59',
color: '#FFB6EB',
zoom: '100%',
url: 'https://www.hoyolab.com/genshin/article/661106',
showOnHome: true,
},
{
name: 'Ley Line Overflow',
pos: '20% 35%',
image: 'ley_line_overflow.jpg',
start: '2021-08-23 04:00:00',
end: '2021-08-30 03:59:59',
color: '#43DA8C',
zoom: '150%',
url: 'https://www.hoyolab.com/genshin/article/661106',
showOnHome: true,
},
{
name: 'Lunar Realm',
pos: '0% 50%',
image: 'lunar_realm.jpg',
start: '2021-09-10 10:00:00',
end: '2021-09-20 03:59:59',
color: '#3585FF',
zoom: '180%',
url: 'https://www.hoyolab.com/genshin/article/780932',
showOnHome: true,
},
{
name: 'Spectral Secrets',
pos: '0% 25%',
image: 'spectral_secrets.jpg',
start: '2021-09-19 10:00:00',
end: '2021-09-26 03:59:59',
color: '#F6F5F0',
zoom: '170%',
url: 'https://www.hoyolab.com/genshin/article/780934',
showOnHome: true,
},
{
name: 'Moonlight Merriment',
pos: '100% 15%',
image: 'moonlight_merriment.jpg',
start: '2021-09-27 10:00:00',
end: '2021-10-11 03:59:59',
color: '#FF7B69',
zoom: '150%',
url: 'https://www.hoyolab.com/genshin/article/780930',
showOnHome: true,
},
{
name: 'An Unforgettable Journey - Anniversary Web Event',
pos: '0% 55%',
image: 'an_unforgettable_journey.jpg',
start: '2021-09-28 04:00:00',
end: '2021-10-12 23:59:59',
color: '#5A8EBE',
zoom: '100%',
url: 'https://mhy.link/adfsJJA6',
showOnHome: true,
},
{
name: 'Passage of Clouds and Stars - Daily Login',
pos: '0% 40%',
image: 'passage_of_clouds_and_stars.jpg',
start: '2021-09-28 10:00:00',
end: '2021-10-11 03:59:59',
color: '#547BE4',
zoom: '200%',
url: 'https://www.hoyolab.com/article/780929',
showOnHome: true,
},
{
name: "Aloy's Exploration Journal",
pos: '0% 30%',
image: 'aloys_exploration_journal.jpg',
start: '2021-10-13 10:00:00',
end: '2021-10-27 23:59:59',
color: '#D3EEF9',
zoom: '200%',
url: 'https://www.hoyolab.com/article/1206113',
showOnHome: true,
},
{
name: "Tuned to the World's Sound",
pos: '0% 10%',
image: 'tuned_to_the_worlds_sounds.jpg',
start: '2021-10-15 10:00:00',
end: '2021-10-25 03:59:59',
color: '#81A6BC',
zoom: '300%',
url: 'https://www.hoyolab.com/article/1206131',
showOnHome: true,
},
{
name: 'Yummy! Barbecue Under the Stars',
pos: '0% 35%',
image: 'barbecue_under_the_stars.jpg',
start: '2021-11-01 10:00:00',
end: '2021-11-07 23:59:59',
color: '#D3EEF9',
zoom: '300%',
url: 'https://www.hoyolab.com/article/1327554',
showOnHome: true,
},
{
name: 'Shadow of the Ancients',
pos: '0% 50%',
image: 'shadow_of_the_ancients.jpg',
start: '2021-11-05 10:00:00',
end: '2021-11-15 03:59:59',
color: '#94D3FA',
zoom: '180%',
url: 'https://www.hoyolab.com/article/1347382',
showOnHome: true,
},
{
name: 'Dreams of Bloom',
pos: '0% 50%',
image: 'dreams_of_bloom.jpg',
start: '2021-11-12 10:00:00',
end: '2021-11-18 03:59:59',
color: '#FFDB7F',
zoom: '200%',
url: 'https://www.hoyolab.com/article/1321455',
showOnHome: true,
},
{
name: 'Shadows Amidst Snowstorms',
pos: '0% 20%',
zoom: '180%',
image: 'shadows_amidst_snowstorms.jpg',
start: '2021-11-25 11:00:00',
end: '2021-12-13 03:59:59',
color: '#dfe9f2',
url: 'https://www.hoyolab.com/article/1432656',
showOnHome: true,
},
{
name: "Traveler's Picture Book Web Event",
pos: '100% 50%',
image: 'travelers_picture_book.jpg',
start: '2021-12-03 11:00:00',
end: '2021-12-13 03:59:59',
color: '#DEECF7',
zoom: '150%',
url: 'https://www.hoyolab.com/article/1531728',
showOnHome: true,
},
{
name: 'Bantan Sango Case Files: The Warrior Dog',
pos: '0% 60%',
image: 'warrior_dog.jpg',
start: '2021-12-07 10:00:00',
end: '2021-12-20 03:59:59',
color: '#838CE7',
zoom: '200%',
url: 'https://www.hoyolab.com/article/1432656',
showOnHome: true,
},
{
name: 'Let the Arataki Gang Show Begin Web Event',
pos: '100% 40%',
image: 'let_the_arataki_gang_show_begin.jpg',
start: '2021-12-09 12:00:00',
end: '2021-12-13 23:59:59',
color: '#FDFB85',
zoom: '150%',
url: 'https://www.hoyolab.com/article/1586790',
showOnHome: true,
},
{
name: 'Misty Dungeon: Realm of Light',
pos: '0% 60%',
image: 'misty_dungeon.jpg',
start: '2021-12-17 10:00:00',
end: '2021-12-27 03:59:59',
color: '#8EC0B3',
zoom: '200%',
url: 'https://www.hoyolab.com/article/1653437',
showOnHome: true,
},
{
name: 'Energy Amplifier Fruition',
pos: '0% 50%',
image: 'energy_amplifier_fruition.jpg',
start: '2021-12-24 10:00:00',
end: '2022-01-03 03:59:59',
color: '#8FE3F0',
zoom: '200%',
url: 'https://www.hoyolab.com/article/1732200',
showOnHome: true,
},
{
name: 'The Crane in the Clouds Web Event',
pos: '100% 30%',
image: 'the_crane_in_the_clouds.jpg',
start: '2021-12-31 12:00:00',
end: '2022-01-04 23:59:59',
color: '#FBF5DB',
zoom: '150%',
url: 'https://www.hoyolab.com/article/1823311',
showOnHome: true,
},
{
name: 'A Study in Potions',
pos: '0% 50%',
image: 'a_study_in_potions.jpg',
start: '2022-01-07 10:00:00',
end: '2022-01-17 03:59:59',
color: '#9FE0D4',
zoom: '200%',
url: 'https://www.hoyolab.com/article/1823273',
showOnHome: true,
},
{
name: 'Windtrace',
pos: '0% 80%',
image: 'windtrace_2.jpg',
start: '2022-01-13 10:00:00',
end: '2022-01-27 03:59:59',
color: '#FFF6DF',
zoom: '170%',
url: 'https://www.hoyolab.com/article/1823273',
showOnHome: true,
},
{
name: 'May Fortune Find You - Daily Login',
pos: '0% 30%',
image: 'daily_login.jpg',
start: '2022-01-25 04:00:00',
end: '2022-02-09 03:59:59',
color: '#F18B53',
zoom: '200%',
url: 'https://www.hoyolab.com/article/1823190',
showOnHome: true,
},
{
name: 'Eight Locales Over Mountains and Seas',
pos: '0% 60%',
image: 'eight_locales_over_mountains_and_seas.jpg',
start: '2022-02-04 10:00:00',
end: '2022-02-14 03:59:59',
color: '#94D1D0',
zoom: '200%',
url: 'https://www.hoyolab.com/article/1823273',
showOnHome: true,
},
{
name: 'Flowing Lights and Colors',
pos: '0% 50%',
image: 'flowing_lights_and_colors.jpg',
start: '2022-02-09 00:00:00',
end: '2022-02-15 14:59:59',
color: '#BB71AD',
zoom: '200%',
url: 'https://www.hoyolab.com/article/1823191',
showOnHome: true,
},
{
name: 'Three Realms Gateway Offering',
pos: '0% 0%',
zoom: '100%',
image: 'three_realms_gateway_offering.jpg',
start: '2022-02-17 10:00:00',
end: '2022-03-30 06:00:00',
color: '#9c9cef',
url: 'https://www.hoyolab.com/article/3356118',
showOnHome: true,
},
{
name: 'Of Drink A-Dreaming',
pos: '0% 50%',
image: 'of_drink_a_dreaming.jpg',
start: '2022-03-10 10:00:00',
end: '2022-03-21 03:59:59',
color: '#e4d9c5',
zoom: '180%',
url: 'https://www.hoyolab.com/article/3714058',
showOnHome: true,
},
{
name: 'When Flowers Bloom',
pos: '0% 30%',
image: 'when_flowers_bloom.jpg',
start: '2022-03-25 12:00:00',
end: '2022-03-30 23:59:59',
color: '#8481da',
zoom: '170%',
url: 'https://www.hoyolab.com/article/3949309',
showOnHome: true,
},
{
name: 'Hues of the Violet Garden',
pos: '0% 15%',
zoom: '200%',
image: 'hues_of_the_violet_garden.jpg',
start: '2022-04-07 10:00:00',
end: '2022-04-25 03:59:59',
color: '#d98498',
url: 'https://www.hoyolab.com/article/3993068',
showOnHome: true,
},
{
name: 'Vibro-Crystal Research',
pos: '0% 25%',
image: 'vibro_crysta_research.jpg',
start: '2022-04-21 10:00:00',
end: '2022-05-05 03:59:59',
color: '#64a3f3',
zoom: '220%',
url: 'https://www.hoyolab.com/article/4245426',
showOnHome: true,
},
{
name: 'Marvelous Merchandise',
pos: '0% 80%',
image: 'marvelous_merchandise_event.jpg',
start: '2022-05-06 10:00:00',
end: '2022-05-13 03:59:59',
color: '#fad688',
zoom: '200%',
url: 'https://www.hoyolab.com/article/4245426',
showOnHome: true,
},
{
name: 'Spices From the West',
pos: '0% 40%',
zoom: '200%',
image: 'spices_from_the_west.jpg',
start: '2022-05-14 10:00:00',
end: '2022-06-07 03:59:59',
color: '#83bfb4',
url: 'https://www.hoyolab.com/article/4592260',
showOnHome: true,
},
{
name: 'Overflowing Mastery',
pos: '0% 50%',
image: 'overflowing_mastery.jpg',
start: '2022-05-22 04:00:00',
end: '2022-05-29 03:59:59',
color: '#f5debc',
zoom: '200%',
url: 'https://www.hoyolab.com/article/4592260',
showOnHome: true,
},
{
name: 'Perilous Trail',
pos: '0% 20%',
zoom: '150%',
image: 'perilous_trail.jpg',
start: '2022-05-31 06:00:00',
end: '2022-06-20 03:59:59',
color: '#c9bcff',
url: 'https://www.hoyolab.com/article/4998961',
showOnHome: true,
timezoneDependent: true,
},
{
name: 'The Almighty Arataki Great and Glorious Drumalong Festival',
pos: '0% 50%',
image: 'drumalong_festival.jpg',
start: '2022-06-13 10:00:00',
end: '2022-07-04 03:59:59',
color: '#ffca92',
zoom: '250%',
url: 'https://www.hoyolab.com/article/5350611',
showOnHome: true,
},
{
name: 'A Muddy Bizzare Adventure',
pos: '0% 50%',
image: 'a_muddy_bizzare_adventure.jpg',
start: '2022-06-22 10:00:00',
end: '2022-07-04 03:59:59',
color: '#8882e2',
zoom: '220%',
url: 'https://www.hoyolab.com/article/5513175',
showOnHome: true,
},
{
name: 'Core of the Apparatus',
pos: '50% 50%',
zoom: '150%',
image: 'core_of_the_apparatus.jpg',
start: '2022-06-29 10:00:00',
end: '2022-07-11 03:59:59',
color: '#fdf9f4',
url: 'https://www.hoyolab.com/article/5444663',
showOnHome: true,
},
{
name: 'Ley Line Overflow',
pos: '0% 20%',
image: 'leyline_overflow.jpg',
start: '2022-07-04 04:00:00',
end: '2022-07-11 03:59:59',
color: '#5baced',
zoom: '200%',
url: 'https://www.hoyolab.com/article/5444663',
showOnHome: true,
},
{
name: 'Summertime Odyssey',
pos: '40% 20%',
zoom: '200%',
image: 'summertime_odyssey.jpg',
start: '2022-07-15 10:00:00',
end: '2022-08-24 06:00:00',
color: '#7eebff',
url: 'https://www.hoyolab.com/article/5958494',
showOnHome: true,
},
{
name: 'Mesmerizing Dream at Sea',
pos: '20% 23%',
image: 'mesmerizing_dream_at_sea.jpg',
start: '2022-07-22 10:00:00',
end: '2022-07-31 23:59:59',
color: '#8cd8f1',
zoom: '300%',
url: 'https://www.hoyolab.com/article/6129641',
showOnHome: true,
},
{
name: 'Hidden Strife',
pos: '0% 30%',
image: 'hidden_strife.jpg',
start: '2022-07-27 10:00:00',
end: '2022-08-15 03:59:59',
color: '#ff7c84',
zoom: '220%',
url: 'https://www.hoyolab.com/article/6171423',
showOnHome: true,
},
{
name: 'Reminiscent Regimen',
pos: '0% 35%',
image: 'reminiscent_regimen.jpg',
start: '2022-08-04 10:00:00',
end: '2022-08-15 03:59:59',
color: '#82cff9',
zoom: '250%',
url: 'https://www.hoyolab.com/article/6533766',
showOnHome: true,
},
{
name: 'Evermotion Mechanical Painting',
pos: '0% 30%',
image: 'evermotion_mechanical_painting.jpg',
start: '2022-08-12 10:00:00',
end: '2022-08-22 03:59:59',
color: '#ffc284',
zoom: '100%',
url: 'https://www.hoyolab.com/article/6197391',
showOnHome: true,
},
];

View File

@ -143,13 +143,13 @@ export const eventsData = [
},
{
name: 'Evermotion Mechanical Painting',
pos: '0% 30%',
pos: '0% 50%',
image: 'evermotion_mechanical_painting.jpg',
start: '2022-08-12 10:00:00',
end: '2022-08-22 03:59:59',
color: '#ffc284',
zoom: '100%',
url: 'https://www.hoyolab.com/article/6197391',
zoom: '300%',
url: 'https://www.hoyolab.com/article/6857289',
showOnHome: true,
},
],

View File

@ -15,6 +15,7 @@
"artifacts": "Artifacts",
"fishing": "Fishing",
"radiantSpincrystal": "Radiant Spincrystal",
"calendar": "Calendar",
"settings": "Settings",
"donate": "Donate"
},
@ -140,10 +141,7 @@
"manualButton": "Enable Manual Input",
"errorBanner": "Banner time mismatch! Please adjust your server on the settings page. Still not working? Please leave a message on Discord 😅",
"globalWishTally": "Global Wish Stats",
"pityTooltip": [
"Shows your current {rarity} pity",
"{count} pulls to guaranteed {rarity}"
],
"pityTooltip": ["Shows your current {rarity} pity", "{count} pulls to guaranteed {rarity}"],
"import": {
"title": "Import Wish History",
"faqsButton": "FAQ - READ FIRST",
@ -179,11 +177,7 @@
"server": "Select your server:",
"wishTallyCheck": "Submit pity for global wish stats",
"wishTally": "We are doing a global wish stats! You can submit your wish stats to participate. All pity data will be aggregated to know what is the average pity of paimon.moe users.",
"wishTallyCollected": [
"What will be collected:",
"and",
"pity from your wish history"
],
"wishTallyCollected": ["What will be collected:", "and", "pity from your wish history"],
"forceUpdateCheck": "Force update wish history (enable only if your wish history is not updating)",
"header": [
"Import and backup your Genshin Impact wish history to keep it for more than 6 months. It also automatically tracks your pity and statistics about your wishes!",
@ -361,11 +355,7 @@
"exportFinish": "Export success, please wait until your browser downloads the file!",
"wishTallyTitle": "Submit Wish Stats",
"wishTally": "We are doing a global wish stats! You can submit your wish stats to participate. All pity data will be aggregated to know what is the average pity of paimon.moe users.",
"wishTallyCollected": [
"What will be collected:",
"and",
"pity from your wish history"
],
"wishTallyCollected": ["What will be collected:", "and", "pity from your wish history"],
"wishTallySubmit": "Submit Wish Stats",
"wishTallyThankyou": "Thank you for participating!",
"manualTitle": "Manual Input Settings",
@ -377,22 +367,13 @@
"subtitle": "After a 1x Wish:",
"pressWhenYouGet": "Press {button} when you get {rarity}★",
"p1": "It will automatically add the lifetime pulls, 5★, and 4★ pity",
"p2": [
"When the",
"pity reaches 10, it will automatically be reset to 0"
],
"p3": [
"When the",
"pity reaches 90, it will automatically be reset to 0"
],
"p2": ["When the", "pity reaches 10, it will automatically be reset to 0"],
"p3": ["When the", "pity reaches 90, it will automatically be reset to 0"],
"p4": [
"After a 10x Wish, press",
"but keep in mind that the pity counter might not be accurate, because there is no way to tell when the drop occured (maybe you got it on the 1st or even the 10th pull). To ensure that the counter is still accurate, you need to check the history table and add it one-by-one like you do 1x Wishes."
],
"p5": [
"You can also press the",
"button to edit the values manually!"
],
"p5": ["You can also press the", "button to edit the values manually!"],
"p6": "Press the arrow on the bottom to see your pulls' details. A popup will show up when you get a 5★ or 4★. You can also add or edit the table manually."
}
},
@ -532,11 +513,7 @@
"calculateTalent": "Calculate Talent Material?",
"inputTalentLevel": "Input the 1st, 2nd & 3rd current talent level",
"inputTalentNotice": "If it has a different color, subtract it by 3",
"inputTalent": [
"1st talent lvl",
"2nd talent lvl",
"3rd talent lvl"
],
"inputTalent": ["1st talent lvl", "2nd talent lvl", "3rd talent lvl"],
"talentToLevel": "to level",
"calculate": "Calculate",
"unknownInformation": "There are some unknown information",
@ -545,11 +522,7 @@
"expWasted": "EXP Wasted",
"addToTodo": "Add to Todo List",
"addedToTodo": "Added to Todo List",
"talent": [
"Attack",
"Skill",
"Burst"
]
"talent": ["Attack", "Skill", "Burst"]
},
"expTable": {
"level": "Level",
@ -641,10 +614,7 @@
"todo": {
"title": "Todo List",
"summary": "Summary",
"empty": [
"Nothing to do yet 😀",
"Add some from the Items page or the Calculator!"
],
"empty": ["Nothing to do yet 😀", "Add some from the Items page or the Calculator!"],
"farmableToday": "Farmable Today",
"resin": "Resin needed",
"based": "Based on AR:{ar} and WL:{wl}",
@ -966,5 +936,11 @@
"whatsNew": "What's new",
"later": "Later",
"refresh": "Refresh"
},
"calendar": {
"lastAppearance": "Last Banner Appearance",
"lastAppearanceDesc": "When is the character's last appearance on a limited banner",
"bannerHover": "X Banner Ago",
"birthday": "Birthday"
}
}

View File

@ -0,0 +1,28 @@
<script>
import { t } from 'svelte-i18n';
export let char;
export let time;
</script>
<div>
<div class="mb-4 gap-4">
{#each char as c}
<div class="h-96 inline-block">
<img
src="/images/{c.id === 'paimon_hello' ? '' : 'characters/full/'}{c.id}.png"
class="h-full w-auto"
alt={c.name}
/>
</div>
{/each}
</div>
{#each char as c}
<h1 class="text-white font-display font-semibold text-xl">{$t(c.name)} {$t('calendar.birthday')}</h1>
{/each}
<p class="text-gray-400 font-body flex flex-col md:flex-row">
<span class="flex">
<span>{time.format('D MMMM')}</span>
</span>
</p>
</div>

View File

@ -4,12 +4,28 @@
import dayjs from 'dayjs';
import { onMount } from 'svelte';
import { bannersDual } from '../../data/bannersDual';
export let type;
export let event;
export let timeDifference;
let start = dayjs(event.start);
let end = dayjs(event.end);
let now = dayjs().add(timeDifference, 'minute');
let image = type === 'banners' ? `${event.name} ${event.image}.png` : event.image;
let banner2 = null;
onMount(() => {
console.log(bannersDual[`${event.name} ${event.image}`]);
if (type === 'banners') {
if (bannersDual[`${event.name} ${event.image}`] !== undefined) {
banner2 = bannersDual[`${event.name} ${event.image}`][1];
}
}
const interval = setInterval(() => {
now = dayjs().add(timeDifference, 'minute');
}, 1000);
@ -21,22 +37,28 @@
$: started = now.isAfter(event.start);
$: ended = now.isAfter(event.end);
$: diffStart = event.start.diff(now);
$: diffEnd = event.end.diff(now);
$: diffStart = start.diff(now);
$: diffEnd = end.diff(now);
</script>
<div>
{#if event.image}
<img src="/images/events/{event.image}" class="w-full rounded-lg mb-4" alt={event.name} />
<img src="/images/{type}/{image}" class="w-full rounded-lg mb-4" alt={event.name} />
{/if}
{#if banner2}
<img src="/images/banners/{banner2.name} {banner2.image}.png" class="w-full rounded-lg mb-4" alt={banner2.name} />
{/if}
<h1 class="text-white font-display font-semibold text-xl">{event.name}</h1>
{#if banner2}
<h1 class="text-white font-display font-semibold text-xl mb-2">{banner2.name}</h1>
{/if}
<p class="text-gray-400 font-body flex flex-col md:flex-row">
<span class="flex">
<span>{event.start.format('ddd, D MMM YYYY HH:mm')}</span>
<span>{start.format('ddd, D MMM YYYY HH:mm')}</span>
{#if !event.startOnly}<span class="mx-2">-</span>{/if}
</span>
{#if !event.startOnly}
<span>{event.end.format('ddd, D MMM YYYY HH:mm')}</span>
<span>{end.format('ddd, D MMM YYYY HH:mm')}</span>
{/if}
</p>
{#if event.url}

View File

@ -0,0 +1,273 @@
<script context="module">
import { banners } from '../../data/banners';
import { characters } from '../../data/characters';
import { birthdays } from '../../data/birthdays';
</script>
<script>
import { onMount } from 'svelte';
import dayjs from 'dayjs';
import duration from 'dayjs/plugin/duration';
import relativeTime from 'dayjs/plugin/relativeTime';
import localeData from 'dayjs/plugin/localeData';
dayjs.extend(duration);
dayjs.extend(relativeTime);
dayjs.extend(localeData);
let firstDay = 0;
let selectedMonth = 0;
let selectedYear = 2022;
let monthName = '';
let weekNames = [];
let startingDate;
let month = [];
let weeks = [];
const birthdayDates = {};
const bannerCombined = [...banners.characters];
const bannerDates = {};
function processGrid() {
const now = dayjs().year(selectedYear).month(selectedMonth).hour(0).minute(0).second(0);
monthName = now.format('MMMM');
const first = now.date(1);
const prev = now.date(1).subtract(1, 'day');
const prevDate = prev.date();
firstDay = first.day();
month = [];
weeks = [];
startingDate = prevDate - firstDay;
let date = prev.date(startingDate);
// const leftover = [[], [], [], [], [], [], []];
for (let i = 0; i < 6; i++) {
const week = [];
let day = [
[0, -1],
[0, -1],
[0, -1],
[0, -1],
[0, -1],
[0, -1],
[0, -1],
];
let stack = [[], [], [], [], [], []];
let fill = [0, 0, 0, 0, 0];
for (let j = 0; j < 7; j++) {
date = date.add(1, 'day');
const current = date.month() === selectedMonth;
const dateNumber = date.date();
const banner = bannerDates?.[date.year()]?.[date.month()]?.[dateNumber];
if (banner) {
const cur = day[j][1] >= day[j][0] ? day[j][1] - 1 : day[j][0];
const end = Math.ceil(dayjs(bannerCombined[banner].end).diff(date, 'day', true));
const used = j - fill[cur];
console.log('BANNER', dateNumber, j, cur, used, JSON.stringify(day));
if (used > 0) {
stack[cur].push({ l: used });
fill[cur] += used;
}
const length = Math.min(7 - j, end);
const left = end - length;
// console.log('END BANNER', end, length, left);
if (left > 0) {
const nextWeek = date.add(1, 'week').day(0);
// leftover[j + 1].push(bannerCombined[banner])
bannerDates[selectedYear][nextWeek.month()][nextWeek.date()] = banner;
}
stack[cur].push({ l: length, t: 'w', d: bannerCombined[banner] });
fill[cur] += length;
for (let k = j; k < j + length; k++) {
day[k][0] += 1;
if (day[k][1] === -1) day[k][1] = cur;
}
}
const birthday = birthdayDates?.[date.month() + 1]?.[dateNumber];
if (birthday) {
const cur = day[j][1] >= day[j][0] ? day[j][1] - 1 : day[j][0];
// console.log('CUR BIRTH', cur);
const used = j - fill[cur];
console.log('CHAR', dateNumber, j, cur, used, JSON.stringify(day));
if (used > 0) {
stack[cur].push({ l: used });
fill[cur] += used;
}
stack[cur].push({ l: 1, t: 'b', d: birthday });
fill[cur] += 1;
day[j][0] += 1;
if (day[j][1] === -1) day[j][1] = cur;
}
week.push({
d: dateNumber,
c: current,
});
}
// console.log(day, stack);
weeks.push(stack);
month.push(week);
}
// console.log(weeks);
month = month;
weeks = weeks;
}
function processWeek() {
const first = now.date(1);
}
function changeMonth(val) {
selectedMonth = selectedMonth + val;
if (selectedMonth > 11) {
selectedMonth = 0;
selectedYear = selectedYear + 1;
} else if (selectedMonth < 0) {
selectedMonth = 11;
selectedYear = selectedYear - 1;
}
processGrid();
}
function processBirthday() {
for (const [char, date] of Object.entries(birthdays)) {
const [month, day] = date;
if (birthdayDates[month] === undefined) {
birthdayDates[month] = {};
}
if (birthdayDates[month][day] === undefined) {
birthdayDates[month][day] = [];
}
birthdayDates[month][day].push(char);
}
console.log(birthdayDates);
}
function processBanner() {
for (let i = 0; i < bannerCombined.length; i++) {
const banner = bannerCombined[i];
const start = dayjs(banner.start);
const end = dayjs(banner.end);
const startYear = start.year();
const startMonth = start.month();
const startDate = start.date();
const endYear = end.month();
const endMonth = end.month();
const endDate = end.date();
// console.log(banner.name, startYear, startMonth, startDate);
if (bannerDates[startYear] === undefined) bannerDates[startYear] = {};
if (bannerDates[startYear][startMonth] === undefined) bannerDates[startYear][startMonth] = {};
if (bannerDates[startYear][startMonth][startDate] === undefined)
bannerDates[startYear][startMonth][startDate] = {};
// if (bannerDates[endMonth] === undefined) bannerDates[endMonth] = [];
// if (bannerDates[endMonth][endDate] === undefined) bannerDates[endMonth][endDate] = [];
bannerDates[startYear][startMonth][startDate] = i;
// bannerDates[startMonth][endDate] = i;
}
console.log(bannerDates);
}
onMount(() => {
processBirthday();
processBanner();
weekNames = dayjs.weekdaysShort();
const now = dayjs();
selectedMonth = now.month();
selectedYear = now.year();
processGrid();
});
</script>
<div class="lg:ml-64 pt-20 lg:pt-8 lg:px-8 max-w-screen-xl">
<div>
<button on:click={() => changeMonth(-1)}>PREV</button>
<span style="display: inline-block; width: 150px; text-align: center; margin-bottom: 10px;">
{monthName}
{selectedYear}
</span>
<button on:click={() => changeMonth(1)}>NEXT</button>
</div>
<table class="border-separate border-spacing-x-1 table-fixed w-full">
<tbody>
<tr>
{#each weekNames as day}
<td class="text-white font-bold bg-background rounded-xl py-2 text-center">
{day}
</td>
{/each}
</tr>
</tbody>
</table>
{#each month as week, i}
<div class="w-full mt-1 relative h-28">
<table class="table-fixed w-full border-separate border-spacing-x-1 absolute z-0">
<tr>
{#each week as day}
<td class="bg-background rounded-xl h-28" />
{/each}
</tr>
</table>
<table class="table-fixed w-full border-separate border-spacing-x-1 absolute z-10">
<tr>
{#each week as day}
<td class="leading-tight pl-1 {day.c ? 'text-white' : 'text-gray-700'}">{day.d}</td>
{/each}
</tr>
{#each weeks[i] as stack}
<tr>
{#each stack as w}
{#if w.t === 'b'}
<td colspan={w.l}>
<p class="rounded-md px-2 bg-purple-400">
{#each w.d as char}
<img src="/images/characters/{char}.png" alt={char} class="w-6 h-6 mr-1 align-bottom inline" />
{/each}
<span class="leading-none text-sm">Birthday</span>
</p>
</td>
{:else if w.t === 'w'}
<td colspan={w.l}>
<p class="rounded-md px-2" style="background-color: {w.d.color}">
<span class="leading-none text-sm">{w.d.name}</span>
</p>
</td>
{:else}
<td colspan={w.l} />
{/if}
{/each}
</tr>
{/each}
</table>
</div>
{/each}
</div>

View File

@ -0,0 +1,520 @@
<script context="module">
import { banners } from '../../data/banners';
import { characters } from '../../data/characters';
import { birthdays } from '../../data/birthdays';
import { events } from '../../data/events';
</script>
<script>
import { getContext, onMount } from 'svelte';
import dayjs from 'dayjs';
import { t } from 'svelte-i18n';
import { mdiChevronLeft, mdiChevronRight } from '@mdi/js';
import duration from 'dayjs/plugin/duration';
import relativeTime from 'dayjs/plugin/relativeTime';
import localeData from 'dayjs/plugin/localeData';
dayjs.extend(duration);
dayjs.extend(relativeTime);
dayjs.extend(localeData);
import { getTimeDifference, getTimeDifferenceAsia, getTimeOffset, server } from '../../stores/server';
import Icon from '../../components/Icon.svelte';
import Tooltip from '../../components/Tooltip.svelte';
import DetailModal from './_detail.svelte';
import BirthdayModal from './_birthday.svelte';
import { getAccountPrefix } from '../../stores/account';
import { readSave } from '../../stores/saveManager';
const { open: openModal } = getContext('simple-modal');
let browserTimeZone = '';
let timeDifference = 0;
let timeDifferenceEvent = 0;
let timeDifferenceAsia = 0;
const today = dayjs();
let selectedMonth = 0;
let selectedYear = 2022;
let hightligtedDate = dayjs().year(2000);
let month = [];
let monthName = '';
let weekNames = [];
const birthdayDates = {};
const lastLegendary = {};
const lastRare = {};
const lastBannerDate = {};
const lastBannerStart = {};
const bannerDates = {
characters: {},
weapons: {},
};
const eventDates = {};
let hovered = '';
let sortedLegendary = [];
let sortedRare = [];
let ignoredLegendary = [
'traveler_anemo',
'traveler_geo',
'traveler_electro',
'aloy',
'diluc',
'keqing',
'qiqi',
'jean',
'mona',
];
let ignoredRare = ['amber', 'kaeya', 'lisa'];
function process() {
const now = dayjs().year(selectedYear).month(selectedMonth);
monthName = now.format('MMMM');
const first = now.date(1);
const prev = now.date(1).subtract(1, 'day');
const prevDate = prev.date();
const firstDay = first.day();
month = [];
let startDate = prevDate - firstDay;
let date = prev.date(startDate);
for (let i = 0; i < 6; i++) {
const week = [];
for (let j = 0; j < 7; j++) {
date = date.add(1, 'day');
const _today = date.isSame(today, 'day');
const dateNumber = date.date();
const monthNumber = date.month();
const current = monthNumber === selectedMonth;
const birthday = birthdayDates?.[date.month() + 1]?.[dateNumber];
const bannerChar = bannerDates.characters?.[date.year()]?.[date.month()]?.[dateNumber];
const bannerWeap = bannerDates.weapons?.[date.year()]?.[date.month()]?.[dateNumber];
const event = eventDates?.[date.year()]?.[date.month()]?.[dateNumber];
const highlited = date.isSame(hightligtedDate, 'day');
week.push({
c: _today ? 'text-primary' : current ? 'text-white' : 'text-gray-700',
t: _today,
h: highlited,
d: dateNumber,
m: monthNumber,
b: birthday,
wc: bannerChar,
wp: bannerWeap,
e: event,
});
}
month.push(week);
}
month = month;
}
function changeMonth(val) {
selectedMonth = selectedMonth + val;
if (selectedMonth > 11) {
selectedMonth = 0;
selectedYear = selectedYear + 1;
} else if (selectedMonth < 0) {
selectedMonth = 11;
selectedYear = selectedYear - 1;
}
hightligtedDate = dayjs().year(2000);
process();
}
function processChar() {
for (const [char, data] of Object.entries(characters)) {
if (data.rarity === 5) lastLegendary[char] = 0;
else lastRare[char] = 0;
}
for (const banner of banners.characters) {
for (const char of Object.keys(lastLegendary)) {
lastLegendary[char]++;
}
for (const char of Object.keys(lastRare)) {
lastRare[char]++;
}
for (const char of banner.featured) {
lastLegendary[char] = 0;
lastBannerDate[char] = dayjs.duration(dayjs(banner.end).diff(dayjs()));
lastBannerStart[char] = banner;
}
for (const char of banner.featuredRare) {
lastRare[char] = 0;
lastBannerDate[char] = dayjs.duration(dayjs(banner.end).diff(dayjs()));
lastBannerStart[char] = banner;
}
}
for (const c of ignoredLegendary) {
delete lastLegendary[c];
}
for (const c of ignoredRare) {
delete lastRare[c];
}
sortedLegendary = Object.entries(lastLegendary).sort((a, b) => b[1] - a[1]);
sortedRare = Object.entries(lastRare).sort((a, b) => b[1] - a[1]);
}
function processBirthday() {
for (const [char, date] of Object.entries(birthdays)) {
const [month, day] = date;
if (birthdayDates[month] === undefined) {
birthdayDates[month] = {};
}
if (birthdayDates[month][day] === undefined) {
birthdayDates[month][day] = [];
}
birthdayDates[month][day].push(char);
}
}
function processBanner(type) {
for (let i = 0; i < banners[type].length; i++) {
const banner = banners[type][i];
const start = dayjs(banner.start).subtract(
banner.timezoneDependent ? timeDifferenceAsia : timeDifferenceEvent,
'minute',
);
const year = start.year();
const month = start.month();
const date = start.date();
if (bannerDates[type][year] === undefined) bannerDates[type][year] = {};
if (bannerDates[type][year][month] === undefined) bannerDates[type][year][month] = {};
bannerDates[type][year][month][date] = banner;
}
}
function processEvent() {
for (let i = 0; i < events.length; i++) {
const event = events[i];
const start = dayjs(event.start).subtract(
event.timezoneDependent ? timeDifferenceAsia : timeDifferenceEvent,
'minute',
);
const end = dayjs(event.end).subtract(timeDifferenceEvent, 'minute');
const year = start.year();
const month = start.month();
const date = start.date();
const yearEnd = end.year();
const monthEnd = end.month();
const dateEnd = end.date();
if (eventDates[year] === undefined) eventDates[year] = {};
if (eventDates[year][month] === undefined) eventDates[year][month] = {};
if (eventDates[year][month][date] === undefined) eventDates[year][month][date] = [];
if (eventDates[yearEnd] === undefined) eventDates[yearEnd] = {};
if (eventDates[yearEnd][monthEnd] === undefined) eventDates[yearEnd][monthEnd] = {};
if (eventDates[yearEnd][monthEnd][dateEnd] === undefined) eventDates[yearEnd][monthEnd][dateEnd] = [];
eventDates[year][month][date].push({ e: event, s: true });
eventDates[yearEnd][monthEnd][dateEnd].push({ e: event, s: false });
}
}
function hoverEvent(name) {
hovered = name;
}
function goToBannerTime(banner) {
const date = dayjs(banner.start).subtract(
banner.timezoneDependent ? timeDifferenceAsia : timeDifferenceEvent,
'minute',
);
selectedYear = date.year();
selectedMonth = date.month();
hightligtedDate = date;
process();
window.scrollTo(0, 0);
}
function openDetail(type, event) {
openModal(
DetailModal,
{
type,
event,
timeDifference,
},
{
closeButton: false,
styleWindow: { background: '#25294A', width: '600px' },
},
);
}
function openBirthday(char, date, month) {
openModal(
BirthdayModal,
{
char: char.map((e) => (e === 'paimon' ? { id: 'paimon_hello', name: 'Paimon' } : characters[e])),
time: dayjs().month(month).date(date),
},
{
closeButton: false,
styleWindow: { background: '#25294A', width: '600px' },
},
);
}
onMount(async () => {
const prefix = getAccountPrefix();
const serverSave = await readSave(`${prefix}server`);
if (serverSave !== null) {
console.log(serverSave);
server.set(serverSave);
}
const diff = getTimeDifference();
timeDifferenceEvent = diff;
timeDifference = 0;
timeDifferenceAsia = getTimeDifferenceAsia();
browserTimeZone = dayjs.tz.guess();
weekNames = dayjs.weekdaysShort();
const now = dayjs();
selectedMonth = now.month();
selectedYear = now.year();
processBirthday();
processBanner('characters');
processBanner('weapons');
processEvent();
processChar();
process();
});
</script>
<div class="lg:ml-64 pt-20 lg:pt-8 max-w-screen-xl">
<div class="px-4 2xl:px-8 mb-4 flex flex-col">
<div class="mb-1 flex gap-1">
<button
class="ring-primary hover:ring-2 bg-background rounded-xl px-3 text-white"
on:click={() => changeMonth(-1)}
>
<Icon path={mdiChevronLeft} />
</button>
<button
class="ring-primary hover:ring-2 bg-background rounded-xl px-3 text-white"
on:click={() => changeMonth(1)}
>
<Icon path={mdiChevronRight} />
</button>
<div class="bg-background rounded-xl h-10 font-bold flex w-full">
<p class=" font-normal text-sm text-gray-600 rounded-xl leading-10 px-8">
{browserTimeZone}
</p>
<p class="leading-10 px-2 text-white flex-1 text-center text-xl">
{monthName}
{selectedYear}
</p>
<p class="font-normal text-sm text-gray-600 rounded-xl leading-10 px-8">
{$server} Server
</p>
</div>
<button
class="ring-primary hover:ring-2 bg-background rounded-xl px-3 text-white"
on:click={() => changeMonth(-1)}
>
<Icon path={mdiChevronLeft} />
</button>
<button
class="ring-primary hover:ring-2 bg-background rounded-xl px-3 text-white"
on:click={() => changeMonth(1)}
>
<Icon path={mdiChevronRight} />
</button>
</div>
<div class="grid grid-cols-7 gap-x-1">
{#each weekNames as day}
<div class="text-white font-bold bg-background rounded-xl text-center h-10 leading-10">
{day}
</div>
{/each}
</div>
{#each month as week}
<div class="grid grid-cols-7 gap-x-1 pt-1">
{#each week as day}
<div
class="scrollbar overflow-y-auto bg-background rounded-xl h-28 relative flex flex-col {day.t
? 'ring-2 ring-primary'
: ''} {day.h ? 'ring-2 ring-green-400' : ''}"
>
<p class="select-none pl-2 {day.c}">{day.d}</p>
<div class="flex flex-wrap gap-1 px-2 pb-1">
{#if day.wc}
<div
on:click={() => openDetail('banners', day.wc)}
style="--tw-ring-color: {day.wc.color}; background-color: {day.wc.color};"
class="cursor-pointer rounded-md inline-flex items-center text-sm px-2 space-x-1 ring-offset-1 ring-offset-background hover:ring-2"
>
<!-- {day.w.name} -->
{#each day.wc.featured as char}
<img class="w-6 h-6 inline" src="/images/characters/{char}.png" alt={char} />
{/each}
{#each day.wc.featuredRare as char}
<img class="w-6 h-6 inline" src="/images/characters/{char}.png" alt={char} />
{/each}
<!-- <img class="w-6 h-6 inline" src="/images/intertwined_fate.png" alt="fate" /> -->
</div>
{/if}
{#if day.wp}
<div
on:click={() => openDetail('banners', day.wp)}
class="cursor-pointer bg-orange-200 rounded-md inline-flex items-center text-sm px-2 ring-orange-200 ring-offset-1 ring-offset-background hover:ring-2"
>
<!-- {day.w.name} -->
{#each day.wp.featured as weapon}
<img class="w-6 h-6 inline" src="/images/weapons/{weapon}.png" alt={weapon} />
{/each}
<!-- <img class="w-6 h-6 inline" src="/images/intertwined_fate.png" alt="fate" /> -->
</div>
{/if}
{#if day.b}
<div
on:click={() => openBirthday(day.b, day.d, day.m)}
class="cursor-pointer bg-purple-400 rounded-md inline-flex items-center px-2 ring-purple-400 ring-offset-1 ring-offset-background hover:ring-2"
>
{#each day.b as char}
<img class="w-6 h-6 inline mr-2" src="/images/characters/{char}.png" alt={char} />
{/each}
<span class="text-sm">🎁</span>
</div>
{/if}
{#if day.e}
{#each day.e as event}
<div
on:mouseenter={() => hoverEvent(event.e.name)}
on:mouseleave={() => hoverEvent('')}
on:click={() => openDetail('events', event.e)}
style="--tw-ring-color: {event.e.color}; --color: {event.e.color};"
class="cursor-pointer rounded-md ring-offset-1 ring-offset-background h-6 w-full inline-flex items-center text-xs px-2 space-x-1 event-strip {event.s
? ''
: 'end'} {hovered === event.e.name ? 'ring-2' : ''}"
>
<p class="max-h-full w-full overflow-hidden text-ellipsis whitespace-nowrap">{event.e.name}</p>
</div>
{/each}
{/if}
</div>
</div>
{/each}
</div>
{/each}
</div>
<h1 class="font-display px-4 md:px-8 font-black text-3xl text-white mt-8">{$t('calendar.lastAppearance')}</h1>
<p class="text-gray-400 px-4 md:px-8 font-medium pb-4 -mt-2">
{$t('calendar.lastAppearanceDesc')}
</p>
<div class="flex gap-4 px-8">
<div class="p-4 bg-background rounded-xl">
<table class="text-white">
<tr>
<td class="text-white font-semibold py-1" />
<td class="text-white font-semibold py-1 px-4">Name</td>
<td class="text-white font-semibold py-1 px-4 text-center">
<Tooltip title={$t('calendar.bannerHover')}>Banner</Tooltip>
</td>
<td class="text-white font-semibold py-1">Time</td>
</tr>
{#each sortedLegendary as [char, val]}
<tr on:click={() => goToBannerTime(lastBannerStart[char])}>
<td class="cursor-pointer text-white py-1 border-t border-gray-700">
<img class="w-8 h-8" src="/images/characters/{char}.png" alt={char} />
</td>
<td class="cursor-pointer text-white px-4 py-1 border-t border-gray-700">{characters[char].name}</td>
<td class="cursor-pointer text-white px-4 py-1 border-t border-gray-700 text-center">{val}</td>
<td class="cursor-pointer text-white py-1 border-t border-gray-700">
{lastBannerDate[char].humanize(true)}
</td>
</tr>
{/each}
</table>
</div>
<div class="p-4 bg-background rounded-xl">
<table class="text-white">
<tr>
<td class="text-white font-semibold py-1" />
<td class="text-white font-semibold py-1 px-4">Name</td>
<td class="text-white font-semibold py-1 px-4 text-center">
<Tooltip title={$t('calendar.bannerHover')}>Banner</Tooltip>
</td>
<td class="text-white font-semibold py-1">Time</td>
</tr>
{#each sortedRare as [char, val]}
<tr on:click={() => goToBannerTime(lastBannerStart[char])}>
<td class="cursor-pointer text-white py-1 border-t border-gray-700">
<img class="w-8 h-8" src="/images/characters/{char}.png" alt={char} />
</td>
<td class="cursor-pointer text-white px-4 py-1 border-t border-gray-700">{characters[char].name}</td>
<td class="cursor-pointer text-white px-4 py-1 border-t border-gray-700 text-center">{val}</td>
<td class="cursor-pointer text-white py-1 border-t border-gray-700">
{lastBannerDate[char].humanize(true)}
</td>
</tr>
{/each}
</table>
</div>
</div>
</div>
<style lang="postcss">
.event-strip {
background: var(--color);
background: linear-gradient(90deg, var(--color) 0%, var(--color) 80%, rgba(0, 0, 0, 0) 100%);
&.end {
background: linear-gradient(-90deg, var(--color) 0%, var(--color) 80%, rgba(0, 0, 0, 0) 100%);
direction: rtl;
}
}
.scrollbar {
scrollbar-color: transparent rgba(0, 0, 0, 0.35);
}
.scrollbar::-webkit-scrollbar {
width: 10px;
height: 10px;
}
.scrollbar::-webkit-scrollbar-thumb {
background: rgba(0, 0, 0, 0.35);
@apply rounded-xl;
}
.scrollbar::-webkit-scrollbar-track {
background: transparent;
}
</style>

View File

@ -13,7 +13,7 @@
import Checkbox from '../../components/Checkbox.svelte';
import EventItem from './_item.svelte';
import DetailModal from './_detail.svelte';
import DetailModal from '../calendar/_detail.svelte';
import { getAccountPrefix } from '../../stores/account';
import { readSave } from '../../stores/saveManager';
import Ad from '../../components/Ad.svelte';

View File

@ -7,7 +7,7 @@ const IMAGE_CACHE = `cacheimg${IMAGE_CACHE_VER}`;
const IMAGE_URL = `${self.location.origin}/images/`;
const changelog = ['Update timeline', 'Fix item list on safari', 'Fix summit shaper materials'];
const changelog = ['Added calendar (database > calendar)', 'Minor fixes'];
const channel = new BroadcastChannel('paimonmoe-sw');

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

After

Width:  |  Height:  |  Size: 75 KiB