Last active
February 19, 2023 10:20
-
-
Save motsu0/810e6d045030814cba3e7dca4f6d537d to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
.block{ | |
margin-bottom: 32px; | |
} | |
th{ | |
font-weight: normal; | |
} | |
/* */ | |
#bt-sample{ | |
margin-right: 12px; | |
cursor: pointer; | |
} | |
#bt-reset{ | |
cursor: pointer; | |
} | |
.area-recipe{ | |
margin: 12px 0; | |
overflow-x: auto; | |
} | |
.recipe{ | |
width: 100%; | |
border-collapse: collapse; | |
text-align: center; | |
word-break: keep-all; | |
white-space: nowrap; | |
font-size: .9rem; | |
} | |
th.recipe__cell{ | |
padding: 2px; | |
background-color: #FFECB3; | |
} | |
.recipe__cell{ | |
border: 1px solid #777; | |
background-color: #FFF8E1; | |
} | |
.recipe__cell--amount{ | |
width: 20%; | |
} | |
.input-number{ | |
box-sizing: border-box; | |
width: 50px; | |
height: 1.5em; | |
padding: 0 4px; | |
font-size: 1rem; | |
} | |
.input-text{ | |
box-sizing: border-box; | |
width: 100%; | |
height: 100%; | |
padding: 0 4px; | |
border: none; | |
text-align: center; | |
} | |
.input-name,.input-amount{ | |
height: 2em; | |
} | |
.bt-del{ | |
border: none; | |
background-color: #E74C3C; | |
color: #fff; | |
cursor: pointer; | |
} | |
.bt-del:hover{ | |
background-color: #C0392B; | |
} | |
/* */ | |
#bt-add{ | |
display: block; | |
margin: 12px auto; | |
cursor: pointer; | |
} | |
/* */ | |
.area-output-text{ | |
position: relative; | |
} | |
#output-text{ | |
box-sizing: border-box; | |
width: 100%; | |
height: 100px; | |
padding: 8px; | |
resize: none; | |
} | |
#icon-copy{ | |
box-sizing: border-box; | |
width: 40px; | |
padding: 4px; | |
position: absolute; | |
top: 0; | |
right: 0; | |
background-color: #888; | |
cursor: pointer; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<div class="block"> | |
<button id="bt-sample">サンプルデータを入力</button> | |
<button id="bt-reset">全てリセット</button> | |
<div class="area-recipe"> | |
<table class="recipe"> | |
<thead> | |
<tr> | |
<th class="recipe__cell"></th> | |
<th class="recipe__cell">材料名</th> | |
<th class="recipe__cell"> | |
元の分量<br> | |
<input type="number" value="4" class="input-number" id="input-person-origin" min="0">人前 | |
</th> | |
<th class="recipe__cell"> | |
新しい分量<br> | |
<input type="number" value="5" class="input-number" id="input-person-new" min="0">人前 | |
</th> | |
</tr> | |
</thead> | |
<tbody id="recipe__tbody"></tbody> | |
</table> | |
</div> | |
<button id="bt-add">+</button> | |
</div> | |
<h3>テキスト出力</h3> | |
<div class="area-output-text"> | |
<textarea id="output-text"></textarea> | |
<img src="pathto/copy-white.svg" alt="書類が2枚重なっているアイコン" id="icon-copy"> | |
</div> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const nowloading = new nowLoading(); | |
nowloading.start(); | |
const bt_sample = document.getElementById('bt-sample'); | |
const bt_reset = document.getElementById('bt-reset'); | |
const el_input_person_origin = document.getElementById('input-person-origin'); | |
const el_input_person_new = document.getElementById('input-person-new'); | |
const el_recipe__tbody = document.getElementById('recipe__tbody'); | |
const els_recipe__unit = document.getElementsByClassName('recipe__unit'); | |
const els_input_name = document.getElementsByClassName('input-name'); | |
const els_input_amount = document.getElementsByClassName('input-amount'); | |
const els_output_amount = document.getElementsByClassName('output-amount'); | |
const bt_add = document.getElementById('bt-add'); | |
const el_output_text = document.getElementById('output-text'); | |
const el_icon_copy = document.getElementById('icon-copy'); | |
const array_sample = [ | |
{name: '豚肉', amount: '200g'}, | |
{name: 'じゃがいも', amount: '1/1個'}, | |
{name: '玉ねぎ', amount: '1+1/2個'}, | |
{name: 'にんじん', amount: '1/2本'}, | |
{name: 'カレールウ', amount: '4片'}, | |
{name: '水', amount: '800ml'} | |
]; | |
bt_sample.addEventListener('click',sampleInput); | |
bt_reset.addEventListener('click',resetAll); | |
el_input_person_origin.addEventListener('input',calcAmountAll); | |
el_input_person_new.addEventListener('input',calcAmountAll); | |
bt_add.addEventListener('click',addRow); | |
el_icon_copy.addEventListener('click',copyText); | |
for(let i=0;i<4;i++){ | |
addRow(); | |
} | |
nowloading.stop(); | |
function sampleInput(){ | |
el_input_person_origin.value = 4; | |
el_input_person_new.value = 5 | |
for(let i=els_recipe__unit.length;i<array_sample.length;i++){ | |
addRow(); | |
} | |
for(let i=0;i<array_sample.length;i++){ | |
els_input_name[i].value = array_sample[i].name; | |
els_input_amount[i].value = array_sample[i].amount; | |
calcAmount(els_input_amount[i], els_output_amount[i]); | |
} | |
} | |
function resetAll(){ | |
[...els_recipe__unit].forEach(el=>{ | |
el.remove(); | |
}); | |
el_input_person_origin.value = ''; | |
el_input_person_new.value = ''; | |
addRow(); | |
recipeToText(); | |
} | |
function addRow(){ | |
const el_tr = document.createElement('tr'); | |
el_tr.classList.add('recipe__unit'); | |
// | |
const el_td_del = document.createElement('td'); | |
el_td_del.classList.add('recipe__cell'); | |
const bt_del = document.createElement('button'); | |
bt_del.classList.add('bt-del'); | |
bt_del.textContent = '✕'; | |
bt_del.addEventListener('click',()=>{ | |
delRow(el_tr); | |
}); | |
el_td_del.appendChild(bt_del); | |
el_tr.appendChild(el_td_del); | |
// | |
const el_td_name = document.createElement('td'); | |
el_td_name.classList.add('recipe__cell'); | |
el_td_name.classList.add('recipe__cell--name'); | |
const el_input_name = document.createElement('input'); | |
el_input_name.classList.add('input-text'); | |
el_input_name.classList.add('input-name'); | |
el_input_name.type = 'text'; | |
el_input_name.placeholder = '材料名'; | |
el_input_name.addEventListener('input',recipeToText); | |
el_td_name.appendChild(el_input_name); | |
el_tr.appendChild(el_td_name); | |
// | |
const el_td_amount_origin = document.createElement('td'); | |
el_td_amount_origin.classList.add('recipe__cell'); | |
el_td_amount_origin.classList.add('recipe__cell--amount'); | |
const el_input_amount_origin = document.createElement('input'); | |
el_input_amount_origin.classList.add('input-text'); | |
el_input_amount_origin.classList.add('input-amount'); | |
el_input_amount_origin.type = 'text'; | |
el_input_amount_origin.placeholder = '例:100g'; | |
el_td_amount_origin.appendChild(el_input_amount_origin); | |
el_tr.appendChild(el_td_amount_origin); | |
// | |
const el_td_amount_new = document.createElement('td'); | |
el_td_amount_new.classList.add('recipe__cell'); | |
el_td_amount_new.classList.add('recipe__cell--amount'); | |
el_td_amount_new.classList.add('output-amount'); | |
el_tr.appendChild(el_td_amount_new); | |
// | |
el_input_amount_origin.addEventListener('input',()=>{ | |
calcAmount(el_input_amount_origin, el_td_amount_new); | |
}); | |
el_recipe__tbody.appendChild(el_tr); | |
} | |
function delRow(el){ | |
el.remove(); | |
recipeToText(); | |
} | |
function calcAmountAll(){ | |
for(let i=0;i<els_recipe__unit.length;i++){ | |
calcAmount(els_input_amount[i], els_output_amount[i]); | |
} | |
} | |
function calcAmount(el_input, el_output){ | |
//値取得 | |
const person_origin = Number(el_input_person_origin.value); | |
const person_new = Number(el_input_person_new.value); | |
//例外処理 | |
if(els_recipe__unit.length===0) return; | |
if(person_origin<=0||person_new<=0){ | |
[...els_output_amount].forEach(el=>{ | |
el.textContent = ''; | |
}); | |
el_output_text.value = ''; | |
return; | |
} | |
if(!/[0-9]/.test(el_input.value)){ | |
el_output.textContent = el_input.value; | |
recipeToText(); | |
return; | |
} | |
if(/\/0/.test(el_input.value)){ | |
el_output.textContent = ''; | |
return; | |
} | |
//数取得 分数判定 単位取得 | |
const match_num = el_input.value.match(/([0-9]+[\.\/][0-9]+|[0-9]+)/g); | |
const match_unit_before = el_input.value.match(/^[^0-9]*/); | |
const match_unit_after = el_input.value.match(/[^0-9]*$/); | |
const has_frac = (()=>{ | |
for(let i=0;i<match_num.length;i++){ | |
if(match_num[i].includes('/')) return true; | |
} | |
return false; | |
})(); | |
//分数があるとき | |
if(has_frac){ | |
let frac = [0,1]; | |
for(let i=0;i<match_num.length;i++){ | |
const frac_add = match_num[i].split('/').map(v=>Number(v)); | |
if(frac_add.length===1){ | |
frac = fracCalc(frac[0], frac[1], frac_add[0], 1, '+'); | |
}else{ | |
frac = fracCalc(frac[0], frac[1], frac_add[0], frac_add[1], '+'); | |
} | |
} | |
const frac_new = fracCalc(frac[0], frac[1], person_new, person_origin, '*'); | |
const str_frac = (()=>{ | |
if(frac_new[1]===1) return frac_new[0]; | |
if(frac_new[0]<frac_new[1]) return frac_new[0]+'/'+frac_new[1]; | |
const int = Math.floor(frac_new[0] / frac_new[1]); | |
const remain = frac_new[0] % frac_new[1]; | |
return `${int}+${remain}/${frac_new[1]}`; | |
})(); | |
const str_new = match_unit_before[0] + str_frac + match_unit_after[0]; | |
el_output.textContent = str_new; | |
} | |
//分数が無い時 | |
else{ | |
const num_origin = match_num.map(v=>Number(v)).reduce((p,c)=>p+c); | |
const rate = person_new/person_origin; | |
const str_new = match_unit_before[0] + (Math.round(num_origin * rate * 10)/10) + match_unit_after[0]; | |
el_output.textContent = str_new; | |
} | |
//計算終了 | |
recipeToText(); | |
} | |
function recipeToText(){ | |
if(els_recipe__unit.length===0){ | |
el_output_text.value = ''; | |
return; | |
} | |
const max_length_name = [...els_input_name].map(el=>el.value.length).reduce((p,c)=>Math.max(p,c)); | |
const max_length_amount = [...els_output_amount].map(el=>el.textContent.length).reduce((p,c)=>Math.max(p,c)); | |
const array_output = [`- ${Number(el_input_person_new.value)}人前の分量 -`]; | |
for(let i=0;i<els_recipe__unit.length;i++){ | |
let str = els_input_name[i].value; | |
const num_blank_name = max_length_name - els_input_name[i].value.length; | |
if(num_blank_name>0) str += ' '.repeat(num_blank_name); | |
str += ' '; | |
str += els_output_amount[i].textContent; | |
array_output.push(str); | |
} | |
const str_output = array_output.join('\n'); | |
el_output_text.value = str_output; | |
} | |
function copyText(){ | |
navigator.clipboard.writeText(el_output_text.value).then(()=>{ | |
el_output_text.select(); | |
}); | |
} | |
function fracCalc(t1,b1,t2,b2,operator){ | |
if(b1===0||b2===0) return undefined; | |
let t3; | |
let b3; | |
switch(operator){ | |
case '+': | |
t3 = t1 * b2 + t2 * b1; | |
b3 = b1 * b2; | |
break; | |
case '*': | |
t3 = t1 * t2; | |
b3 = b1 * b2; | |
break; | |
default: | |
break; | |
} | |
const dec_t3 = String(t3).split('.'); | |
const pow_t3 = (()=>{ | |
if(dec_t3.length===1) return 0; | |
return dec_t3[1].length; | |
})(); | |
const dec_b3 = String(b3).split('.'); | |
const pow_b3 = (()=>{ | |
if(dec_b3.length===1) return 0; | |
return dec_b3[1].length; | |
})(); | |
const pow = Math.min(Math.max(pow_b3,pow_t3),8); | |
t3 = Math.round(t3*(10**pow)); | |
b3 = Math.round(b3*(10**pow)); | |
return fracReduction(t3,b3); | |
function fracReduction(t, b){ | |
let x = Math.max(t,b); | |
let y = Math.min(t,b); | |
let r; | |
if(t===0){ | |
r = 0; | |
y = 1; | |
} | |
while(r!==0){ | |
r = x % y; | |
if(r===0) break; | |
x = y; | |
y = r; | |
} | |
const t_new = t / y; | |
const b_new = b / y; | |
//分母の最大値設定 | |
if(b_new>8){ | |
const rate = 8 / b_new; | |
const t_round = (()=>{ | |
const temp = Math.round(t_new * rate); | |
if(temp===0) return 1; | |
return temp; | |
})(); | |
const b_round = 8; | |
return fracReduction(t_round, b_round); | |
} | |
// | |
return [t_new,b_new] | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment