Merge pull request #1694 from alexistb2904/esx_notify-position

feat(esx_notify): Add a configurable position
This commit is contained in:
zykem
2025-08-23 21:12:30 +02:00
committed by GitHub
8 changed files with 502 additions and 292 deletions
+3 -2
View File
@@ -154,9 +154,10 @@ end
---@param notifyType? string The type of notification to show
---@param length? number The length of the notification
---@param title? string The title of the notification
---@param position? string The position of the notification
---@return nil
function ESX.ShowNotification(message, notifyType, length, title)
return IsResourceFound('esx_notify') and exports['esx_notify']:Notify(notifyType, length, message, title)
function ESX.ShowNotification(message, notifyType, length, title, position)
return IsResourceFound('esx_notify') and exports['esx_notify']:Notify(notifyType, length, message, title, position)
end
function ESX.TextUI(...)
+3 -4
View File
@@ -103,7 +103,7 @@
---@field setMeta fun(index: string, value: any, subValue?: any) # Set metadata value(s).
---@field clearMeta fun(index: string, subValues?: string|table) # Clear metadata value(s).
--- Notification Functions
---@field showNotification fun(msg: string, notifyType?: string, length?: number) # Show a simple notification.
---@field showNotification fun(msg: string, notifyType?: string, length?: number, title?: string, position?: string) # Show a simple notification.
---@field showAdvancedNotification fun(sender: string, subject: string, msg: string, textureDict: string, iconType: string, flash: boolean, saveToBrief: boolean, hudColorIndex: number) # Show advanced notification.
---@field showHelpNotification fun(msg: string, thisFrame?: boolean, beep?: boolean, duration?: number) # Show help notification.
--- Misc Functions
@@ -793,11 +793,10 @@ function CreateExtendedPlayer(playerId, identifier, ssn, group, accounts, invent
return nil, nil
end
function self.showNotification(msg, notifyType, length)
self.triggerEvent("esx:showNotification", msg, notifyType, length)
function self.showNotification(msg, notifyType, length, title, position)
self.triggerEvent("esx:showNotification", msg, notifyType, length, title, position)
end
function self.showAdvancedNotification(sender, subject, msg, textureDict, iconType, flash, saveToBrief, hudColorIndex)
self.triggerEvent("esx:showAdvancedNotification", sender, subject, msg, textureDict, iconType, flash, saveToBrief, hudColorIndex)
end
+2 -1
View File
@@ -1,3 +1,4 @@
Config = {}
Config.notificationSoundEnabled = true
Config.notificationSoundEnabled = true
Config.position = "middle-right" -- top-right, top-left, top-middle, bottom-right, bottom-left, bottom-middle, middle-left, middle-right
+26 -8
View File
@@ -1,16 +1,28 @@
local Debug = ESX.GetConfig().EnableDebug
local possiblePosition = {
["top-right"] = true,
["top-left"] = true,
["top-middle"] = true,
["bottom-right"] = true,
["bottom-left"] = true,
["bottom-middle"] = true,
["middle-left"] = true,
["middle-right"] = true
}
---@param notificatonType string the notification type
---@param length number the length of the notification
---@param message any the message :D
---@param title string optional title for the notification
local function Notify(notificatonType, length, message, title)
---@param position string optional position for the notification
local function Notify(notificatonType, length, message, title, position)
if Debug then
print("1 ".. tostring(notificatonType))
print("2 "..tostring(length))
print("3 "..message)
print("4 "..tostring(title))
print("5 "..tostring(position))
end
if type(notificatonType) ~= "string" then
@@ -21,11 +33,16 @@ local function Notify(notificatonType, length, message, title)
length = 3000
end
if not possiblePosition[position] then
position = Config.position or "middle-right"
end
if Debug then
print("5 ".. tostring(notificatonType))
print("6 "..tostring(length))
print("7 "..message)
print("8 "..tostring(title))
print("6 ".. tostring(notificatonType))
print("7 "..tostring(length))
print("8 "..message)
print("9 "..tostring(title))
print("10 "..tostring(position))
end
if type(message) == "string" then
@@ -37,7 +54,8 @@ local function Notify(notificatonType, length, message, title)
length = length or 5000,
message = message or "ESX-Notify",
title = title or "New Notification",
notificationSoundEnabled = Config.notificationSoundEnabled
position = position,
notificationSoundEnabled = Config.notificationSoundEnabled or false
}))
end
@@ -67,5 +85,5 @@ if Debug then
RegisterCommand("notify4", function()
ESX.ShowNotification("You Did something ~r~WRONG~s~!", "warning", 3000, "~y~Warning~s~")
end)
end
end)
end
+267 -126
View File
@@ -1,196 +1,337 @@
@import url("https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600&display=swap");
:root {
--background: rgba(20, 20, 20, 0.9);
--text-color: #ffffff;
--text-secondary: rgba(255, 255, 255, 0.7);
--success-color: #2ecc71;
--success-border: #1f9c4d;
--error-color: #e74c3c;
--error-border: #c0392b;
--info-color: #3498db;
--info-border: #2980b9;
--warning-color: #f39c12;
--warning-border: #d35400;
--background: rgba(20, 20, 20, 0.9);
--text-color: #ffffff;
--text-secondary: rgba(255, 255, 255, 0.7);
--success-color: #2ecc71;
--success-border: #1f9c4d;
--error-color: #e74c3c;
--error-border: #c0392b;
--info-color: #3498db;
--info-border: #2980b9;
--warning-color: #f39c12;
--warning-border: #d35400;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
width: 100vw;
height: 100vh;
font-family: "Poppins", sans-serif;
overflow: hidden;
width: 100vw;
height: 100vh;
font-family: "Poppins", sans-serif;
overflow: hidden;
}
#root {
position: fixed;
right: 2rem;
top: 50%;
transform: translateY(-50%);
display: flex;
flex-direction: column;
align-items: flex-end;
gap: 10px;
max-height: 80vh;
z-index: 1000;
.notification-container {
position: fixed;
display: flex;
flex-direction: column;
align-items: flex-end;
gap: 10px;
max-height: 80vh;
z-index: 1000;
}
#middle-right {
right: 2rem;
top: 50%;
transform: translateY(-50%);
}
#middle-left {
left: 2rem;
top: 50%;
transform: translateY(-50%);
}
#top-right {
right: 2rem;
top: 2rem;
}
#top-left {
left: 2rem;
top: 2rem;
}
#top-middle {
left: 50%;
top: 2rem;
transform: translateX(-50%);
}
#bottom-right {
right: 2rem;
bottom: 2rem;
}
#bottom-left {
left: 2rem;
bottom: 2rem;
}
#bottom-middle {
left: 50%;
bottom: 2rem;
transform: translateX(-50%);
}
.notify {
display: flex;
width: 300px;
background: var(--background);
border-radius: 8px;
overflow: hidden;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
position: relative;
padding: 12px;
align-items: center;
border-left: 4px solid #555;
display: flex;
width: 300px;
background: var(--background);
border-radius: 8px;
overflow: hidden;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
position: relative;
padding: 12px;
align-items: center;
border-left: 4px solid #555;
}
.notify-success {
border-left-color: var(--success-border);
border-left-color: var(--success-border);
}
.notify-error {
border-left-color: var(--error-border);
border-left-color: var(--error-border);
}
.notify-info {
border-left-color: var(--info-border);
border-left-color: var(--info-border);
}
.notify-warning {
border-left-color: var(--warning-border);
border-left-color: var(--warning-border);
}
.notify-icon-container {
margin-right: 12px;
display: flex;
align-items: center;
justify-content: center;
margin-right: 12px;
display: flex;
align-items: center;
justify-content: center;
}
.hexagon {
position: relative;
width: 40px;
height: 40px;
display: flex;
align-items: center;
justify-content: center;
clip-path: polygon(50% 0%, 95% 25%, 95% 75%, 50% 100%, 5% 75%, 5% 25%);
background: linear-gradient(145deg, var(--icon-color), rgba(0, 0, 0, 0.7));
box-shadow: 0 0 10px rgba(0, 0, 0, 0.3);
position: relative;
width: 40px;
height: 40px;
display: flex;
align-items: center;
justify-content: center;
clip-path: polygon(50% 0%, 95% 25%, 95% 75%, 50% 100%, 5% 75%, 5% 25%);
background: linear-gradient(145deg, var(--icon-color), rgba(0, 0, 0, 0.7));
box-shadow: 0 0 10px rgba(0, 0, 0, 0.3);
}
.hexagon::before {
content: "";
position: absolute;
top: 3px;
left: 3px;
right: 3px;
bottom: 3px;
background: var(--background);
clip-path: polygon(50% 0%, 95% 25%, 95% 75%, 50% 100%, 5% 75%, 5% 25%);
z-index: 1;
content: "";
position: absolute;
top: 3px;
left: 3px;
right: 3px;
bottom: 3px;
background: var(--background);
clip-path: polygon(50% 0%, 95% 25%, 95% 75%, 50% 100%, 5% 75%, 5% 25%);
z-index: 1;
}
.hexagon::after {
content: "";
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: linear-gradient(145deg, var(--icon-color) 10%, transparent 70%);
opacity: 0.4;
clip-path: polygon(50% 0%, 95% 25%, 95% 75%, 50% 100%, 5% 75%, 5% 25%);
z-index: 2;
content: "";
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: linear-gradient(145deg, var(--icon-color) 10%, transparent 70%);
opacity: 0.4;
clip-path: polygon(50% 0%, 95% 25%, 95% 75%, 50% 100%, 5% 75%, 5% 25%);
z-index: 2;
}
.hexagon .material-symbols-outlined {
color: var(--icon-color);
font-size: 20px;
position: relative;
z-index: 3;
filter: drop-shadow(0 0 2px rgba(0, 0, 0, 0.5));
display: flex;
align-items: center;
justify-content: center;
height: 24px;
width: 24px;
margin-top: -1px;
color: var(--icon-color);
font-size: 20px;
position: relative;
z-index: 3;
filter: drop-shadow(0 0 2px rgba(0, 0, 0, 0.5));
display: flex;
align-items: center;
justify-content: center;
height: 24px;
width: 24px;
margin-top: -1px;
}
.notify-content {
flex: 1;
position: relative;
padding-bottom: 8px;
flex: 1;
position: relative;
padding-bottom: 8px;
}
.notify-title {
color: var(--text-color);
font-size: 14px;
font-weight: 600;
margin-bottom: 2px;
color: var(--text-color);
font-size: 14px;
font-weight: 600;
margin-bottom: 2px;
}
.notify-text {
color: var(--text-secondary);
font-size: 12px;
font-weight: 400;
color: var(--text-secondary);
font-size: 12px;
font-weight: 400;
}
.notify-progress {
position: absolute;
bottom: 0;
left: 0;
height: 2px;
width: 100%;
border-radius: 1px;
position: absolute;
bottom: 0;
left: 0;
height: 2px;
width: 100%;
border-radius: 1px;
}
.material-symbols-outlined {
font-variation-settings: "FILL" 1, "wght" 400, "GRAD" 0, "opsz" 48;
font-variation-settings: "FILL" 1, "wght" 400, "GRAD" 0, "opsz" 48;
}
.notify-warning .material-symbols-outlined,
.notify-error .material-symbols-outlined {
margin-top: -2px;
margin-top: -2px;
}
.fadeIn {
animation: fadeIn 0.3s ease-in-out;
#top-right .fadeIn,
#bottom-right .fadeIn,
#middle-right .fadeIn {
animation: fadeIn-right 0.3s ease-in-out;
}
#top-left .fadeIn,
#bottom-left .fadeIn,
#middle-left .fadeIn {
animation: fadeIn-left 0.3s ease-in-out;
}
#top-middle .fadeIn {
animation: fadeIn-top 0.3s ease-in-out;
}
#bottom-middle .fadeIn {
animation: fadeIn-bottom 0.3s ease-in-out;
}
.fadeOut {
animation: fadeOut 0.5s ease-in-out;
opacity: 0;
opacity: 0;
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateX(50px);
}
to {
opacity: 1;
transform: translateX(0);
}
#top-right .fadeOut,
#bottom-right .fadeOut,
#middle-right .fadeOut {
animation: fadeOut-right 0.5s ease-in-out;
}
@keyframes fadeOut {
from {
opacity: 1;
transform: translateX(0);
}
to {
opacity: 0;
transform: translateX(50px);
}
#top-left .fadeOut,
#bottom-left .fadeOut,
#middle-left .fadeOut {
animation: fadeOut-left 0.5s ease-in-out;
}
#top-middle .fadeOut {
animation: fadeOut-top 0.5s ease-in-out;
}
#bottom-middle .fadeOut {
animation: fadeOut-bottom 0.5s ease-in-out;
}
@keyframes fadeIn-right {
from {
opacity: 0;
transform: translateX(50px);
}
to {
opacity: 1;
transform: translateX(0);
}
}
@keyframes fadeIn-left {
from {
opacity: 0;
transform: translateX(-50px);
}
to {
opacity: 1;
transform: translateX(0);
}
}
@keyframes fadeIn-top {
from {
opacity: 0;
transform: translateY(-50px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes fadeIn-bottom {
from {
opacity: 0;
transform: translateY(50px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes fadeOut-right {
from {
opacity: 1;
transform: translateX(0);
}
to {
opacity: 0;
transform: translateX(50px);
}
}
@keyframes fadeOut-left {
from {
opacity: 1;
transform: translateX(0);
}
to {
opacity: 0;
transform: translateX(-50px);
}
}
@keyframes fadeOut-top {
from {
opacity: 1;
transform: translateY(0);
}
to {
opacity: 0;
transform: translateY(-50px);
}
}
@keyframes fadeOut-bottom {
from {
opacity: 1;
transform: translateY(0);
}
to {
opacity: 0;
transform: translateY(50px);
}
}
+37 -13
View File
@@ -1,15 +1,39 @@
<!DOCTYPE html>
<html lang="en">
<head>
<script src="nui://game/ui/jquery.js" type="text/javascript"></script>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200" />
<link rel="stylesheet" href="css/style.css" />
<title>ESX Notifications</title>
</head>
<body>
<div id="root">
<!-- Notifications will be added here -->
</div>
<script src="js/script.js"></script>
</body>
</html>
<head>
<script src="nui://game/ui/jquery.js" type="text/javascript"></script>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200" />
<link rel="stylesheet" href="css/style.css" />
<title>ESX Notifications</title>
</head>
<body>
<div id="top-right" class="notification-container">
<!-- Top Right notifications will be added here -->
</div>
<div id="top-left" class="notification-container">
<!-- Top Left notifications will be added here -->
</div>
<div id="top-middle" class="notification-container">
<!-- Top Middle notifications will be added here -->
</div>
<div id="middle-left" class="notification-container">
<!-- Middle Left notifications will be added here -->
</div>
<div id="middle-right" class="notification-container">
<!-- Middle Right notifications will be added here -->
</div>
<div id="bottom-left" class="notification-container">
<!-- Bottom Left notifications will be added here -->
</div>
<div id="bottom-middle" class="notification-container">
<!-- Bottom Middle notifications will be added here -->
</div>
<div id="bottom-right" class="notification-container">
<!-- Bottom Right notifications will be added here -->
</div>
<script src="js/script.js"></script>
</body>
</html>
+143 -137
View File
@@ -1,160 +1,167 @@
const w = window
const w = window;
const types = {
["success"]: {
["icon"]: "check_circle",
["frequency"]: 800,
["duration"]: 100,
["color"]: "#2ecc71",
["borderColor"]: "#1f9c4d",
},
["error"]: {
["icon"]: "error",
["frequency"]: 300,
["duration"]: 150,
["color"]: "#e74c3c",
["borderColor"]: "#c0392b",
},
["info"]: {
["icon"]: "info",
["frequency"]: 600,
["duration"]: 100,
["color"]: "#3498db",
["borderColor"]: "#2980b9",
},
["warning"]: {
["icon"]: "warning_amber",
["frequency"]: 450,
["duration"]: 125,
["color"]: "#f39c12",
["borderColor"]: "#d35400",
},
}
["success"]: {
["icon"]: "check_circle",
["frequency"]: 800,
["duration"]: 100,
["color"]: "#2ecc71",
["borderColor"]: "#1f9c4d",
},
["error"]: {
["icon"]: "error",
["frequency"]: 300,
["duration"]: 150,
["color"]: "#e74c3c",
["borderColor"]: "#c0392b",
},
["info"]: {
["icon"]: "info",
["frequency"]: 600,
["duration"]: 100,
["color"]: "#3498db",
["borderColor"]: "#2980b9",
},
["warning"]: {
["icon"]: "warning_amber",
["frequency"]: 450,
["duration"]: 125,
["color"]: "#f39c12",
["borderColor"]: "#d35400",
},
};
// the color codes example `i ~r~love~s~ donuts`
const codes = {
"~r~": "#c0392b",
"~b~": "#378cbf",
"~g~": "#2ecc71",
"~y~": "yellow",
"~p~": "purple",
"~c~": "grey",
"~m~": "#212121",
"~u~": "black",
"~o~": "#fb9b04",
}
"~r~": "#c0392b",
"~b~": "#378cbf",
"~g~": "#2ecc71",
"~y~": "yellow",
"~p~": "purple",
"~c~": "grey",
"~m~": "#212121",
"~u~": "black",
"~o~": "#fb9b04",
};
// Audio context for sound effects
let audioContext
let audioContext;
// Initialize audio context on user interaction
document.addEventListener("click", initAudioContext, { once: true })
document.addEventListener("keydown", initAudioContext, { once: true })
document.addEventListener("click", initAudioContext, { once: true });
document.addEventListener("keydown", initAudioContext, { once: true });
function initAudioContext() {
if (!audioContext) {
audioContext = new (window.AudioContext || window.webkitAudioContext)()
}
if (!audioContext) {
audioContext = new (window.AudioContext || window.webkitAudioContext)();
}
}
function playNotificationSound(type) {
if (!audioContext) initAudioContext()
if (!audioContext) initAudioContext();
const typeConfig = types[type] || types["info"]
const oscillator = audioContext.createOscillator()
const gainNode = audioContext.createGain()
const typeConfig = types[type] || types["info"];
const oscillator = audioContext.createOscillator();
const gainNode = audioContext.createGain();
oscillator.type = "sine"
oscillator.frequency.setValueAtTime(typeConfig.frequency, audioContext.currentTime)
oscillator.type = "sine";
oscillator.frequency.setValueAtTime(typeConfig.frequency, audioContext.currentTime);
gainNode.gain.setValueAtTime(0.3, audioContext.currentTime)
gainNode.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + typeConfig.duration / 1000)
gainNode.gain.setValueAtTime(0.3, audioContext.currentTime);
gainNode.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + typeConfig.duration / 1000);
oscillator.connect(gainNode)
gainNode.connect(audioContext.destination)
oscillator.connect(gainNode);
gainNode.connect(audioContext.destination);
oscillator.start()
oscillator.stop(audioContext.currentTime + typeConfig.duration / 1000)
oscillator.start();
oscillator.stop(audioContext.currentTime + typeConfig.duration / 1000);
}
w.addEventListener("message", (event) => {
notification({
type: event.data.type,
title: event.data.title || "New Notification",
message: event.data.message,
length: event.data.length,
notificationSoundEnabled: event.data.notificationSoundEnabled,
})
})
notification({
type: event.data.type,
title: event.data.title || "New Notification",
message: event.data.message,
length: event.data.length,
position: event.data.position,
notificationSoundEnabled: event.data.notificationSoundEnabled,
});
});
const replaceColors = (str, obj) => {
let strToReplace = str
let strToReplace = str;
for (const id in obj) {
strToReplace = strToReplace.replace(new RegExp(id, "g"), obj[id])
}
for (const id in obj) {
strToReplace = strToReplace.replace(new RegExp(id, "g"), obj[id]);
}
return strToReplace
}
return strToReplace;
};
const sanitizeHTML = (str) => {
const temp = document.createElement("div")
temp.textContent = str
return temp.innerHTML
}
const temp = document.createElement("div");
temp.textContent = str;
return temp.innerHTML;
};
const processLineBreaks = (str) => {
// Replace <br> tags with actual line breaks
return str.replace(/&lt;br&gt;/g, "<br>")
}
// Replace <br> tags with actual line breaks
return str.replace(/&lt;br&gt;/g, "<br>");
};
const notification = (data) => {
if (typeof $ === "undefined") {
console.error("jQuery is not loaded. Please ensure jQuery is included in your project.")
return
}
if (typeof $ === "undefined") {
console.error("jQuery is not loaded. Please ensure jQuery is included in your project.");
return;
}
if (data.message) {
// Remove any standalone ~s~ tags (not preceded by a color code)
data.message = data.message.replace(/~s~/g, "")
}
if (data.message) {
// Remove any standalone ~s~ tags (not preceded by a color code)
data.message = data.message.replace(/~s~/g, "");
}
if (data.title) {
// Remove any standalone ~s~ tags (not preceded by a color code)
data.title = data.title.replace(/~s~/g, "")
}
if (data.title) {
// Remove any standalone ~s~ tags (not preceded by a color code)
data.title = data.title.replace(/~s~/g, "");
}
let sanitizedTitle = data.title ? sanitizeHTML(data.title) : ""
let sanitizedMessage = data.message ? sanitizeHTML(data.message) : ""
let sanitizedTitle = data.title ? sanitizeHTML(data.title) : "";
let sanitizedMessage = data.message ? sanitizeHTML(data.message) : "";
if (data.title) {
for (const color in codes) {
if (sanitizedTitle.includes(color)) {
const objArr = {}
objArr[color] = `<span style="color: ${codes[color]}">`
objArr["~s~"] = "</span>"
sanitizedTitle = replaceColors(sanitizedTitle, objArr)
}
}
}
if (data.title) {
for (const color in codes) {
if (sanitizedTitle.includes(color)) {
const objArr = {};
objArr[color] = `<span style="color: ${codes[color]}">`;
objArr["~s~"] = "</span>";
sanitizedTitle = replaceColors(sanitizedTitle, objArr);
}
}
}
for (const color in codes) {
if (sanitizedMessage.includes(color)) {
const objArr = {}
objArr[color] = `<span style="color: ${codes[color]}">`
objArr["~s~"] = "</span>"
sanitizedMessage = replaceColors(sanitizedMessage, objArr)
}
}
for (const color in codes) {
if (sanitizedMessage.includes(color)) {
const objArr = {};
objArr[color] = `<span style="color: ${codes[color]}">`;
objArr["~s~"] = "</span>";
sanitizedMessage = replaceColors(sanitizedMessage, objArr);
}
}
sanitizedMessage = processLineBreaks(sanitizedMessage)
sanitizedTitle = processLineBreaks(sanitizedTitle)
sanitizedMessage = processLineBreaks(sanitizedMessage);
sanitizedTitle = processLineBreaks(sanitizedTitle);
const id = Math.floor(Math.random() * 100000)
const typeInfo = types[data.type] || types["info"]
const duration = data.length || 3000
const id = Math.floor(Math.random() * 100000);
const typeInfo = types[data.type] || types["info"];
const duration = data.length || 3000;
const containerToAppend = data.position ? `#${data.position}` : "#middle-right";
const notificationElement = $(`
if (!$(containerToAppend).length) {
console.error(`Container ${containerToAppend} to add the notification not found.`);
return;
}
const notificationElement = $(`
<div id="${id}" class="notify notify-${data.type} fadeIn">
<div class="notify-icon-container">
<div class="hexagon" style="--icon-color: ${typeInfo.color}; --border-color: ${typeInfo.borderColor}">
@@ -167,28 +174,27 @@ const notification = (data) => {
<div class="notify-progress" style="background-color: ${typeInfo.color}"></div>
</div>
</div>
`).appendTo(`#root`)
`).appendTo(containerToAppend);
$(`#${id} .notify-progress`).css({
transition: `width ${duration}ms linear`,
width: "0%",
})
$(`#${id} .notify-progress`).css({
transition: `width ${duration}ms linear`,
width: "0%",
});
if (data.notificationSoundEnabled) {
playNotificationSound(data.type)
}
if (data.notificationSoundEnabled) {
playNotificationSound(data.type);
}
setTimeout(() => {
$(`#${id} .notify-progress`).css("width", "100%")
}, 10)
setTimeout(() => {
$(`#${id} .notify-progress`).css("width", "100%");
}, 10);
setTimeout(() => {
$(`#${id}`).removeClass("fadeIn").addClass("fadeOut")
setTimeout(() => {
$(`#${id}`).remove()
}, 500)
}, duration)
setTimeout(() => {
$(`#${id}`).removeClass("fadeIn").addClass("fadeOut");
setTimeout(() => {
$(`#${id}`).remove();
}, 500);
}, duration);
return notificationElement
}
return notificationElement;
};
+21 -1
View File
@@ -17,6 +17,20 @@ ESX.ShowNotification("text here") -- Default will time and type will be info/300
-- With title (optional 4th parameter)
ESX.ShowNotification("message here", "success", 3000, "Achievement")
--[[
With position (optional 5th parameter)
Possible positions:
"top-right"
"top-left"
"top-middle"
"bottom-right"
"bottom-left"
"bottom-middle"
"middle-left"
"middle-right"
]]
ESX.ShowNotification("Your message here", "info", 3000, "Info Title", "top-right")
```
`<h3>`Export Usage`</h3>`
@@ -28,6 +42,9 @@ exports["esx_notify"]:Notify("info", 3000, "message here")
-- With title
exports["esx_notify"]:Notify("success", 3000, "Item purchased!", "Shop")
-- With position
exports["esx_notify"]:Notify("warning", 4000, "Inventory full!", "Warning", "bottom-left")
-- With line break
exports["esx_notify"]:Notify("warning", 4000, "Inventory full!~br~Some items were dropped.", "Warning")
```
@@ -40,6 +57,9 @@ TriggerEvent("ESX:Notify", "info", 3000, "message here")
-- With title
TriggerEvent("ESX:Notify", "error", 5000, "You don't have enough money!", "Transaction Failed")
-- With position
TriggerEvent("ESX:Notify", "success", 4000, "Welcome!", "Greetings", "top-middle")
```
`<h3>`Color Code Usage`</h3>`
@@ -75,4 +95,4 @@ This program Is free software: you can redistribute it And/Or modify it under th
This program Is distributed In the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty Of MERCHANTABILITY Or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License For more details.
You should have received a copy Of the GNU General Public License along with this program. If Not, see `<http://www.gnu.org/licenses/>`.
You should have received a copy Of the GNU General Public License along with this program. If Not, see `<http://www.gnu.org/licenses/>`.