InputManJSのコメントコンポーネントとタグボックスコントロールを利用して、チャットボット風なアプリケーションを作成します。このサンプルでは、次のシナリオを確認することができます。
コメントコンポーネント:コメントコンポーネントを利用して、問い合わせ用の回答を表示しています。質問に応じた回答を、リンク、テーブル、画像、リストなどで表現しています。
タグボックスコントロール:タグボックスコントロールを利用して、問い合わせ用の質問を表示しています。
import { InputMan } from '@mescius/inputman.comment';
import '@mescius/inputman.comment/CSS/gc.inputman.comment.css';
import '@mescius/inputman/CSS/gc.inputman-js.css';
import './styles.css';
import * as GcCommonControl from '@mescius/inputman';
import { firstQuery, getMessage, getQueryItems } from './data';
GcCommonControl.InputMan.appearanceStyle =
GcCommonControl.InputMan.AppearanceStyle.Modern;
const gcComment = new InputMan.GcComment(document.getElementById('gcComment'), {
openLinkMode: InputMan.OpenLinkMode.GoToComment,
editorConfig: {
editorType: InputMan.GcCommentEditorType.GcMultilineTextBox,
height: 60,
},
userInfo: {
id: '1',
name: 'testUser',
},
showIcon: false,
commentMode: InputMan.GcCommentMode.ChatMode,
header: [],
showZeroReaction: true,
showOpenReactionListButton: false,
contextToolbar: []
});
const postChatBot = (text) => {
const date = new Date();
gcComment.execCommand(InputMan.GcCommentCommand.AddCommentElement, {
comment: {
id: date.valueOf().toString(),
userInfo: {
id: '0',
name: 'チャットボット',
},
content: text,
postTime: date,
updateTime: date,
reactions: text.includes('class="finalAnswer')
? [
{
reactionChar: '👍',
count: 0,
},
{
reactionChar: '👎',
count: 0,
},
]
: [],
scrollIntoView: false,
},
});
};
//初期コメント
postChatBot('ご用件をお伺いいたします。');
gcComment.addEventListener(
InputMan.GcCommentEvent.AfterExecuteCommand,
(args) => {
if (
args.command === InputMan.GcCommentCommand.AddCommentElement &&
args.value.comment.userInfo.id == '1'
) {
let inputMsg = args.value.comment.content;
generateChat(inputMsg);
} else {
let renderedMsgElement = gcComment.getCommentElement(
args.value.comment.id
);
renderedMsgElement.scrollIntoView({
behavior: 'smooth',
block: 'end',
inline: 'nearest',
});
}
}
);
function generateChat(query) {
setTimeout(() => {
let message = getMessage(query);
postChatBot(message);
tagBox1.readOnly = false;
tagBox1.value = getQueryItems(message);
tagBox1.readOnly = true;
}, 800);
}
var tagBox1 = new GcCommonControl.InputMan.GcTagBox(
document.getElementById('tagBox1'),
{
height: 59,
width: 380,
value: firstQuery,
readOnly: true,
displayMode: GcCommonControl.InputMan.TagBoxDisplayMode.Multiline,
showDropDown: false,
}
);
const ele = tagBox1.getElement();
let newContent = '';
//TagBoxの選択されている項目を取得する処理
ele.addEventListener('click', (e) => {
if (e.target.classList.contains('tag-content') || e.target.classList.contains('tag')) {
newContent = e.target.textContent;
setTimeout(() => {
const date = new Date();
gcComment.execCommand(InputMan.GcCommentCommand.AddCommentElement, {
comment: {
id: date.valueOf().toString(),
userInfo: {
id: '1',
name: 'testUser',
},
content: newContent,
postTime: date,
updateTime: date,
},
});
}, 50);
}
});
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<meta name="description" content="実用例 - チャットアプリ" lang="ja" xml:lang="ja" />
<title>実用例 - チャットボット</title>
<!-- SystemJS -->
<script src="node_modules/systemjs/dist/system.src.js"></script>
<script src="systemjs.config.js"></script>
<script>
window.onload = function () {
System.import('./src/app');
};
</script>
</head>
<body>
<div class="container-fluid">
<div class="display">
<div class="header">
<h3 class="title">InputManJS チャットボット</h3>
</div>
<div class="scroll-area">
<div id="gcComment"></div>
</div>
<div class="action-area">
<div id="tagBox1"></div>
</div>
</div>
<p>※コメント内のリンクは無効化されています。</p>
</div>
</body>
</html>
body {
font-family: sans-serif;
font-size: 14px;
}
.display {
margin-top: 16px;
height: 465px;
width: 380px;
margin-left: 16px;
border: 1px solid #cfd8df;
background-color: #fff;
justify-content: center;
border-radius: 5px;
}
.scroll-area {
height: 350px;
overflow-y: scroll;
}
.scroll-area::-webkit-scrollbar {
display: none;
}
.header {
height: 40px;
width: 100%;
background-color: rgb(79, 165, 235);
display: flex;
justify-content: center;
align-items: center;
color: white;
border-radius: 5px;
}
.gcim__gccomment[gcim-comment-mode='chatmode'] .gcim__gccomment-comment-container .gcim__comment[gcim-post-by-current-user='true'] .gcim__comment-info {
display: none;
}
.gcim__gccomment[gcim-comment-mode='chatmode'] .gcim__gccomment-comment-container .gcim__comment .gcim__comment-control::before {
content: unset;
}
.gcim__gccomment[gcim-comment-mode='chatmode'] .gcim__gccomment-comment-container .gcim__comment[gcim-post-by-current-user='false'] .gcim__comment-info {
display: none;
}
.context-menu {
display: none !important;
}
.gcim__gccomment .gcim__header-footer {
display: none !important;
}
.action-area {
margin-top: 15px;
}
.gcim__gccomment[gcim-comment-mode='chatmode'] .gcim__gccomment-comment-container .gcim__comment .gcim__comment-control {
max-width: 100% !important;
}
/* タグボックスのテキスト */
.tag-content {
font-size: 90%;
}
table {
display: table;
border-collapse: separate;
box-sizing: border-box;
text-indent: initial;
unicode-bidi: isolate;
border-spacing: 2px;
border-color: gray;
}
table tr>th {
text-align: left;
background: #f0f0f0;
font-weight: normal;
width: 35%;
}
table tr>* {
padding: 0.25rem 0.5rem;
border: 1px solid #d2d2d2;
background: whitesmoke;
}
.gcim__gccomment {
font-size: 12px;
--gcim__gccomment-left-right-padding: 10px;
--gcim__gccomment-avatar-width: 25px;
--gcim__gccomment-avatar-height: 25px;
}
.gcim__gccomment[gcim-comment-mode='chatmode'] {
--comment-padding: 8px;
}
.gcim__gccomment[gcim-comment-mode='chatmode'] .gcim__gccomment-comment-container .gcim__comment .gcim__comment-control {
--comment-balloon-pole-width: 7px;
border-radius: 5px;
}
.gcim__gccomment[gcim-comment-mode='chatmode'] .gcim__gccomment-comment-container .gcim__comment .gcim__comment-control::before {
top: 7px;
}
.gcim__gccomment[gcim-comment-mode='chatmode'] .gcim__gccomment-comment-container .gcim__comment[gcim-post-by-current-user='true'] .gcim__comment-control::before {
right: -7px;
}
.gcim__gccomment[gcim-comment-mode='chatmode'] .gcim__gccomment-comment-container .gcim__comment[gcim-post-by-current-user='false'] .gcim__comment-control::before {
left: -7px;
}
.gcim__gccomment[gcim-comment-mode='chatmode'] .gcim__gccomment-comment-container .gcim__comment[gcim-post-by-current-user='true'] {
--backcolor: #cfeec7;
}
.tag {
width: 41%;
text-align: center;
}
.finalAnswer {
margin-bottom: -5px;
}
.list {
margin-top: -15px;
margin-bottom: -30px
}
.margin {
margin-bottom: -20px;
}
a {
pointer-events: none;
}
export const firstQuery = [
'アカウントや購入について',
'製品機能について',
'価格やライセンスについて',
'その他',
];
export const licenseQuery = [
'ライセンスの種類を知りたい',
'製品の価格を知りたい',
'前の質問に戻る',
];
export const productQuery = [
'製品の特長を紹介してほしい',
'製品の使い方を知りたい',
'前の質問に戻る',
];
export const accountQuery = [
'アカウントを作成したい',
'パスワードを忘れた',
'製品を購入したい',
'前の質問に戻る',
];
export const returnQuery = ['最初の質問に戻る'];
export const firstMessage = ['ご用件をお伺いいたします。'];
export const selectItemeMessage = ['ご質問内容を選択ください。'];
const otherMessage = `<p class="finalAnswer">その他のお問い合わせにつきましては、弊社営業部までお問い合わせください。</p>
<table style="width:100%;"><tbody><tr><th>専用窓口</th><td>営業部</td></tr><tr><th>営業時間</th><td>月~金 10:00~12:00/13:30~17:00</td></tr><tr><th>電話番号</th><td>050-5490-4661</td></tr><tr><th>FAX番号</th><td>050-3156-3561</td></tr><tr><th>メールアドレス</th><td><a href="mailto:sales@mescius.com">sales@mescius.com</a></td></tr></td></tr></tbody></table>`;
const licenseMessage =
'製品や価格に関するお問い合わせですね。ご要望をお選びください。';
const productMessage =
'製品機能関するお問い合わせですね。ご要望をお選びください。';
const accountMessage =
'アカウントや製品の購入に関するお問い合わせですね。ご要望をお選びください。';
const accountCreateMessage = `<p class="finalAnswer">アカウントの作成についてですね。</p><p>アカウント作成の手順は、弊社ホームページから確認することができます。<br>
製品ページ:<a href="https://developer.mescius.jp/support/user/create" target="_blank" rel="noopener">アカウント作成・変更</a><br>上記のページをご参照下さい。</p>
`;
const resetPasswordMessage = `<p class="finalAnswer">パスワードの再設定についてですね。</p><p>パスワードの再設定は、以下のパスワードの再設定ページから行ってください。</p>
<a href="https://developer.mescius.jp/login/forgot-password" target="_blank" rel="noopener">パスワードの再設定ページ</a>
`;
const purchaseMessage = `<p class="finalAnswer">製品のご購入についてですね。</p><p class="margin">最新バージョンをお求めの場合には次の方法によりご購入いただけます。購入ルートによってお取引条件が異なりますので、直接お問合せください。</p>
<ul class="list">
<li>弊社から直接の購入する場合:<a href="https://developer.mescius.jp/purchase/how-to-buy#directsales" target="_blank" rel="noopener">こちら</a></p>をご覧ください。</li>
<li>オンラインから購入する場合:<a href="https://developer.mescius.jp/purchase/how-to-buy#onlinesales" target="_blank" rel="noopener">こちら</a></p>をご覧ください。</li>
<li>販売パートナーから購入する場合:<a href="https://developer.mescius.jp/purchase/how-to-buy#partnersales" target="_blank" rel="noopener">こちら</a></p>をご覧ください。</li>
</ul>
`;
const licenseInfoMessage = `<p class="finalAnswer">InputManJSのライセンスの種類についてですね。InputManJSには次のライセンスがあります。</p><ul class="list">
<li>開発ライセンス:InputManJSを組み込んだアプリケーションを開発する際に利用するライセンスです。</li>
<li>配布ライセンス:InputManJSを組み込んだアプリケーションを配布する際に利用するライセンスです。</li>
</ul>
<p>ライセンスに関するより詳細な情報は、弊社製品ページから確認することができます。</p>
<p>製品ページ:<a href="https://developer.mescius.jp/license/javascript" target="_blank" rel="noopener">URL</a></p>`;
const priceInfoMessage = `<p class="finalAnswer">製品の価格についてですね。</p>
<p class="margin">一例として、製品のを開発する際に必要な、開発ライセンス(イニシャル)の価格は、税込みで <b>121,000円</b> となります。<br>他のライセンスの価格は、弊社製品ページから確認することができます。</p>
<p>製品ページ:<a href="https://developer.mescius.jp/inputmanjs/pricelist" target="_blank" rel="noopener">URL</a></p>`;
const productInfoMessage = `<p class="finalAnswer">製品の特長についてですね。</p>
<img src="https://demo.mescius.jp/inputmanjs/demos/resource/logo.png" style="width:100%; height:auto;" alt="InputManJSのロゴ">
<p class="margin">InputManJSは、リッチテキスト、テキスト、マスク、日付時刻、数値、コンボなど用途別に最適化された日本仕様の入力用JavaScriptコントロールセットです。収録されているコントロールの実際の動作は、弊社デモ環境で確認することができます。</p>
<p>製品デモページ:<a href="https://demo.mescius.jp/inputmanjs/demos/" target="_blank" rel="noopener">InputManJS デモ</a></p>`;
const useInfoMessage = `<p class="finalAnswer">製品の利用方法についてですね。<br>以下の情報をご参考にしていただければと思います。</p>
<ul class="list">
<li>製品の実際の動作を確認したい場合:<a href="https://demo.mescius.jp/inputmanjs/demos/" target="_blank" rel="noopener">製品デモ</a>をご覧ください。</li>
<li>製品のドキュメントを確認したい場合:<a href="https://demo.mescius.jp/inputmanjs/docs/welcome" target="_blank" rel="noopener">製品ヘルプ</a>をご覧ください。</li>
<li>その他の技術情報を確認したい場合:<a href="https://support.mescius.jp/hc/ja/categories/360000686076" target="_blank" rel="noopener">ナレッジベース</a>をご覧ください。</li>
</ul>`;
export const getMessage = (query) => {
let message = '';
switch (query) {
//最初の質問の分岐
case firstQuery[2]:
message = licenseMessage;
break;
case firstQuery[0]:
message = accountMessage;
break;
case firstQuery[1]:
message = productMessage;
break;
case firstQuery[3]:
message = otherMessage;
break;
case '前の質問に戻る':
message = firstMessage;
break;
case '':
message = selectItemeMessage;
break;
//ライセンスに関する質問の分岐
case licenseQuery[0]:
message = licenseInfoMessage;
break;
case licenseQuery[1]:
message = priceInfoMessage;
break;
//アカウントに関する質問の分岐
case accountQuery[0]:
message = accountCreateMessage;
break;
//アカウントに関する質問の分岐
case accountQuery[1]:
message = resetPasswordMessage;
break;
case accountQuery[2]:
message = purchaseMessage;
break;
//製品に関する質問の分岐
case productQuery[0]:
message = productInfoMessage;
break;
case productQuery[1]:
message = useInfoMessage;
break;
//最初の質問の分岐
case returnQuery[0]:
message = firstMessage;
break;
default:
}
return message;
};
export const getQueryItems = (message) => {
let item = firstQuery;
switch (message) {
case licenseMessage:
item = licenseQuery;
break;
case productMessage:
item = productQuery;
break;
case accountMessage:
item = accountQuery;
break;
case accountCreateMessage:
case resetPasswordMessage:
case purchaseMessage:
case licenseInfoMessage:
case priceInfoMessage:
case productInfoMessage:
case useInfoMessage:
case otherMessage:
item = returnQuery;
break;
default:
}
return item;
};
System.config({
transpiler: 'plugin-babel',
babelOptions: {
es2015: true
},
meta: {
'*.css': { loader: 'css' }
},
paths: {
// paths serve as alias
'npm:': 'node_modules/'
},
// map tells the System loader where to look for things
map: {
'@mescius/inputman': 'npm:@mescius/inputman/index.js',
'@mescius/inputman/CSS': 'npm:@mescius/inputman/CSS',
'@mescius/inputman.richtexteditor': 'npm:@mescius/inputman.richtexteditor/index.js',
"@mescius/inputman.richtexteditor/CSS": "npm:@mescius/inputman.richtexteditor/CSS",
'@mescius/inputman.richtexteditor/JS/plugins/advlist': 'npm:@mescius/inputman.richtexteditor/JS/plugins/advlist/plugin.js',
'@mescius/inputman.richtexteditor/JS/plugins/all': 'npm:@mescius/inputman.richtexteditor/JS/plugins/all/plugin.js',
'@mescius/inputman.richtexteditor/JS/plugins/autosave': 'npm:@mescius/inputman.richtexteditor/JS/plugins/autosave/plugin.js',
'@mescius/inputman.richtexteditor/JS/plugins/charmap': 'npm:@mescius/inputman.richtexteditor/JS/plugins/charmap/plugin.js',
'@mescius/inputman.richtexteditor/JS/plugins/directionality': 'npm:@mescius/inputman.richtexteditor/JS/plugins/directionality/plugin.js',
'@mescius/inputman.richtexteditor/JS/plugins/emoticons': 'npm:@mescius/inputman.richtexteditor/JS/plugins/emoticons/plugin.js',
'@mescius/inputman.richtexteditor/JS/plugins/fullscreen': 'npm:@mescius/inputman.richtexteditor/JS/plugins/fullscreen/plugin.js',
'@mescius/inputman.richtexteditor/JS/plugins/htmlcode': 'npm:@mescius/inputman.richtexteditor/JS/plugins/htmlcode/plugin.js',
'@mescius/inputman.richtexteditor/JS/plugins/image': 'npm:@mescius/inputman.richtexteditor/JS/plugins/image/plugin.js',
'@mescius/inputman.richtexteditor/JS/plugins/link': 'npm:@mescius/inputman.richtexteditor/JS/plugins/link/plugin.js',
'@mescius/inputman.richtexteditor/JS/plugins/lists': 'npm:@mescius/inputman.richtexteditor/JS/plugins/lists/plugin.js',
'@mescius/inputman.richtexteditor/JS/plugins/media': 'npm:@mescius/inputman.richtexteditor/JS/plugins/media/plugin.js',
'@mescius/inputman.richtexteditor/JS/plugins/pagebreak': 'npm:@mescius/inputman.richtexteditor/JS/plugins/pagebreak/plugin.js',
'@mescius/inputman.richtexteditor/JS/plugins/preview': 'npm:@mescius/inputman.richtexteditor/JS/plugins/preview/plugin.js',
'@mescius/inputman.richtexteditor/JS/plugins/save': 'npm:@mescius/inputman.richtexteditor/JS/plugins/save/plugin.js',
'@mescius/inputman.richtexteditor/JS/plugins/searchreplace': 'npm:@mescius/inputman.richtexteditor/JS/plugins/searchreplace/plugin.js',
'@mescius/inputman.richtexteditor/JS/plugins/table': 'npm:@mescius/inputman.richtexteditor/JS/plugins/table/plugin.js',
'@mescius/inputman.richtexteditor/JS/plugins/template': 'npm:@mescius/inputman.richtexteditor/JS/plugins/template/plugin.js',
'@mescius/inputman.richtexteditor/JS/plugins/wordcount': 'npm:@mescius/inputman.richtexteditor/JS/plugins/wordcount/plugin.js',
'@mescius/inputman.comment': 'npm:@mescius/inputman.comment/index.js',
'@mescius/inputman.comment/CSS': 'npm:@mescius/inputman.comment/CSS',
'@mescius/wijmo': 'npm:@mescius/wijmo/index.js',
'@mescius/wijmo.styles': 'npm:@mescius/wijmo.styles',
'@mescius/wijmo.cultures': 'npm:@mescius/wijmo.cultures',
'@mescius/wijmo.input': 'npm:@mescius/wijmo.input/index.js',
'@mescius/wijmo.grid': 'npm:@mescius/wijmo.grid/index.js',
'@mescius/wijmo.nav': 'npm:@mescius/wijmo.nav/index.js',
'@mescius/spread-sheets': 'npm:@mescius/spread-sheets/index.js',
'@mescius/spread-sheets-resources-ja': 'npm:@mescius/spread-sheets-resources-ja/index.js',
'@mescius/spread-sheets/styles': 'npm:@mescius/spread-sheets/styles',
'css': 'npm:systemjs-plugin-css/css.js',
'plugin-babel': 'npm:systemjs-plugin-babel/plugin-babel.js',
'systemjs-babel-build': 'npm:systemjs-plugin-babel/systemjs-babel-browser.js'
},
// packages tells the System loader how to load when no filename and/or no extension
packages: {
src: {
defaultExtension: 'js'
},
"node_modules": {
defaultExtension: 'js'
},
}
});