Skip to content

Instantly share code, notes, and snippets.

@motsu0
Last active February 19, 2023 10:20
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/810e6d045030814cba3e7dca4f6d537d to your computer and use it in GitHub Desktop.
Save motsu0/810e6d045030814cba3e7dca4f6d537d to your computer and use it in GitHub Desktop.
.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;
}
<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>
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