Skip to content

Instantly share code, notes, and snippets.

@motsu0
Last active February 1, 2023 09:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save motsu0/810edcdf680bba0d5f0ca40549451b2e to your computer and use it in GitHub Desktop.
Save motsu0/810edcdf680bba0d5f0ca40549451b2e to your computer and use it in GitHub Desktop.
#message{
color: #e00000;
}
/* */
.app__menu{
display: flex;
column-gap: 8px;
}
.app__menu__item{
flex-grow: 1;
background-color: #ddd;
text-align: center;
cursor: pointer;
}
.app__menu__item.on{
background-color: #2ECC71;
color: #fff;
}
.app__body{
/* padding: 12px; */
border: 2px solid #2ECC71;
}
/* */
.app__block{
padding: 12px;
}
.copipe{
padding: 8px;
border-radius: 4px;
background-color: #fff;
filter: drop-shadow(0px 0px 2px #777);
}
.copipe:nth-child(n+2){
margin-top: 20px;
}
.copipe__area-bt{
margin: 8px 0;
}
.copipe__head{
display: flex;
column-gap: 8px;
}
.copipe__name{
flex-grow: 1;
}
.bt-arrow{
width: 20px;
cursor: pointer;
}
.copipe__area-bt{
display: flex;
column-gap: 16px;
}
.bt-copy{
flex-grow: 1;
padding: 8px 0;
border: none;
background-color: #3498DB;
color: #fff;
}
.bt-copy:hover{
background-color: #2980B9;
}
.bt-copy__icon{
width: 20px;
margin-right: 4px;
vertical-align: middle;
}
.bt-del{
border: none;
background-color: #fff;
color: #E74C3C;
}
.bt-del:hover{
text-decoration: underline;
font-weight: bold;
}
.bt-control,.copipe__summary{
cursor: pointer;
}
.copipe__text{
box-sizing: border-box;
width: 100%;
height: 100px;
padding: 4px;
resize: vertical;
background-color: #eee;
font-size: .75rem;
}
/* */
#input-name-new,#textarea-new{
box-sizing: border-box;
display: block;
width: 100%;
}
#textarea-new{
margin: 16px 0;
height: 200px;
resize: vertical;
}
#bt-add{
padding: 4px 12px;
cursor: pointer;
}
/* */
.area-del-db{
margin-top: 8px;
text-align: right;
}
#bt-del-db{
border: none;
background-color: #fff;
font-size: .8rem;
color: #555;
cursor: pointer;
}
#bt-del-db:hover{
text-decoration: underline;
color: #000;
}
/* */
.s-hide{
display: none;
}
<link rel="stylesheet" href="pathto/now-loading.css">
<div id="message"></div>
<div id="app" class="s-hide">
<div class="app__menu">
<div class="app__menu__item on">保管庫</div>
<div class="app__menu__item">新規追加</div>
</div>
<div class="app__body">
<div id="block-storage" class="app__block"></div>
<div class="app__block s-hide">
<input type="text" id="input-name-new" placeholder="定型文の名前">
<textarea id="textarea-new" placeholder="保存したい定型文を入力"></textarea>
<button id="bt-add" class="bt-control">新規追加</button>
</div>
</div>
<div class="area-del-db">
<button id="bt-del-db">データベースを削除</button>
</div>
</div>
<!-- テンプレ -->
<div id="copipe-origin" class="s-hide">
<div class="copipe__head">
<input type="text" class="copipe__name" placeholder="定型文の名前"></input>
<img src="/img/icon/arrow-up-01.svg" alt="上向きの矢印" class="bt-arrow bt-up">
<img src="/img/icon/arrow-down-01.svg" alt="下向きの矢印" class="bt-arrow bt-down">
</div>
<div class="copipe__area-bt">
<button class="bt-copy bt-control">
<img src="pathto/copy-white.svg" alt="書類が2枚重なっているアイコン。" class="bt-copy__icon">
<span class="bt-copy__text">コピー</span>
</button>
<button class="bt-del bt-control">削除</button>
</div>
<details class="copipe__details">
<summary class="copipe__summary">内容</summary>
<textarea class="copipe__text" placeholder="保存したい定型文を入力"></textarea>
</details>
</div>
<script src="pathto/now-loading.js"></script>
const nowloading = new nowLoading();
//
const el_message = document.getElementById('message');
//
const el_app = document.getElementById('app');
const els_app__menu__item = document.getElementsByClassName('app__menu__item');
const els_app__block = document.getElementsByClassName('app__block');
//
const el_block_storage = document.getElementById('block-storage');
const el_copipe_origin = document.getElementById('copipe-origin');
const els_copipe = document.getElementsByClassName('copipe');
const els_copipe__name = document.getElementsByClassName('copipe__name');
const els_copipe__text = document.getElementsByClassName('copipe__text');
//
const el_input_name_new = document.getElementById('input-name-new');
const el_textarea_new = document.getElementById('textarea-new');
const bt_add = document.getElementById('bt-add');
//
const bt_del_db = document.getElementById('bt-del-db');
//
const name_db = 'copipeDB';
const name_store = 'copipe-storage'
let db;
//
storageInit();
function storageInit(){
nowloading.start();
if(window.indexedDB===undefined){
console.error('indexDB 使用不可');
msgBox('ご利用中の環境ではこのアプリを使用できません。');
nowloading.stop();
return;
}
bt_add.addEventListener('click',()=>{
addCopipe(el_input_name_new.value,el_textarea_new.value);
});
[...els_app__menu__item].forEach((element,i)=>{
element.addEventListener('click',()=>{changeTab(i)});
});
bt_del_db.addEventListener('click',delDB);
window.addEventListener('beforeunload',saveData);
openDB();
}
function openDB(){
//リクエスト発行 データベースを開く
const req_open = indexedDB.open(name_db);
//成功時
req_open.onsuccess = (event)=>{
console.log('DB接続成功');
//DB取得
db = event.target.result;
//トランザクションの開始
const transaction = db.transaction(name_store, 'readonly');
//オブジェクトストア取得
const store = transaction.objectStore(name_store);
//リクエスト発行
const req_get = store.get('0');
req_get.onsuccess = (event)=>{
if(req_get.result===undefined){
loadSample();
}else{
loadData(req_get.result);
}
el_app.classList.remove('s-hide');
nowloading.stop();
};
req_get.onerror = (event)=>{
console.error('DB読込失敗');
msgBox('データの取得に失敗しました。');
nowloading.stop();
}
};
//失敗時
req_open.onerror = (error)=>{
console.error('DB接続失敗');
console.error(error);
msgBox('データベースへの接続に失敗しました。');
nowloading.stop();
};
//データベース更新時 or 新規作成時
req_open.onupgradeneeded = (event)=>{
console.log('DB新規作成・更新');
const db = event.target.result;
const object_store = db.createObjectStore(name_store,{keyPath: 'id'});
};
}
function saveData(){
//保存データ作成
const array_data = [];
for(let i=0;i<els_copipe__name.length-1;i++){
array_data.push({
name: els_copipe__name[i].value,
text: els_copipe__text[i].value
});
}
const obj_data = {
id: '0',
data: array_data
};
//トランザクションの開始
const transaction = db.transaction(name_store, 'readwrite');
transaction.oncomplete = (event)=>{
console.log('トランザクション完了');
};
transaction.onerror = (event)=>{
console.error('トランザクション失敗');
};
//オブジェクトストア取得
const store = transaction.objectStore(name_store);
//リクエスト発行
const req_put = store.put(obj_data);
req_put.onsuccess = (event)=>{
console.log('データ書込完了')
};
req_put.onerror = (event)=>{
console.error('データ書込失敗');
}
}
function loadData(result){
if(
(result===null)
||typeof result !== 'object'
||result.data === undefined
||!Array.isArray(result.data)
){
console.error('DB内データ破損');
msgBox('保存データが破損していたため破棄しました。');
return;
}
result.data.forEach(val=>{
addCopipe(val.name,val.text);
});
}
function loadSample(){
const name_sample = 'サンプル定型文';
const text_sample = '定型文のサンプル。\n複数行記述可能。\n上部のコピーボタンから簡単にコピー可能。\n保存後の編集も可能。\n削除や並び替えも可能。';
addCopipe(name_sample, text_sample);
}
function addCopipe(name,text){
if(text==='') return;
nowloading.start();
//クローン作成
const el_copipe = el_copipe_origin.cloneNode(true);
el_copipe.id = '';
el_copipe.classList.add('copipe');
el_copipe.classList.remove('s-hide');
//クローン内設定
const el_copipe__name = el_copipe.getElementsByClassName('copipe__name')[0];
el_copipe__name.value = name;
const bt_up = el_copipe.getElementsByClassName('bt-up')[0];
bt_up.addEventListener('click',()=>{sortCopipe(el_copipe, 'up')});
const bt_down = el_copipe.getElementsByClassName('bt-down')[0];
bt_down.addEventListener('click',()=>{sortCopipe(el_copipe, 'down')});
const el_copipe__text = el_copipe.getElementsByClassName('copipe__text')[0];
el_copipe__text.value = text;
const bt_copy = el_copipe.getElementsByClassName('bt-copy')[0];
const bt_copy__text = el_copipe.getElementsByClassName('bt-copy__text')[0];
bt_copy.addEventListener('click',()=>{copyData(bt_copy, bt_copy__text, el_copipe__text.value);});
const bt_del = el_copipe.getElementsByClassName('bt-del')[0];
bt_del.addEventListener('click',()=>{delCopipe(el_copipe, el_copipe__name.value)});
//
el_block_storage.appendChild(el_copipe);
el_input_name_new.value = '';
el_textarea_new.value = '';
nowloading.stop();
}
function sortCopipe(element, type){
if(type==='up'){
const index_old = [...els_copipe].indexOf(element);
if(index_old===0) return;
el_block_storage.insertBefore(element, els_copipe[index_old-1]);
}else if(type==='down'){
const index_old = [...els_copipe].indexOf(element);
if(index_old===els_copipe.length-1) return;
el_block_storage.insertBefore(element, els_copipe[index_old+2]);
}
}
function delCopipe(element, name){
const name_short = (()=>{
if(name.length<=10){
return name;
}else{
return name.slice(0,10)+'…';
}
})();
if(window.confirm(`「${name_short}」を削除しますか?`)){
element.remove();
}
}
function delDB(){
if(window.confirm('全てのデータを削除しますか?')){
db.close();
const req_del = indexedDB.deleteDatabase(name_db);
req_del.onsuccess = (event)=>{
console.log('DB削除成功');
el_app.textContent = '';
msgBox('データベース削除完了。<br>再び使用したい場合はページを再読み込みしてください。');
};
req_del.onerror = (event)=>{
console.error('DB削除失敗');
};
}
}
function changeTab(index_clicked){
for(let i=0;i<els_app__menu__item.length;i++){
if(i==index_clicked){
els_app__menu__item[i].classList.add('on');
els_app__block[i].classList.remove('s-hide');
}else{
els_app__menu__item[i].classList.remove('on');
els_app__block[i].classList.add('s-hide');
}
}
}
function msgBox(str){
el_message.innerHTML = str;
}
function copyData(bt, bt__text, text){
navigator.clipboard.writeText(text).then(()=>{
if(!bt.classList.contains('s-copied')){
bt.classList.add('s-copied');
bt__text.textContent = 'Copied!';
setTimeout(()=>{
bt.classList.remove('s-copied');
bt__text.textContent = 'コピー';
},2000);
}
});
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment