mirror of
https://github.com/citizenfx/cfx-server-data.git
synced 2025-02-10 15:42:54 +08:00
chat: theming support, hide chat when not needed, other minor changes
This commit is contained in:
parent
9914752788
commit
03362d2c6d
10
resources/[system]/chat-theme-gtao/__resource.lua
Normal file
10
resources/[system]/chat-theme-gtao/__resource.lua
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
file 'style.css'
|
||||||
|
file 'shadow.js'
|
||||||
|
|
||||||
|
chat_theme 'gtao' {
|
||||||
|
styleSheet = 'style.css',
|
||||||
|
script = 'shadow.js',
|
||||||
|
msgTemplates = {
|
||||||
|
default = '<b>{0}</b><span>{1}</span>'
|
||||||
|
}
|
||||||
|
}
|
74
resources/[system]/chat-theme-gtao/shadow.js
Normal file
74
resources/[system]/chat-theme-gtao/shadow.js
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
(function() {
|
||||||
|
var Filters = {}
|
||||||
|
|
||||||
|
var svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
||||||
|
svg.setAttribute("style", "display:block;width:0px;height:0px");
|
||||||
|
var defs = document.createElementNS("http://www.w3.org/2000/svg", "defs");
|
||||||
|
|
||||||
|
var blurFilter = document.createElementNS("http://www.w3.org/2000/svg", "filter");
|
||||||
|
blurFilter.setAttribute("id", "svgBlurFilter");
|
||||||
|
var feGaussianFilter = document.createElementNS("http://www.w3.org/2000/svg", "feGaussianBlur");
|
||||||
|
feGaussianFilter.setAttribute("stdDeviation", "0 0");
|
||||||
|
blurFilter.appendChild(feGaussianFilter);
|
||||||
|
defs.appendChild(blurFilter);
|
||||||
|
Filters._svgBlurFilter = feGaussianFilter;
|
||||||
|
|
||||||
|
// Drop Shadow Filter
|
||||||
|
var dropShadowFilter = document.createElementNS("http://www.w3.org/2000/svg", "filter");
|
||||||
|
dropShadowFilter.setAttribute("id", "svgDropShadowFilter");
|
||||||
|
var feGaussianFilter = document.createElementNS("http://www.w3.org/2000/svg", "feGaussianBlur");
|
||||||
|
feGaussianFilter.setAttribute("in", "SourceAlpha");
|
||||||
|
feGaussianFilter.setAttribute("stdDeviation", "3");
|
||||||
|
dropShadowFilter.appendChild(feGaussianFilter);
|
||||||
|
Filters._svgDropshadowFilterBlur = feGaussianFilter;
|
||||||
|
|
||||||
|
var feOffset = document.createElementNS("http://www.w3.org/2000/svg", "feOffset");
|
||||||
|
feOffset.setAttribute("dx", "0");
|
||||||
|
feOffset.setAttribute("dy", "0");
|
||||||
|
feOffset.setAttribute("result", "offsetblur");
|
||||||
|
dropShadowFilter.appendChild(feOffset);
|
||||||
|
Filters._svgDropshadowFilterOffset = feOffset;
|
||||||
|
|
||||||
|
var feFlood = document.createElementNS("http://www.w3.org/2000/svg", "feFlood");
|
||||||
|
feFlood.setAttribute("flood-color", "rgba(0,0,0,1)");
|
||||||
|
dropShadowFilter.appendChild(feFlood);
|
||||||
|
Filters._svgDropshadowFilterFlood = feFlood;
|
||||||
|
|
||||||
|
var feComposite = document.createElementNS("http://www.w3.org/2000/svg", "feComposite");
|
||||||
|
feComposite.setAttribute("in2", "offsetblur");
|
||||||
|
feComposite.setAttribute("operator", "in");
|
||||||
|
dropShadowFilter.appendChild(feComposite);
|
||||||
|
var feComposite = document.createElementNS("http://www.w3.org/2000/svg", "feComposite");
|
||||||
|
feComposite.setAttribute("in2", "SourceAlpha");
|
||||||
|
feComposite.setAttribute("operator", "out");
|
||||||
|
feComposite.setAttribute("result", "outer");
|
||||||
|
dropShadowFilter.appendChild(feComposite);
|
||||||
|
|
||||||
|
var feMerge = document.createElementNS("http://www.w3.org/2000/svg", "feMerge");
|
||||||
|
var feMergeNode = document.createElementNS("http://www.w3.org/2000/svg", "feMergeNode");
|
||||||
|
feMerge.appendChild(feMergeNode);
|
||||||
|
var feMergeNode = document.createElementNS("http://www.w3.org/2000/svg", "feMergeNode");
|
||||||
|
feMerge.appendChild(feMergeNode);
|
||||||
|
Filters._svgDropshadowMergeNode = feMergeNode;
|
||||||
|
dropShadowFilter.appendChild(feMerge);
|
||||||
|
defs.appendChild(dropShadowFilter);
|
||||||
|
svg.appendChild(defs);
|
||||||
|
document.documentElement.appendChild(svg);
|
||||||
|
|
||||||
|
const blurScale = 1;
|
||||||
|
const scale = (document.body.clientWidth / 1280);
|
||||||
|
|
||||||
|
Filters._svgDropshadowFilterBlur.setAttribute("stdDeviation",
|
||||||
|
1 * blurScale + " " +
|
||||||
|
1 * blurScale
|
||||||
|
);
|
||||||
|
Filters._svgDropshadowFilterOffset.setAttribute("dx",
|
||||||
|
String(Math.cos(45 * Math.PI / 180) * 1 * scale));
|
||||||
|
Filters._svgDropshadowFilterOffset.setAttribute("dy",
|
||||||
|
String(Math.sin(45 * Math.PI / 180) * 1 * scale));
|
||||||
|
Filters._svgDropshadowFilterFlood.setAttribute("flood-color",
|
||||||
|
'rgba(0, 0, 0, 1)');
|
||||||
|
Filters._svgDropshadowMergeNode.setAttribute("in",
|
||||||
|
"SourceGraphic");
|
||||||
|
|
||||||
|
})();
|
116
resources/[system]/chat-theme-gtao/style.css
Normal file
116
resources/[system]/chat-theme-gtao/style.css
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
* {
|
||||||
|
font-family: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-window {
|
||||||
|
--size: calc((((2.7vw / 1.77777) * 1.2)) * 6);
|
||||||
|
|
||||||
|
position: absolute;
|
||||||
|
right: calc(1.56vw);
|
||||||
|
top: calc(50% - (var(--size) / 2));
|
||||||
|
height: var(--size) !important;
|
||||||
|
|
||||||
|
background: inherit !important;
|
||||||
|
|
||||||
|
text-align: right;
|
||||||
|
|
||||||
|
left: auto;
|
||||||
|
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Font2';
|
||||||
|
src: url(https://runtime.fivem.net/temp/ChaletLondonNineteenSixty.otf?a);
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Font2_cond';
|
||||||
|
src: url(https://runtime.fivem.net/temp/chaletcomprime-colognesixty-webfont.ttf?a);
|
||||||
|
}
|
||||||
|
|
||||||
|
.msg {
|
||||||
|
font-family: Font2, sans-serif;
|
||||||
|
color: #fff;
|
||||||
|
|
||||||
|
font-size: calc(1.8vw / 1.77777); /* 13px in 720p, calc'd by width */
|
||||||
|
filter: url(#svgDropShadowFilter);
|
||||||
|
|
||||||
|
line-height: calc((2.7vw / 1.77777) * 1.2);
|
||||||
|
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-messages {
|
||||||
|
margin: 0;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.msg > span > span > b {
|
||||||
|
font-family: Font2_cond, sans-serif;
|
||||||
|
font-weight: normal;
|
||||||
|
|
||||||
|
vertical-align: baseline;
|
||||||
|
padding-right: 11px;
|
||||||
|
|
||||||
|
line-height: 1;
|
||||||
|
|
||||||
|
font-size: calc(2.7vw / 1.77777); /* 13px in 720p, calc'd by width */
|
||||||
|
}
|
||||||
|
|
||||||
|
.msg > span > span > span {
|
||||||
|
vertical-align: baseline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.msg i:first-of-type {
|
||||||
|
font-style: normal;
|
||||||
|
color: #c0c0c0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-input {
|
||||||
|
position: absolute;
|
||||||
|
right: calc(1.56vw);
|
||||||
|
bottom: calc(1.56vw);
|
||||||
|
|
||||||
|
background: inherit !important;
|
||||||
|
|
||||||
|
text-align: right;
|
||||||
|
|
||||||
|
top: auto;
|
||||||
|
left: auto;
|
||||||
|
|
||||||
|
height: auto;
|
||||||
|
|
||||||
|
font-family: Font2, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-input > div {
|
||||||
|
background-color: rgba(0, 0, 0, .6);
|
||||||
|
padding: calc(0.15625vw / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-input .prefix {
|
||||||
|
margin: 0;
|
||||||
|
margin-left: 0.7%;
|
||||||
|
margin-top: -0.1%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-input > div + div {
|
||||||
|
position: absolute;
|
||||||
|
bottom: calc(1.65vh + 0.15625vw + 0.15625vw + 0.15625vw + (0.15625vw / 2));
|
||||||
|
width: 99.6%;
|
||||||
|
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.suggestions {
|
||||||
|
border: calc(0.15625vw / 2) solid rgba(180, 180, 180, .6);
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
textarea {
|
||||||
|
background: transparent;
|
||||||
|
border: calc(0.15625vw / 2) solid rgba(180, 180, 180, .6);
|
||||||
|
padding: calc(0.15625vw / 2);
|
||||||
|
padding-left: calc(3.5% + (0.15625vw / 2));
|
||||||
|
}
|
@ -1,5 +1,7 @@
|
|||||||
local chatInputActive = false
|
local chatInputActive = false
|
||||||
local chatInputActivating = false
|
local chatInputActivating = false
|
||||||
|
local chatHidden = true
|
||||||
|
local chatLoaded = false
|
||||||
|
|
||||||
RegisterNetEvent('chatMessage')
|
RegisterNetEvent('chatMessage')
|
||||||
RegisterNetEvent('chat:addTemplate')
|
RegisterNetEvent('chat:addTemplate')
|
||||||
@ -132,16 +134,54 @@ local function refreshCommands()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local function refreshThemes()
|
||||||
|
local themes = {}
|
||||||
|
|
||||||
|
for resIdx = 0, GetNumResources() - 1 do
|
||||||
|
local resource = GetResourceByFindIndex(resIdx)
|
||||||
|
|
||||||
|
if GetResourceState(resource) == 'started' then
|
||||||
|
local numThemes = GetNumResourceMetadata(resource, 'chat_theme')
|
||||||
|
|
||||||
|
if numThemes > 0 then
|
||||||
|
local themeName = GetResourceMetadata(resource, 'chat_theme')
|
||||||
|
local themeData = json.decode(GetResourceMetadata(resource, 'chat_theme_extra') or 'null')
|
||||||
|
|
||||||
|
if themeName and themeData then
|
||||||
|
themeData.baseUrl = 'nui://' .. resource .. '/'
|
||||||
|
themes[themeName] = themeData
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
SendNUIMessage({
|
||||||
|
type = 'ON_UPDATE_THEMES',
|
||||||
|
themes = themes
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
AddEventHandler('onClientResourceStart', function(resName)
|
AddEventHandler('onClientResourceStart', function(resName)
|
||||||
Wait(500)
|
Wait(500)
|
||||||
|
|
||||||
refreshCommands()
|
refreshCommands()
|
||||||
|
refreshThemes()
|
||||||
|
end)
|
||||||
|
|
||||||
|
AddEventHandler('onClientResourceStop', function(resName)
|
||||||
|
Wait(500)
|
||||||
|
|
||||||
|
refreshCommands()
|
||||||
|
refreshThemes()
|
||||||
end)
|
end)
|
||||||
|
|
||||||
RegisterNUICallback('loaded', function(data, cb)
|
RegisterNUICallback('loaded', function(data, cb)
|
||||||
TriggerServerEvent('chat:init');
|
TriggerServerEvent('chat:init');
|
||||||
|
|
||||||
refreshCommands()
|
refreshCommands()
|
||||||
|
refreshThemes()
|
||||||
|
|
||||||
|
chatLoaded = true
|
||||||
|
|
||||||
cb('ok')
|
cb('ok')
|
||||||
end)
|
end)
|
||||||
@ -171,5 +211,22 @@ Citizen.CreateThread(function()
|
|||||||
chatInputActivating = false
|
chatInputActivating = false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if chatLoaded then
|
||||||
|
local shouldBeHidden = false
|
||||||
|
|
||||||
|
if IsScreenFadedOut() or IsPauseMenuActive() then
|
||||||
|
shouldBeHidden = true
|
||||||
|
end
|
||||||
|
|
||||||
|
if (shouldBeHidden and not chatHidden) or (not shouldBeHidden and chatHidden) then
|
||||||
|
chatHidden = shouldBeHidden
|
||||||
|
|
||||||
|
SendNUIMessage({
|
||||||
|
type = 'ON_SCREEN_STATE_CHANGE',
|
||||||
|
shouldHide = shouldBeHidden
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
@ -6,6 +6,7 @@ window.APP = {
|
|||||||
style: CONFIG.style,
|
style: CONFIG.style,
|
||||||
showInput: false,
|
showInput: false,
|
||||||
showWindow: false,
|
showWindow: false,
|
||||||
|
shouldHide: true,
|
||||||
backingSuggestions: [],
|
backingSuggestions: [],
|
||||||
removedSuggestions: [],
|
removedSuggestions: [],
|
||||||
templates: CONFIG.templates,
|
templates: CONFIG.templates,
|
||||||
@ -13,6 +14,8 @@ window.APP = {
|
|||||||
messages: [],
|
messages: [],
|
||||||
oldMessages: [],
|
oldMessages: [],
|
||||||
oldMessagesIndex: -1,
|
oldMessagesIndex: -1,
|
||||||
|
tplBackups: [],
|
||||||
|
msgTplBackups: []
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
destroyed() {
|
destroyed() {
|
||||||
@ -48,6 +51,9 @@ window.APP = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
ON_SCREEN_STATE_CHANGE({ shouldHide }) {
|
||||||
|
this.shouldHide = shouldHide;
|
||||||
|
},
|
||||||
ON_OPEN() {
|
ON_OPEN() {
|
||||||
this.showInput = true;
|
this.showInput = true;
|
||||||
this.showWindow = true;
|
this.showWindow = true;
|
||||||
@ -91,6 +97,85 @@ window.APP = {
|
|||||||
this.templates[template.id] = template.html;
|
this.templates[template.id] = template.html;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
ON_UPDATE_THEMES({ themes }) {
|
||||||
|
this.removeThemes();
|
||||||
|
|
||||||
|
this.setThemes(themes);
|
||||||
|
},
|
||||||
|
removeThemes() {
|
||||||
|
for (let i = 0; i < document.styleSheets.length; i++) {
|
||||||
|
const styleSheet = document.styleSheets[i];
|
||||||
|
const node = styleSheet.ownerNode;
|
||||||
|
|
||||||
|
if (node.getAttribute('data-theme')) {
|
||||||
|
node.parentNode.removeChild(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.tplBackups.reverse();
|
||||||
|
|
||||||
|
for (const [ elem, oldData ] of this.tplBackups) {
|
||||||
|
elem.innerText = oldData;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.tplBackups = [];
|
||||||
|
|
||||||
|
this.msgTplBackups.reverse();
|
||||||
|
|
||||||
|
for (const [ id, oldData ] of this.msgTplBackups) {
|
||||||
|
this.templates[id] = oldData;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.msgTplBackups = [];
|
||||||
|
},
|
||||||
|
setThemes(themes) {
|
||||||
|
for (const [ id, data ] of Object.entries(themes)) {
|
||||||
|
if (data.style) {
|
||||||
|
const style = document.createElement('style');
|
||||||
|
style.type = 'text/css';
|
||||||
|
style.setAttribute('data-theme', id);
|
||||||
|
style.appendChild(document.createTextNode(data.style));
|
||||||
|
|
||||||
|
document.head.appendChild(style);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.styleSheet) {
|
||||||
|
const link = document.createElement('link');
|
||||||
|
link.rel = 'stylesheet';
|
||||||
|
link.type = 'text/css';
|
||||||
|
link.href = data.baseUrl + data.styleSheet;
|
||||||
|
link.setAttribute('data-theme', id);
|
||||||
|
|
||||||
|
document.head.appendChild(link);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.templates) {
|
||||||
|
for (const [ tplId, tpl ] of Object.entries(data.templates)) {
|
||||||
|
const elem = document.getElementById(tplId);
|
||||||
|
|
||||||
|
if (elem) {
|
||||||
|
this.tplBackups.push([ elem, elem.innerText ]);
|
||||||
|
elem.innerText = tpl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.script) {
|
||||||
|
const script = document.createElement('script');
|
||||||
|
script.type = 'text/javascript';
|
||||||
|
script.src = data.baseUrl + data.script;
|
||||||
|
|
||||||
|
document.head.appendChild(script);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.msgTemplates) {
|
||||||
|
for (const [ tplId, tpl ] of Object.entries(data.msgTemplates)) {
|
||||||
|
this.msgTplBackups.push([ tplId, this.templates[tplId] ]);
|
||||||
|
this.templates[tplId] = tpl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
warn(msg) {
|
warn(msg) {
|
||||||
this.messages.push({
|
this.messages.push({
|
||||||
args: [msg],
|
args: [msg],
|
||||||
|
@ -103,7 +103,7 @@ textarea:focus, input:focus {
|
|||||||
list-style-type: none;
|
list-style-type: none;
|
||||||
padding: 0.5%;
|
padding: 0.5%;
|
||||||
padding-left: 1.4%;
|
padding-left: 1.4%;
|
||||||
font-size: 1.1rem;
|
font-size: 1.65vh;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
color: white;
|
color: white;
|
||||||
background-color: rgba(44, 62, 80, 1.0);
|
background-color: rgba(44, 62, 80, 1.0);
|
||||||
@ -121,3 +121,7 @@ textarea:focus, input:focus {
|
|||||||
.suggestion {
|
.suggestion {
|
||||||
margin-bottom: 0.5%;
|
margin-bottom: 0.5%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.hidden {
|
||||||
|
display: none;
|
||||||
|
}
|
@ -18,7 +18,7 @@
|
|||||||
<!-- App Template -->
|
<!-- App Template -->
|
||||||
<script type="text/x-template" id="app_template">
|
<script type="text/x-template" id="app_template">
|
||||||
<div id="app">
|
<div id="app">
|
||||||
<div class="chat-window" :style="this.style" :class="{ 'fadeOut animated': !showWindow }">
|
<div class="chat-window" :style="this.style" :class="{ 'fadeOut animated': !showWindow, 'hidden': shouldHide }">
|
||||||
<div class="chat-messages" ref="messages">
|
<div class="chat-messages" ref="messages">
|
||||||
<message v-for="msg in messages"
|
<message v-for="msg in messages"
|
||||||
:templates="templates"
|
:templates="templates"
|
||||||
@ -32,6 +32,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="chat-input" v-show="showInput">
|
<div class="chat-input" v-show="showInput">
|
||||||
|
<div>
|
||||||
<span class="prefix">➤</span>
|
<span class="prefix">➤</span>
|
||||||
<textarea v-model="message"
|
<textarea v-model="message"
|
||||||
ref="input"
|
ref="input"
|
||||||
@ -43,6 +44,7 @@
|
|||||||
@keydown="keyDown"
|
@keydown="keyDown"
|
||||||
@keypress.enter.prevent="send">
|
@keypress.enter.prevent="send">
|
||||||
</textarea>
|
</textarea>
|
||||||
|
</div>
|
||||||
<suggestions :message="message" :suggestions="suggestions">
|
<suggestions :message="message" :suggestions="suggestions">
|
||||||
</suggestions>
|
</suggestions>
|
||||||
</div>
|
</div>
|
||||||
@ -58,7 +60,8 @@
|
|||||||
|
|
||||||
<!-- Suggestions Template -->
|
<!-- Suggestions Template -->
|
||||||
<script type="text/x-template" id="suggestions_template">
|
<script type="text/x-template" id="suggestions_template">
|
||||||
<ul class="suggestions" v-show="currentSuggestions.length > 0">
|
<div class="suggestions-wrap" v-show="currentSuggestions.length > 0">
|
||||||
|
<ul class="suggestions">
|
||||||
<li class="suggestion" v-for="s in currentSuggestions">
|
<li class="suggestion" v-for="s in currentSuggestions">
|
||||||
<p>
|
<p>
|
||||||
<span :class="{ 'disabled': s.disabled }">
|
<span :class="{ 'disabled': s.disabled }">
|
||||||
@ -80,6 +83,7 @@
|
|||||||
</small>
|
</small>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
</div>
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<!-- Scripts -->
|
<!-- Scripts -->
|
||||||
|
Loading…
Reference in New Issue
Block a user